diff --git a/app/Console/Commands/CreateSingleAccount.php b/app/Console/Commands/CreateSingleAccount.php index 881542cf07..785bdd1561 100644 --- a/app/Console/Commands/CreateSingleAccount.php +++ b/app/Console/Commands/CreateSingleAccount.php @@ -223,7 +223,7 @@ class CreateSingleAccount extends Command 'company_id' => $company->id, ]); - $client->id_number = $this->getNextClientNumber($client); + $client->number = $this->getNextClientNumber($client); $settings = $client->settings; $settings->currency_id = "1"; diff --git a/app/Console/Commands/CreateTestData.php b/app/Console/Commands/CreateTestData.php index ab04224278..30803cfcd8 100644 --- a/app/Console/Commands/CreateTestData.php +++ b/app/Console/Commands/CreateTestData.php @@ -400,7 +400,7 @@ class CreateTestData extends Command 'company_id' => $company->id, ]); - $client->id_number = $this->getNextClientNumber($client); + $client->number = $this->getNextClientNumber($client); $settings = $client->settings; $settings->currency_id = (string) rand(1, 79); diff --git a/app/Console/Commands/DemoMode.php b/app/Console/Commands/DemoMode.php index 022b7e7002..65629ecacb 100644 --- a/app/Console/Commands/DemoMode.php +++ b/app/Console/Commands/DemoMode.php @@ -286,7 +286,7 @@ class DemoMode extends Command 'company_id' => $company->id, ]); - $client->id_number = $this->getNextClientNumber($client); + $client->number = $this->getNextClientNumber($client); $settings = $client->settings; $settings->currency_id = (string) rand(1, 3); diff --git a/app/Http/Controllers/OpenAPI/ClientSchema.php b/app/Http/Controllers/OpenAPI/ClientSchema.php index 1bfa27aa44..7198eb027f 100644 --- a/app/Http/Controllers/OpenAPI/ClientSchema.php +++ b/app/Http/Controllers/OpenAPI/ClientSchema.php @@ -33,6 +33,7 @@ * @OA\Property(property="custom_value4", type="string", example="", description="________"), * @OA\Property(property="vat_number", type="string", example="", description="________"), * @OA\Property(property="id_number", type="string", example="", description="________"), + * @OA\Property(property="number", type="string", example="", description="________"), * @OA\Property(property="shipping_address1", type="string", example="", description="________"), * @OA\Property(property="shipping_address2", type="string", example="", description="________"), * @OA\Property(property="shipping_city", type="string", example="", description="________"), diff --git a/app/Http/Controllers/OpenAPI/VendorSchema.php b/app/Http/Controllers/OpenAPI/VendorSchema.php index bfc7968d97..fc91d27280 100644 --- a/app/Http/Controllers/OpenAPI/VendorSchema.php +++ b/app/Http/Controllers/OpenAPI/VendorSchema.php @@ -35,6 +35,7 @@ * @OA\Property(property="custom_value4", type="string", example="", description="________"), * @OA\Property(property="vat_number", type="string", example="", description="________"), * @OA\Property(property="id_number", type="string", example="", description="________"), + * @OA\Property(property="number", type="string", example="", description="________"), * @OA\Property(property="is_deleted", type="boolean", example=true, description="________"), * @OA\Property(property="last_login", type="number", format="integer", example="134341234234", description="Timestamp"), * @OA\Property(property="created_at", type="number", format="integer", example="134341234234", description="Timestamp"), diff --git a/app/Http/Requests/Quote/StoreQuoteRequest.php b/app/Http/Requests/Quote/StoreQuoteRequest.php index 48e2ee5508..c75e571c9d 100644 --- a/app/Http/Requests/Quote/StoreQuoteRequest.php +++ b/app/Http/Requests/Quote/StoreQuoteRequest.php @@ -32,51 +32,6 @@ class StoreQuoteRequest extends Request return auth()->user()->can('create', Quote::class); } - protected function prepareForValidation() - { - $input = $this->all(); - - if (array_key_exists('design_id', $input) && is_string($input['design_id'])) { - $input['design_id'] = $this->decodePrimaryKey($input['design_id']); - } - - if ($input['client_id']) { - $input['client_id'] = $this->decodePrimaryKey($input['client_id']); - } - - if (array_key_exists('assigned_user_id', $input) && is_string($input['assigned_user_id'])) { - $input['assigned_user_id'] = $this->decodePrimaryKey($input['assigned_user_id']); - } - - if (isset($input['client_contacts'])) { - foreach ($input['client_contacts'] as $key => $contact) { - if (! array_key_exists('send_email', $contact) || ! array_key_exists('id', $contact)) { - unset($input['client_contacts'][$key]); - } - } - } - - if (isset($input['invitations'])) { - foreach ($input['invitations'] as $key => $value) { - if (isset($input['invitations'][$key]['id']) && is_numeric($input['invitations'][$key]['id'])) { - unset($input['invitations'][$key]['id']); - } - - if (isset($input['invitations'][$key]['id']) && is_string($input['invitations'][$key]['id'])) { - $input['invitations'][$key]['id'] = $this->decodePrimaryKey($input['invitations'][$key]['id']); - } - - if (is_string($input['invitations'][$key]['client_contact_id'])) { - $input['invitations'][$key]['client_contact_id'] = $this->decodePrimaryKey($input['invitations'][$key]['client_contact_id']); - } - } - } - - $input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : []; - //$input['line_items'] = json_encode($input['line_items']); - $this->replace($input); - } - public function rules() { $rules = []; @@ -98,4 +53,17 @@ class StoreQuoteRequest extends Request return $rules; } + + protected function prepareForValidation() + { + $input = $this->all(); + + $input = $this->decodePrimaryKeys($input); + + $input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : []; + $input['amount'] = 0; + $input['balance'] = 0; + + $this->replace($input); + } } diff --git a/app/Http/Requests/Quote/UpdateQuoteRequest.php b/app/Http/Requests/Quote/UpdateQuoteRequest.php index d9f65c710b..019c630aac 100644 --- a/app/Http/Requests/Quote/UpdateQuoteRequest.php +++ b/app/Http/Requests/Quote/UpdateQuoteRequest.php @@ -59,22 +59,12 @@ class UpdateQuoteRequest extends Request { $input = $this->all(); - if (array_key_exists('design_id', $input) && is_string($input['design_id'])) { - $input['design_id'] = $this->decodePrimaryKey($input['design_id']); - } - - if (isset($input['client_id'])) { - $input['client_id'] = $this->decodePrimaryKey($input['client_id']); - } + $input = $this->decodePrimaryKeys($input); if (isset($input['line_items'])) { $input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : []; } - if (array_key_exists('assigned_user_id', $input) && is_string($input['assigned_user_id'])) { - $input['assigned_user_id'] = $this->decodePrimaryKey($input['assigned_user_id']); - } - if (array_key_exists('documents', $input)) { unset($input['documents']); } diff --git a/app/Mail/Engine/PaymentEmailEngine.php b/app/Mail/Engine/PaymentEmailEngine.php index e529bb8125..6160516256 100644 --- a/app/Mail/Engine/PaymentEmailEngine.php +++ b/app/Mail/Engine/PaymentEmailEngine.php @@ -123,6 +123,7 @@ class PaymentEmailEngine extends BaseEmailEngine $data['$address1'] = ['value' => $this->client->address1 ?: ' ', 'label' => ctrans('texts.address1')]; $data['$address2'] = ['value' => $this->client->address2 ?: ' ', 'label' => ctrans('texts.address2')]; $data['$id_number'] = ['value' => $this->client->id_number ?: ' ', 'label' => ctrans('texts.id_number')]; + $data['$client.number'] = ['value' => $this->client->number ?: ' ', 'label' => ctrans('texts.number')]; $data['$vat_number'] = ['value' => $this->client->vat_number ?: ' ', 'label' => ctrans('texts.vat_number')]; $data['$website'] = ['value' => $this->client->present()->website() ?: ' ', 'label' => ctrans('texts.website')]; $data['$phone'] = ['value' => $this->client->present()->phone() ?: ' ', 'label' => ctrans('texts.phone')]; diff --git a/app/Models/Client.php b/app/Models/Client.php index a94d23514c..d8a35da1f3 100644 --- a/app/Models/Client.php +++ b/app/Models/Client.php @@ -73,7 +73,8 @@ class Client extends BaseModel implements HasLocalePreference 'id_number', 'group_settings_id', 'public_notes', - 'phone' + 'phone', + 'number', ]; protected $with = [ diff --git a/app/Models/Vendor.php b/app/Models/Vendor.php index e5f04a310c..d4987f0c18 100644 --- a/app/Models/Vendor.php +++ b/app/Models/Vendor.php @@ -44,6 +44,7 @@ class Vendor extends BaseModel 'custom_value2', 'custom_value3', 'custom_value4', + 'number', ]; protected $casts = [ diff --git a/app/PaymentDrivers/Authorize/AuthorizePaymentMethod.php b/app/PaymentDrivers/Authorize/AuthorizePaymentMethod.php index 9cd4d2cde7..0526476824 100644 --- a/app/PaymentDrivers/Authorize/AuthorizePaymentMethod.php +++ b/app/PaymentDrivers/Authorize/AuthorizePaymentMethod.php @@ -94,6 +94,7 @@ class AuthorizePaymentMethod if ($client_gateway_token = $this->authorize->findClientGatewayRecord()) { $payment_profile = $this->addPaymentMethodToClient($client_gateway_token->gateway_customer_reference, $data); + $gateway_customer_reference = $client_gateway_token->gateway_customer_reference; } else { $gateway_customer_reference = (new AuthorizeCreateCustomer($this->authorize, $this->authorize->client))->create($data); $payment_profile = $this->addPaymentMethodToClient($gateway_customer_reference, $data); diff --git a/app/Repositories/ClientRepository.php b/app/Repositories/ClientRepository.php index 088155fc7d..4fc82a56dc 100644 --- a/app/Repositories/ClientRepository.php +++ b/app/Repositories/ClientRepository.php @@ -60,8 +60,8 @@ class ClientRepository extends BaseRepository $client->fill($data); - if (!isset($client->id_number) || empty($client->id_number)) { - $client->id_number = $this->getNextClientNumber($client); + if (!isset($client->number) || empty($client->number)) { + $client->number = $this->getNextClientNumber($client); } if (empty($data['name'])) { diff --git a/app/Repositories/VendorRepository.php b/app/Repositories/VendorRepository.php index f59b3c691a..03306a5c8f 100644 --- a/app/Repositories/VendorRepository.php +++ b/app/Repositories/VendorRepository.php @@ -47,8 +47,8 @@ class VendorRepository extends BaseRepository $vendor->save(); - if ($vendor->id_number == '' || ! $vendor->id_number) { - $vendor->id_number = $this->getNextVendorNumber($vendor); + if ($vendor->number == '' || ! $vendor->number) { + $vendor->number = $this->getNextVendorNumber($vendor); } //todo write tests for this and make sure that custom vendor numbers also works as expected from here $vendor->save(); diff --git a/app/Services/Quote/CreateInvitations.php b/app/Services/Quote/CreateInvitations.php index 9ea688641a..0229e7cccc 100644 --- a/app/Services/Quote/CreateInvitations.php +++ b/app/Services/Quote/CreateInvitations.php @@ -41,6 +41,7 @@ class CreateInvitations $invitation = QuoteInvitation::whereCompanyId($this->quote->company_id) ->whereClientContactId($contact->id) ->whereQuoteId($this->quote->id) + ->withTrashed() ->first(); if (! $invitation && $contact->send_email) { diff --git a/app/Transformers/ClientTransformer.php b/app/Transformers/ClientTransformer.php index 9e2b212f62..34341eca34 100644 --- a/app/Transformers/ClientTransformer.php +++ b/app/Transformers/ClientTransformer.php @@ -147,6 +147,7 @@ class ClientTransformer extends EntityTransformer 'archived_at' => (int) $client->deleted_at, 'created_at' => (int) $client->created_at, 'display_name' => $client->present()->name(), + 'number' => (string) $client->number ?: '', ]; } } diff --git a/app/Utils/HtmlEngine.php b/app/Utils/HtmlEngine.php index bcb400224f..9373325d46 100644 --- a/app/Utils/HtmlEngine.php +++ b/app/Utils/HtmlEngine.php @@ -216,6 +216,7 @@ class HtmlEngine $data['$address1'] = ['value' => $this->client->address1 ?: ' ', 'label' => ctrans('texts.address1')]; $data['$address2'] = ['value' => $this->client->address2 ?: ' ', 'label' => ctrans('texts.address2')]; $data['$id_number'] = ['value' => $this->client->id_number ?: ' ', 'label' => ctrans('texts.id_number')]; + $data['$client.number'] = ['value' => $this->client->number ?: ' ', 'label' => ctrans('texts.number')]; $data['$vat_number'] = ['value' => $this->client->vat_number ?: ' ', 'label' => ctrans('texts.vat_number')]; $data['$website'] = ['value' => $this->client->present()->website() ?: ' ', 'label' => ctrans('texts.website')]; $data['$phone'] = ['value' => $this->client->present()->phone() ?: ' ', 'label' => ctrans('texts.phone')]; diff --git a/app/Utils/Traits/GeneratesCounter.php b/app/Utils/Traits/GeneratesCounter.php index 80fecb8b84..45b31134b4 100644 --- a/app/Utils/Traits/GeneratesCounter.php +++ b/app/Utils/Traits/GeneratesCounter.php @@ -355,9 +355,9 @@ trait GeneratesCounter * @param int $counter The counter * @param int $padding The padding * - * @param $pattern - * @param string $prefix - * @return string The padded and prefixed entity number + * @param string $pattern + * @param string $prefix + * @return string The padded and prefixed entity number */ private function checkEntityNumber($class, $entity, $counter, $padding, $pattern, $prefix = '') { @@ -370,13 +370,7 @@ trait GeneratesCounter $number = $this->prefixCounter($number, $prefix); - if ($class == Invoice::class || $class == RecurringInvoice::class) { - $check = $class::whereCompanyId($entity->company_id)->whereNumber($number)->withTrashed()->first(); - } elseif ($class == Client::class || $class == Vendor::class) { - $check = $class::whereCompanyId($entity->company_id)->whereIdNumber($number)->withTrashed()->first(); - } else { - $check = $class::whereCompanyId($entity->company_id)->whereNumber($number)->withTrashed()->first(); - } + $check = $class::whereCompanyId($entity->company_id)->whereNumber($number)->withTrashed()->first(); $counter++; } while ($check); @@ -388,11 +382,12 @@ trait GeneratesCounter /*Check if a number is available for use. */ public function checkNumberAvailable($class, $entity, $number) :bool { - if ($entity = $class::whereCompanyId($entity->company_id)->whereNumber($number)->withTrashed()->first()) { - return false; - } + if ($entity = $class::whereCompanyId($entity->company_id)->whereNumber($number)->withTrashed()->first()) + return false; + return true; + } /** @@ -611,6 +606,9 @@ trait GeneratesCounter $search[] = '{$vendor_id_number}'; $replace[] = $entity->vendor->id_number; + $search[] = '{$vendor_number}'; + $replace[] = $entity->vendor->number; + $search[] = '{$vendor_custom1}'; $replace[] = $entity->vendor->custom_value1; @@ -643,6 +641,9 @@ trait GeneratesCounter $search[] = '{$client_custom4}'; $replace[] = $client->custom_value4; + $search[] = '{$client_number}'; + $replace[] = $client->number; + $search[] = '{$client_id_number}'; $replace[] = $client->id_number; } diff --git a/database/migrations/2021_01_25_095351_add_number_field_to_clients_and_vendors.php b/database/migrations/2021_01_25_095351_add_number_field_to_clients_and_vendors.php new file mode 100644 index 0000000000..8eb8316503 --- /dev/null +++ b/database/migrations/2021_01_25_095351_add_number_field_to_clients_and_vendors.php @@ -0,0 +1,34 @@ +string('number')->nullable(); + }); + + Schema::table('vendors', function (Blueprint $table) { + $table->string('number')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + + } +} diff --git a/database/seeders/RandomDataSeeder.php b/database/seeders/RandomDataSeeder.php index 52507ec25b..bfe3f30c4c 100644 --- a/database/seeders/RandomDataSeeder.php +++ b/database/seeders/RandomDataSeeder.php @@ -294,7 +294,7 @@ class RandomDataSeeder extends Seeder foreach ($clients as $client) { //$client->getNextClientNumber($client); - $client->id_number = $client->getNextClientNumber($client); + $client->number = $client->getNextClientNumber($client); $client->save(); } diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php index 085e4cb2d6..728d5a293f 100644 --- a/resources/lang/en/texts.php +++ b/resources/lang/en/texts.php @@ -3210,7 +3210,6 @@ return [ 'waiting_for_approval' => 'Waiting for approval', 'quote_still_not_approved' => 'This quote is still not approved', 'list_of_credits' => 'Credits', - 'required_extensions' => 'Required extensions', 'php_version' => 'PHP version', 'writable_env_file' => 'Writable .env file', @@ -3218,11 +3217,9 @@ return [ 'minumum_php_version' => 'Minimum PHP version', 'satisfy_requirements' => 'Make sure all requirements are satisfied.', 'oops_issues' => 'Oops, something doesn\'t look right!', - 'open_in_new_tab' => 'Open in new tab', 'complete_your_payment' => 'Complete payment', 'authorize_for_future_use' => 'Authorize payment method for future use', - 'page' => 'Page', 'per_page' => 'Per page', 'of' => 'Of', @@ -3230,122 +3227,81 @@ return [ 'to_view_entity_password' => 'To view the :entity you need to enter password.', 'showing_x_of' => 'Showing :first to :last out of :total results', 'no_results' => 'No results found.', - 'payment_failed_subject' => 'Payment failed for Client :client', 'payment_failed_body' => 'A payment made by client :client failed with message :message', - 'register' => 'Register', 'register_label' => 'Create your account in seconds', 'password_confirmation' => 'Confirm your password', 'verification' => 'Verification', - 'complete_your_bank_account_verification' => 'Before using bank account they must be verified.', - + 'complete_your_bank_account_verification' => 'Before using a bank account it must be verified.', 'checkout_com' => 'Checkout.com', - 'footer_label' => 'Copyright © :year :company.', - 'credit_card_invalid' => 'Provided credit card number is not valid.', 'month_invalid' => 'Provided month is not valid.', 'year_invalid' => 'Provided year is not valid.', - 'https_required' => 'HTTPS is required, form will fail', 'if_you_need_help' => 'If you need help you can post to our', 'reversed' => 'Reversed', 'update_password_on_confirm' => 'After updating password, your account will be confirmed.', - 'bank_account_not_linked' => 'To pay with bank account, first you have to add it as payment method.', - + 'bank_account_not_linked' => 'To pay with a bank account, first you have to add it as payment method.', 'application_settings_label' => 'Let\'s store basic information about your Invoice Ninja!', 'recommended_in_production' => 'Highly recommended in production', 'enable_only_for_development' => 'Enable only for development', - 'test_pdf' => 'Test PDF', 'cancelled' => 'Cancelled', - 'checkout_authorize_label' => 'Checkout.com can be can saved as payment method for future use, once you complete your first transaction. Don\'t forget to check "Store credit card details" during payment process.', 'sofort_authorize_label' => 'Bank account (SOFORT) can be can saved as payment method for future use, once you complete your first transaction. Don\'t forget to check "Store payment details" during payment process.', - 'node_status' => 'Node status', 'npm_status' => 'NPM status', 'node_status_not_found' => 'I could not find Node anywhere. Is it installed?', 'npm_status_not_found' => 'I could not find NPM anywhere. Is it installed?', 'locked_invoice' => 'This invoice is locked and unable to be modified', - 'downloads' => 'Downloads', 'resource' => 'Resource', - 'document_details' => 'Details about the document.', - 'width' => 'Width', 'height' => 'Height', - 'document_details' => 'Details about the document', - 'hash' => 'Hash', - 'resources' => 'Resources', - 'allowed_file_types' => 'Allowed file types:', 'common_codes' => 'Common codes and their meanings', - 'payment_error_code_20087' => '20087: Bad Track Data (invalid CVV and/or expiry date)', - 'download_selected' => 'Download selected', - 'to_pay_invoices' => 'To pay invoices, you have to', 'add_payment_method_first' => 'add payment method', - 'no_items_selected' => 'No items selected.', - 'payment_due' => 'Payment due', 'account_balance' => 'Account balance', 'password_strength' => 'Password strength too weak', - 'thanks' => 'Thanks', - 'minimum_required_payment' => 'Minimum required payment is :amount', - 'under_payments_disabled' => 'Company doesn\'t support under payments.', 'over_payments_disabled' => 'Company doesn\'t support over payments.', - 'paused' => 'Paused', - 'saved_at' => 'Saved at :time', 'credit_payment' => 'Credit applied to Invoice :invoice_number', - 'credit_subject' => 'New credit :number from :account', 'credit_message' => 'To view your credit for :amount, click the link below.', 'payment_type_Crypto' => 'Cryptocurrency', 'payment_type_Credit' => 'Credit', - 'store_for_future_use' => 'Store for future use', - 'pay_with_credit' => 'Pay with credit', 'payment_method_saving_failed' => 'Payment method can\'t be saved for future use.', - 'pay_with' => 'Pay with', - 'n/a' => 'N/A', 'payment_number' => 'Payment Number', - 'activity_63' => ':user emailed reminder 1 for invoice :invoice to :contact', 'activity_64' => ':user emailed reminder 2 for invoice :invoice to :contact', 'activity_65' => ':user emailed reminder 3 for invoice :invoice to :contact', 'activity_66' => ':user emailed reminder endless for invoice :invoice to :contact', - 'by_clicking_next_you_accept_terms' => 'By clicking "Next step" you accept terms.', 'not_specified' => 'Not specified', - 'before_proceeding_with_payment_warning' => 'Before proceeding with payment, you have to fill following fields', 'after_completing_go_back_to_previous_page' => 'After completing, go back to previous page.', - - 'billing_country' => 'Billing Country', - 'shipping_country' => 'Shipping Country', - 'service' => 'Service', 'pay' => 'Pay', - 'instructions' => 'Instructions', - 'notification_invoice_reminder1_sent_subject' => 'Reminder 1 for Invoice :invoice was sent to :client', 'notification_invoice_reminder2_sent_subject' => 'Reminder 2 for Invoice :invoice was sent to :client', 'notification_invoice_reminder3_sent_subject' => 'Reminder 3 for Invoice :invoice was sent to :client', @@ -3355,26 +3311,19 @@ return [ 'custom_value4' => 'Custom Value', 'inclusive_taxes' => 'Include taxes', 'sort_order' => 'Sort Order', - 'setup_steps_notice' => 'To proceed to next step, make sure you test each section.', - 'setup_phantomjs_note' => 'Note about Phantom JS. Read more.', 'currency_armenian_dram' => 'Armenian Dram', 'currency_albanian_lek' => 'Albanian Lek', - 'endless' => 'Endless', 'minimum_payment' => 'Minimum Payment', - 'no_action_provided' => 'No action provided. If you believe this is wrong, please contact the support.', 'no_payable_invoices_selected' => 'No payable invoices selected. Make sure you are not trying to pay draft invoice or invoice with zero balance due.', - 'required_payment_information' => 'Required payment details', 'required_payment_information_more' => 'To complete a payment we need more details about you.', - 'required_client_info_save_label' => 'We will save this, so you don\'t have to enter it next time.', 'notification_credit_bounced' => 'We were unable to deliver Credit :invoice to :contact. \n :error', 'notification_credit_bounced_subject' => 'Unable to deliver Credit :invoice', - 'company_limit_reached' => 'Limit of 10 companies per account.', 'credits_applied_validation' => 'Total credits applied cannot be MORE than total of invoices', 'credit_number_taken' => 'Credit number already taken', @@ -3429,4 +3378,5 @@ return [ 'self_update_not_available' => 'Self update not available on this system.', 'user_detached' => 'User detached from company', 'create_webhook_failure' => 'Failed to create Webhook', + 'number' => 'Number', ];