diff --git a/app/DataMapper/CompanySettings.php b/app/DataMapper/CompanySettings.php index e3d9830941ab..76fc05d086ea 100644 --- a/app/DataMapper/CompanySettings.php +++ b/app/DataMapper/CompanySettings.php @@ -72,6 +72,7 @@ class CompanySettings extends BaseSettings public $translations; + public $counter_number_applied = 'when_saved'; // when_saved , when_sent , when_paid /* Counters */ public $invoice_number_pattern = ''; public $invoice_number_counter = 1; @@ -132,7 +133,6 @@ class CompanySettings extends BaseSettings public $tax_rate2 = 0; public $tax_name3 = ''; public $tax_rate3 = 0; - public $number_of_tax_rates = 1; public $payment_type_id = '1'; public $custom_fields = ''; public $invoice_fields = ''; @@ -226,7 +226,7 @@ class CompanySettings extends BaseSettings public static $casts = [ - 'number_of_tax_rates' => 'int', + 'counter_number_applied' => 'string', 'email_subject_custom1' => 'string', 'email_subject_custom2' => 'string', 'email_subject_custom3' => 'string', diff --git a/app/Helpers/Invoice/InvoiceItemSum.php b/app/Helpers/Invoice/InvoiceItemSum.php index d03c9629e59e..0007766dd449 100644 --- a/app/Helpers/Invoice/InvoiceItemSum.php +++ b/app/Helpers/Invoice/InvoiceItemSum.php @@ -241,6 +241,9 @@ class InvoiceItemSum foreach($this->line_items as $this->item) { + if($this->item->line_total == 0) + continue; + $amount = $this->item->line_total - ($this->item->line_total * ($this->invoice->discount/$this->sub_total)); $item_tax_rate1_total = $this->calcAmountLineTax($this->item->tax_rate1, $amount); diff --git a/app/Http/Requests/Invoice/StoreInvoiceRequest.php b/app/Http/Requests/Invoice/StoreInvoiceRequest.php index 31af5d0aaeba..0fb25feeb6cf 100644 --- a/app/Http/Requests/Invoice/StoreInvoiceRequest.php +++ b/app/Http/Requests/Invoice/StoreInvoiceRequest.php @@ -14,11 +14,13 @@ namespace App\Http\Requests\Invoice; use App\Http\Requests\Request; use App\Models\ClientContact; use App\Models\Invoice; +use App\Utils\Traits\CleanLineItems; use App\Utils\Traits\MakesHash; class StoreInvoiceRequest extends Request { use MakesHash; + use CleanLineItems; /** * Determine if the user is authorized to make this request. @@ -34,6 +36,7 @@ class StoreInvoiceRequest extends Request public function rules() { $this->sanitize(); + return [ 'client_id' => 'required', // 'invoice_type_id' => 'integer', @@ -46,11 +49,12 @@ class StoreInvoiceRequest extends Request $input = $this->all(); $input['client_id'] = $this->decodePrimaryKey($input['client_id']); - $input['line_items'] = isset($input['line_items']) ? $input['line_items'] : []; + $input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : []; //$input['line_items'] = json_encode($input['line_items']); $this->replace($input); return $this->all(); } + } diff --git a/app/Http/Requests/Invoice/UpdateInvoiceRequest.php b/app/Http/Requests/Invoice/UpdateInvoiceRequest.php index 3c498071e136..1d2cb4cd71e6 100644 --- a/app/Http/Requests/Invoice/UpdateInvoiceRequest.php +++ b/app/Http/Requests/Invoice/UpdateInvoiceRequest.php @@ -12,6 +12,7 @@ namespace App\Http\Requests\Invoice; use App\Http\Requests\Request; +use App\Utils\Traits\CleanLineItems; use App\Utils\Traits\MakesHash; use Illuminate\Support\Facades\Log; use Illuminate\Validation\Rule; @@ -19,6 +20,7 @@ use Illuminate\Validation\Rule; class UpdateInvoiceRequest extends Request { use MakesHash; + use CleanLineItems; /** * Determine if the user is authorized to make this request. * @@ -52,6 +54,8 @@ class UpdateInvoiceRequest extends Request if(isset($input['client_id'])) $input['client_id'] = $this->decodePrimaryKey($input['client_id']); + $input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : []; + $this->replace($input); return $this->all(); diff --git a/app/Jobs/Invoice/ApplyInvoiceNumber.php b/app/Jobs/Invoice/ApplyInvoiceNumber.php new file mode 100644 index 000000000000..face7abd7f37 --- /dev/null +++ b/app/Jobs/Invoice/ApplyInvoiceNumber.php @@ -0,0 +1,81 @@ +invoice = $invoice; + + $this->settings = $settings; + } + + /** + * Execute the job. + * + * + * @return void + */ + public function handle() + { + //return early + if($this->invoice->invoice_number != '') + return $this->invoice; + + switch ($this->settings->counter_number_applied) { + case 'when_saved': + $this->invoice->invoice_number = $this->getNextInvoiceNumber($this->invoice->client); + break; + case 'when_sent': + if($this->invoice->status_id == Invoice::STATUS_SENT) + $this->invoice->invoice_number = $this->getNextInvoiceNumber($this->invoice->client); + break; + + default: + # code... + break; + } + + $this->invoice->save(); + + return $this->invoice; + + } + + +} diff --git a/app/Jobs/Invoice/ApplyPaymentToInvoice.php b/app/Jobs/Invoice/ApplyPaymentToInvoice.php index 890ec3a4111e..7a791bb9ec26 100644 --- a/app/Jobs/Invoice/ApplyPaymentToInvoice.php +++ b/app/Jobs/Invoice/ApplyPaymentToInvoice.php @@ -11,6 +11,7 @@ namespace App\Jobs\Invoice; +use App\Jobs\Invoice\ApplyInvoiceNumber; use App\Models\Invoice; use App\Models\Payment; use App\Models\PaymentTerm; @@ -108,6 +109,8 @@ class ApplyPaymentToInvoice implements ShouldQueue $this->invoice->save(); + $this->invoice = ApplyInvoiceNumber::dispatchNow($this->invoice, $invoice->client->getMergedSettings()); + return $this->invoice; } } diff --git a/app/Repositories/InvoiceRepository.php b/app/Repositories/InvoiceRepository.php index a8b1d334162a..ae22f7a12075 100644 --- a/app/Repositories/InvoiceRepository.php +++ b/app/Repositories/InvoiceRepository.php @@ -16,6 +16,7 @@ use App\Events\Invoice\InvoiceWasUpdated; use App\Factory\InvoiceInvitationFactory; use App\Helpers\Invoice\InvoiceSum; use App\Jobs\Company\UpdateCompanyLedgerWithInvoice; +use App\Jobs\Invoice\ApplyInvoiceNumber; use App\Listeners\Invoice\CreateInvoiceInvitation; use App\Models\ClientContact; use App\Models\Invoice; @@ -120,6 +121,8 @@ class InvoiceRepository extends BaseRepository if($finished_amount != $starting_amount) UpdateCompanyLedgerWithInvoice::dispatchNow($invoice, ($finished_amount - $starting_amount)); + $invoice = ApplyInvoiceNumber::dispatchNow($invoice, $invoice->client->getMergedSettings()); + return $invoice->fresh(); } @@ -139,7 +142,7 @@ class InvoiceRepository extends BaseRepository $invoice->status_id = Invoice::STATUS_SENT; - $this->markInvitationsSent(); + $this->markInvitationsSent($invoice); $invoice->save(); @@ -148,7 +151,9 @@ class InvoiceRepository extends BaseRepository * When marked as sent it becomes a ledgerable item. * */ - UpdateCompanyLedgerWithInvoice::dispatchNow($this->invoice, $this->balance); + $invoice = ApplyInvoiceNumber::dispatchNow($invoice, $invoice->client->getMergedSettings()); + + UpdateCompanyLedgerWithInvoice::dispatchNow($invoice, $this->balance); return $invoice; diff --git a/app/Transformers/InvoiceTransformer.php b/app/Transformers/InvoiceTransformer.php index f36e5e62e6ce..cccb2b4c5f9f 100644 --- a/app/Transformers/InvoiceTransformer.php +++ b/app/Transformers/InvoiceTransformer.php @@ -92,7 +92,7 @@ class InvoiceTransformer extends EntityTransformer 'design_id' => (string) ($invoice->design_id ?: 1), 'updated_at' => $invoice->updated_at, 'archived_at' => $invoice->deleted_at, - 'invoice_number' => $invoice->invoice_number, + 'invoice_number' => $invoice->invoice_number ?: '', 'discount' => (float) $invoice->discount, 'po_number' => $invoice->po_number ?: '', 'invoice_date' => $invoice->invoice_date ?: '', diff --git a/app/Utils/Traits/CleanLineItems.php b/app/Utils/Traits/CleanLineItems.php new file mode 100644 index 000000000000..327b6aafca4c --- /dev/null +++ b/app/Utils/Traits/CleanLineItems.php @@ -0,0 +1,67 @@ +cleanLineItem($item); + + return $cleaned_items; + + } + + /** + * Sets default values for the line_items + * @return $this + */ + private function cleanLineItem($item) + { + $invoice_item = (object)get_class_vars(InvoiceItem::class); + unset($invoice_item->casts); + + foreach($invoice_item as $key => $value) + { + + if(!array_key_exists($key, $item) || !isset($item[$key])){ + $item[$key] = $value; + $item[$key] = BaseSettings::castAttribute(InvoiceItem::$casts[$key], $value); + } + + } + + if(array_key_exists("id", $item)) + { + $item['id'] = $item['id']*-1; + } + + return $item; + } + +} + diff --git a/app/Utils/Traits/GeneratesCounter.php b/app/Utils/Traits/GeneratesCounter.php index d96661fb42fe..379ccb46924f 100644 --- a/app/Utils/Traits/GeneratesCounter.php +++ b/app/Utils/Traits/GeneratesCounter.php @@ -41,33 +41,33 @@ trait GeneratesCounter //Reset counters if enabled $this->resetCounters($client); - $is_client_counter = false; - //todo handle if we have specific client patterns in the future - $pattern = $client->company->settings->invoice_number_pattern; + $pattern = $client->getSetting('invoice_number_pattern'); //Determine if we are using client_counters - if(strpos($pattern, 'client_counter') === false) + if(strpos($pattern, 'clientCounter')) { - $counter = $client->company->settings->invoice_number_counter; + $counter = $client->settings->invoice_number_counter; + $counter_entity = $client; + } + elseif(strpos($pattern, 'groupCounter')) + { + $counter = $client->group_settings->invoice_number_counter; + $counter_entity = $client->group_settings; } else { - $counter = $client->settings->invoice_number_counter; - $is_client_counter = true; + $counter = $client->company->settings->invoice_number_counter; + $counter_entity = $client->company; } //Return a valid counter - $pattern = $client->company->settings->invoice_number_pattern; - $prefix = $client->company->settings->invoice_number_prefix; - $padding = $client->company->settings->counter_padding; + $pattern = $client->getSetting('invoice_number_pattern'); + $prefix = $client->getSetting('invoice_number_prefix'); + $padding = $client->getSetting('counter_padding'); $invoice_number = $this->checkEntityNumber(Invoice::class, $client, $counter, $padding, $prefix, $pattern); - //increment the correct invoice_number Counter (company vs client) - if($is_client_counter) - $this->incrementCounter($client, 'invoice_number_counter'); - else - $this->incrementCounter($client->company, 'invoice_number_counter'); + $this->incrementCounter($counter_entity, 'invoice_number_counter'); return $invoice_number; } @@ -339,7 +339,10 @@ trait GeneratesCounter $search[] = '{$counter}'; $replace[] = $counter; - $search[] = '{$client_counter}'; + $search[] = '{$clientCounter}'; + $replace[] = $counter; + + $search[] = '{$groupCounter}'; $replace[] = $counter; if (strstr($pattern, '{$user_id}')) { diff --git a/database/migrations/2014_10_13_000000_create_users_table.php b/database/migrations/2014_10_13_000000_create_users_table.php index a5133c939de6..926fe440f490 100644 --- a/database/migrations/2014_10_13_000000_create_users_table.php +++ b/database/migrations/2014_10_13_000000_create_users_table.php @@ -2,6 +2,7 @@ use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; +use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Schema; class CreateUsersTable extends Migration @@ -182,6 +183,8 @@ class CreateUsersTable extends Migration }); + DB::statement('ALTER table companies key_block_size=8 row_format=compressed'); + Schema::create('company_user', function (Blueprint $table) { $table->increments('id'); diff --git a/database/seeds/PaymentLibrariesSeeder.php b/database/seeds/PaymentLibrariesSeeder.php index 11a096b7ba81..4ed6d39c4e7d 100644 --- a/database/seeds/PaymentLibrariesSeeder.php +++ b/database/seeds/PaymentLibrariesSeeder.php @@ -30,7 +30,7 @@ class PaymentLibrariesSeeder extends Seeder ['name' => 'PayPal Pro', 'provider' => 'PayPal_Pro', 'key' => '80af24a6a69f5c0bbec33e930ab40665', 'fields' => '{"username":"","password":"","signature":"","testMode":false}'], ['name' => 'Pin', 'provider' => 'Pin', 'key' => '0749cb92a6b36c88bd9ff8aabd2efcab', 'fields' => '{"secretKey":"","testMode":false}'], ['name' => 'SagePay Direct', 'provider' => 'SagePay_Direct', 'key' => '4c8f4e5d0f353a122045eb9a60cc0f2d', 'fields' => '{"vendor":"","testMode":false,"referrerId":""}'], - ['name' => 'SecurePay DirectPost', 'provider' => 'SecurePay_DirectPost', 'key' => '8036a5aadb2bdaafb23502da8790b6a2', 'fields' => '{"merchantId":"","transactionPassword":"","testMode":false}'], + ['name' => 'SecurePay DirectPost', 'provider' => 'SecurePay_DirectPost', 'key' => '8036a5aadb2bdaafb23502da8790b6a2', 'fields' => '{"merchantId":"","transactionPassword":"","testMode":false,"enable_ach":"","enable_sofort":"","enable_apple_pay":"","enable_alipay":""}'], ['name' => 'Stripe', 'provider' => 'Stripe', 'sort_order' => 1, 'key' => 'd14dd26a37cecc30fdd65700bfb55b23', 'fields' => '{"apiKey":"", "publishableKey":""}'], ['name' => 'TargetPay Direct eBanking', 'provider' => 'TargetPay_Directebanking', 'key' => 'd14dd26a37cdcc30fdd65700bfb55b23', 'fields' => '{"subAccountId":""}'], ['name' => 'TargetPay Ideal', 'provider' => 'TargetPay_Ideal', 'key' => 'ea3b328bd72d381387281c3bd83bd97c', 'fields' => '{"subAccountId":""}'], diff --git a/database/seeds/RandomDataSeeder.php b/database/seeds/RandomDataSeeder.php index 7524705f5e07..3294fdc0f9bd 100644 --- a/database/seeds/RandomDataSeeder.php +++ b/database/seeds/RandomDataSeeder.php @@ -114,7 +114,7 @@ class RandomDataSeeder extends Seeder factory(\App\Models\Product::class,50)->create(['user_id' => $user->id, 'company_id' => $company->id]); /** Invoice Factory */ - factory(\App\Models\Invoice::class,50)->create(['user_id' => $user->id, 'company_id' => $company->id, 'client_id' => $client->id, 'settings' => ClientSettings::buildClientSettings($company->settings, $client->settings)]); + factory(\App\Models\Invoice::class,500)->create(['user_id' => $user->id, 'company_id' => $company->id, 'client_id' => $client->id, 'settings' => ClientSettings::buildClientSettings($company->settings, $client->settings)]); $invoices = Invoice::all(); $invoice_repo = new InvoiceRepository(); diff --git a/tests/Unit/GeneratesCounterTest.php b/tests/Unit/GeneratesCounterTest.php index d81ef7f8bda8..02f3ffc2a48d 100644 --- a/tests/Unit/GeneratesCounterTest.php +++ b/tests/Unit/GeneratesCounterTest.php @@ -103,7 +103,8 @@ class GeneratesCounterTest extends TestCase public function testInvoiceNumberPattern() { $settings = $this->client->company->settings; - $settings->invoice_number_prefix = null; + $settings->invoice_number_prefix = ''; + $settings->invoice_number_counter = 1; $settings->invoice_number_pattern = '{$year}-{$counter}'; $this->client->company->settings = $settings; @@ -122,8 +123,8 @@ class GeneratesCounterTest extends TestCase { $settings = $this->client->company->settings; - $settings->invoice_number_prefix = null; - $settings->invoice_number_pattern = '{$year}-{$client_counter}'; + $settings->invoice_number_prefix = ''; + $settings->invoice_number_pattern = '{$year}-{$clientCounter}'; $this->client->company->settings = $settings; $this->client->company->save();