diff --git a/app/DataMapper/CompanySettings.php b/app/DataMapper/CompanySettings.php index 8dc815fd1a37..55b611e2236c 100644 --- a/app/DataMapper/CompanySettings.php +++ b/app/DataMapper/CompanySettings.php @@ -159,6 +159,10 @@ class CompanySettings extends BaseSettings 'client_number_counter' => 1, 'counter_padding' => 0, 'recurring_invoice_number_prefix' => 'R', + 'invoice_number_prefix' => '', + 'quote_number_prefix' => '', + 'credit_number_prefix' => '', + 'client_number_prefix' => '', 'auto_archive_invoice' => 'FALSE', 'translations' => (object) [], diff --git a/app/Models/BaseModel.php b/app/Models/BaseModel.php index a1cf7ef8fad9..4f07859c8b7d 100644 --- a/app/Models/BaseModel.php +++ b/app/Models/BaseModel.php @@ -88,11 +88,11 @@ class BaseModel extends Model { //Log::error('harvesting client settings for key = '. $key . ' and it has the value = '. $this->getSettings()->{$key}); //Log::error(print_r($this->getSettings(),1)); - return $this->getSettings(); + return $this->getSettings()->{$key}; } else { //Log::error(print_r(new CompanySettings($this->company->settings),1)); - return new CompanySettings($this->company->settings); + return (new CompanySettings($this->company->settings))->{$key}; } } diff --git a/app/Utils/Traits/GeneratesCounter.php b/app/Utils/Traits/GeneratesCounter.php new file mode 100644 index 000000000000..b5f302bc6b46 --- /dev/null +++ b/app/Utils/Traits/GeneratesCounter.php @@ -0,0 +1,281 @@ +resetCounters($client); + + $counter = $this->getNextInvoiceCounter($client); + $counter = $this->checkEntityNumber($client, $counter, $client->company->settings->counter_padding, $client->company->settings->invoice_number_prefix); + + $client_counter = $this->getNextInvoiceClientCounter($client); + $client_counter = $this->checkEntityNumber($client, $client_counter, $client->company->settings->counter_padding, $client->company->settings->invoice_number_prefix); + + //build number pattern + $invoice_number = $this->applyNumberPattern($client, $counter, $client_counter, $client->company->settings->invoice_number_pattern); + + } + + public function getNextCreditNumber() + { + + } + + public function getNextQuoteNumber() + { + + } + + public function getNextRecurringInvoiceNumber() + { + + } + + public function getNextClientInvoiceNumber() + { + + } + + public function getNextClientNumber() + { + + } + + + /** + * Checks that the number has not already been used + * + * @param Collection $entity The entity ie App\Models\Client, Invoice, Quote etc + * @param integer $counter The counter + * @param integer $padding The padding + * @param string $prefix The prefix + * + * @return ( description_of_the_return_value ) + */ + private function checkEntityNumber($entity, $counter, $padding, $prefix) + { + $check = false; + + do { + + $number = $this->padCounter($counter, $padding); + $number = $this->prefixCounter($number, $prefix); + + $check = $entity::company($entity->company_id)->whereIdNumber($number)->withTrashed()->first(); + + $counter++; + + } while ($check); + + $this->incrementCounter($client, 'invoice_number_counter'); + + return $number; + } + + + /** + * Saves counters at both the company and client level + * + * @param \App\Models\Client $client The client + * @param \App\Models\Client|integer|string $counter_name The counter name + */ + private function incrementCounter(Client $client, string $counter_name) :void + { + $company_settings = $client->company->settings; + $company_settings->$counter_name = $company_settings->$counter_name + 1; + $client->company->settings = $company_settings; + $client->company->save(); + + $client_settings = $client->settings; + $client_settings->$counter_name = $client_settings->$counter_name + 1; + $client->settings = $client_settings; + $client->save(); + } + + private function prefixCounter($counter, $prefix) : string + { + if(strlen($prefix) == 0) + return $counter; + + return $prefix . $counter; + + } + + /** + * Pads a number with leading 000000's + * + * @param int $counter The counter + * @param int $padding The padding + * + * @return int the padded counter + */ + private function padCounter($counter, $padding) + { + + return str_pad($counter, $padding, '0', STR_PAD_LEFT); + + } + + /** + * Gets the next invoice counter. + * + * Determine whether we need to harvest the + * Client specific invoice increment OR + * the Company wide invoice increment + * + * @param string $number_pattern The number pattern + * @param \App\Models\Client $client The client + * + * @return string The next invoice counter. + */ + private function getNextInvoiceCounter(Client $client) : string + { + + return $client->company->settings->invoice_number_counter; + + } + + private function getNextInvoiceClientCounter(Client $client) : string + { + + return $client->settings->invoice_number_counter; + + } + + /** + * If we are using counter reset, + * check if we need to reset here + * + * @param Client $client client entity + * @return void + */ + private function resetCounters(Client $client) + { + + $timezone = $client->company->timezone()->name; + + $reset_date = Carbon::parse($client->company->settings->reset_counter_date, $timezone); + + if (! $reset_date->isToday()) + return false; + + switch ($client->company->reset_counter_frequency_id) { + case RecurringInvoice::FREQUENCY_WEEKLY: + $reset_date->addWeek(); + break; + case RecurringInvoice::FREQUENCY_TWO_WEEKS: + $reset_date->addWeeks(2); + break; + case RecurringInvoice::FREQUENCY_FOUR_WEEKS: + $reset_date->addWeeks(4); + break; + case RecurringInvoice::FREQUENCY_MONTHLY: + $reset_date->addMonth(); + break; + case RecurringInvoice::FREQUENCY_TWO_MONTHS: + $reset_date->addMonths(2); + break; + case RecurringInvoice::FREQUENCY_THREE_MONTHS: + $reset_date->addMonths(3); + break; + case RecurringInvoice::FREQUENCY_FOUR_MONTHS: + $reset_date->addMonths(4); + break; + case RecurringInvoice::FREQUENCY_SIX_MONTHS: + $reset_date->addMonths(6); + break; + case RecurringInvoice::FREQUENCY_ANNUALLY: + $reset_date->addYear(); + break; + case RecurringInvoice::FREQUENCY_TWO_YEARS: + $reset_date->addYears(2); + break; + } + + $settings = $client->company->settings; + $settings->reset_counter_date = $reset_date->format('Y-m-d'); + $settings->invoice_number_counter = 1; + $settings->quote_number_counter = 1; + $settings->credit_number_counter = 1; + + $client->company->settings = $settings; + $client->company->save(); + } + + private function applyNumberPattern($client, $counter, $client_counter, $pattern) + { + if(!$pattern) + return $counter; + + $search = ['{$year}']; + $replace = [date('Y')]; + + $search[] = '{$counter}'; + $replace[] = [$counter]; + + $search[] = '{$client_counter'; + $replace[] = [$client_counter]; + + if (strstr($pattern, '{$user_id}')) { + $user_id = auth()->check() ? auth()->user()->id : 0; + $search[] = '{$user_id}'; + $replace[] = str_pad(($user_id), 2, '0', STR_PAD_LEFT); + } + + $matches = false; + preg_match('/{\$date:(.*?)}/', $pattern, $matches); + if (count($matches) > 1) { + $format = $matches[1]; + $search[] = $matches[0]; + + /* The following adjusts for the company timezone - may bork tests depending on the time of day the tests are run!!!!!!*/ + $date = Carbon::now($client->company->timezone()->name)->format($format); + $replace[] = str_replace($format, $date, $matches[1]); + } + + $search[] = '{$custom1}'; + $replace[] = [$client->custom_value1]; + + $search[] = '{$custom2}'; + $replace[] = [$client->custom_value1]; + + $search[] = '{$custom3}'; + $replace[] = [$client->custom_value1]; + + $search[] = '{$custom4}'; + $replace[] = [$client->custom_value1]; + + $search[] = '{$id_number}'; + $replace[] = [$client->id_number]; + + return str_replace($search, $replace, $pattern); + + } + +} \ No newline at end of file diff --git a/app/Utils/Traits/GeneratesNumberCounter.php b/app/Utils/Traits/GeneratesNumberCounter.php index cb85defe8b3f..40bf84e16096 100644 --- a/app/Utils/Traits/GeneratesNumberCounter.php +++ b/app/Utils/Traits/GeneratesNumberCounter.php @@ -42,7 +42,7 @@ trait GeneratesNumberCounter if ($this->hasNumberPattern($entity)) { $number = $this->applyNumberPattern($entity, $counter); } else { - $number = $prefix . str_pad($counter, $entity->getSettingsByKey('counter_padding')->counter_padding, '0', STR_PAD_LEFT); + $number = $prefix . str_pad($counter, $entity->getSettingsByKey('counter_padding'), '0', STR_PAD_LEFT); } @@ -85,7 +85,7 @@ trait GeneratesNumberCounter public function hasSharedCounter($entity) : bool { - return $entity->getSettingsByKey('shared_invoice_quote_counter')->shared_invoice_quote_counter === TRUE; + return $entity->getSettingsByKey('shared_invoice_quote_counter') === TRUE; } @@ -116,7 +116,7 @@ trait GeneratesNumberCounter $field = $entity_name . "_number_pattern"; - return $entity->getSettingsByKey( $field )->{$field}; + return $entity->getSettingsByKey( $field ); } @@ -131,7 +131,7 @@ trait GeneratesNumberCounter $field = $this->entityName($entity_name) . "_number_prefix"; - return $entity->getSettingsByKey( $field )->{$field}; + return $entity->getSettingsByKey( $field ); } @@ -176,9 +176,9 @@ trait GeneratesNumberCounter $counter = $this->getCounterName($entity) . '_number_counter'; if($counter == 'client_number_counter') - return $entity->company->getSettingsByKey( $counter )->{$counter}; + return $entity->company->getSettingsByKey( $counter ); else - return $entity->getSettingsByKey( $counter )->{$counter}; + return $entity->getSettingsByKey( $counter ); } @@ -203,7 +203,7 @@ trait GeneratesNumberCounter $replace = [date('Y')]; $search[] = '{$counter}'; - $replace[] = str_pad($counter, $entity->getSettingsByKey( 'counter_padding' )->counter_padding, '0', STR_PAD_LEFT); + $replace[] = str_pad($counter, $entity->getSettingsByKey( 'counter_padding' ), '0', STR_PAD_LEFT); if (strstr($pattern, '{$user_id}')) { $user_id = auth()->check() ? auth()->user()->id : 0; @@ -251,8 +251,8 @@ trait GeneratesNumberCounter $counter = $this->getCounterName($entity) . '_number_counter'; - $counter_value = $entity->client->getSettingsByKey( $counter )->{$counter}; - $entity_padding = $this->getSettingsByKey( 'counter_padding' )->counter_padding; + $counter_value = $entity->client->getSettingsByKey( $counter ); + $entity_padding = $this->getSettingsByKey( 'counter_padding' ); $replace = [ $this->custom_value1,