diff --git a/app/DataMapper/CompanySettings.php b/app/DataMapper/CompanySettings.php index 71e0871c6eb7..09f485cc83bc 100644 --- a/app/DataMapper/CompanySettings.php +++ b/app/DataMapper/CompanySettings.php @@ -53,7 +53,8 @@ class CompanySettings extends BaseSettings public $custom_message_unapproved_quote = ''; public $lock_sent_invoices = false; public $auto_archive_invoice = false; - + public $auto_archive_quote = false; + public $auto_convert_quote = false; public $inclusive_taxes = false; @@ -104,13 +105,9 @@ class CompanySettings extends BaseSettings public $recurring_invoice_number_prefix = 'R'; public $reset_counter_frequency_id = '0'; public $reset_counter_date = ''; - public $counter_padding = 0; + public $counter_padding = 4; public $design = 'views/pdf/design1.blade.php'; - - public $update_products = true; - public $fill_products = false; - public $convert_products = false; public $invoice_terms = ''; public $quote_terms = ''; @@ -127,7 +124,6 @@ class CompanySettings extends BaseSettings public $tax_rate2 = 0; public $tax_name3 = ''; public $tax_rate3 = 0; - public $enable_second_tax_rate = false; public $payment_type_id = '1'; public $custom_fields = ''; public $invoice_fields = ''; @@ -156,9 +152,11 @@ class CompanySettings extends BaseSettings public $email_subject_reminder1 = ''; public $email_subject_reminder2 = ''; public $email_subject_reminder3 = ''; + public $email_subject_reminder_endless = ''; public $email_template_reminder1 = ''; public $email_template_reminder2 = ''; public $email_template_reminder3 = ''; + public $email_template_reminder_endless = ''; public $email_footer = ''; /* Company Meta data that we can use to build sub companies*/ @@ -204,7 +202,6 @@ class CompanySettings extends BaseSettings 'address2' => 'string', 'city' => 'string', 'company_logo' => 'string', - 'convert_products' => 'bool', 'country_id' => 'string', 'client_number_prefix' => 'string', 'client_number_pattern' => 'string', @@ -237,12 +234,12 @@ class CompanySettings extends BaseSettings 'email_subject_reminder1' => 'string', 'email_subject_reminder2' => 'string', 'email_subject_reminder3' => 'string', + 'email_subject_reminder_endless' => 'string', 'email_template_reminder1' => 'string', 'email_template_reminder2' => 'string', 'email_template_reminder3' => 'string', + 'email_template_reminder_endless' => 'string', 'enable_portal_password' => 'bool', - 'enable_second_tax_rate' => 'bool', - 'fill_products' => 'bool', 'inclusive_taxes' => 'bool', 'invoice_number_prefix' => 'string', 'invoice_number_pattern' => 'string', @@ -270,7 +267,6 @@ class CompanySettings extends BaseSettings 'require_invoice_signature' => 'bool', 'require_quote_signature' => 'bool', 'show_item_taxes' => 'bool', - 'update_products' => 'bool', 'state' => 'string', 'email' => 'string', 'vat_number' => 'string', @@ -292,6 +288,8 @@ class CompanySettings extends BaseSettings 'show_tasks_in_portal' => 'bool', 'lock_sent_invoices' => 'bool', 'auto_archive_invoice' => 'bool', + 'auto_archive_quote' => 'bool', + 'auto_convert_quote' => 'bool', 'shared_invoice_quote_counter' => 'bool', 'counter_padding' => 'integer', 'design' => 'string', diff --git a/app/Factory/GroupSettingFactory.php b/app/Factory/GroupSettingFactory.php index faf2d9f59ee8..b065d8535388 100644 --- a/app/Factory/GroupSettingFactory.php +++ b/app/Factory/GroupSettingFactory.php @@ -23,7 +23,7 @@ class GroupSettingFactory $gs->name = ''; $gs->company_id = $company_id; $gs->user_id = $user_id; - $gs->settings = new \stdClass; + $gs->settings = '{}'; return $gs; diff --git a/app/Factory/TaxRateFactory.php b/app/Factory/TaxRateFactory.php new file mode 100644 index 000000000000..21a629f020ee --- /dev/null +++ b/app/Factory/TaxRateFactory.php @@ -0,0 +1,31 @@ +name = ''; + $tax_rate->rate = ''; + $tax_rate->company_id = $company_id; + $tax_rate->user_id = $user_id; + + return $tax_rate; + } + +} \ No newline at end of file diff --git a/app/Helpers/Invoice/InvoiceSum.php b/app/Helpers/Invoice/InvoiceSum.php index 42a49086f3f7..e169034fe6af 100644 --- a/app/Helpers/Invoice/InvoiceSum.php +++ b/app/Helpers/Invoice/InvoiceSum.php @@ -99,16 +99,16 @@ class InvoiceSum private function calculateCustomValues() { - $this->total_taxes += $this->valuerTax($this->invoice->custom_value1, $this->settings->custom_invoice_taxes1); + $this->total_taxes += $this->valuerTax($this->invoice->custom_value1, $this->invoice->custom_surcharge_taxes1); $this->total_custom_values += $this->valuer($this->invoice->custom_value1); - $this->total_taxes += $this->valuerTax($this->invoice->custom_value2, $this->settings->custom_invoice_taxes2); + $this->total_taxes += $this->valuerTax($this->invoice->custom_value2, $this->invoice->custom_surcharge_taxes2); $this->total_custom_values += $this->valuer($this->invoice->custom_value2); - $this->total_taxes += $this->valuerTax($this->invoice->custom_value3, $this->settings->custom_invoice_taxes3); + $this->total_taxes += $this->valuerTax($this->invoice->custom_value3, $this->invoice->custom_surcharge_taxes3); $this->total_custom_values += $this->valuer($this->invoice->custom_value3); - $this->total_taxes += $this->valuerTax($this->invoice->custom_value4, $this->settings->custom_invoice_taxes4); + $this->total_taxes += $this->valuerTax($this->invoice->custom_value4, $this->invoice->custom_surcharge_taxes4); $this->total_custom_values += $this->valuer($this->invoice->custom_value4); $this->total += $this->total_custom_values; diff --git a/app/Helpers/Invoice/InvoiceSumInclusive.php b/app/Helpers/Invoice/InvoiceSumInclusive.php index ff2c64a9cfd7..b2980cb552c1 100644 --- a/app/Helpers/Invoice/InvoiceSumInclusive.php +++ b/app/Helpers/Invoice/InvoiceSumInclusive.php @@ -100,16 +100,16 @@ class InvoiceSumInclusive private function calculateCustomValues() { - $this->total_taxes += $this->valuerTax($this->invoice->custom_value1, $this->settings->custom_invoice_taxes1); + $this->total_taxes += $this->valuerTax($this->invoice->custom_value1, $this->invoice->custom_surcharge_taxes1); $this->total_custom_values += $this->valuer($this->invoice->custom_value1); - $this->total_taxes += $this->valuerTax($this->invoice->custom_value2, $this->settings->custom_invoice_taxes2); + $this->total_taxes += $this->valuerTax($this->invoice->custom_value2, $this->invoice->custom_surcharge_taxes2); $this->total_custom_values += $this->valuer($this->invoice->custom_value2); - $this->total_taxes += $this->valuerTax($this->invoice->custom_value3, $this->settings->custom_invoice_taxes3); + $this->total_taxes += $this->valuerTax($this->invoice->custom_value3, $this->invoice->custom_surcharge_taxes3); $this->total_custom_values += $this->valuer($this->invoice->custom_value3); - $this->total_taxes += $this->valuerTax($this->invoice->custom_value4, $this->settings->custom_invoice_taxes4); + $this->total_taxes += $this->valuerTax($this->invoice->custom_value4, $this->invoice->custom_surcharge_taxes4); $this->total_custom_values += $this->valuer($this->invoice->custom_value4); $this->total += $this->total_custom_values; diff --git a/app/Http/Controllers/OpenAPI/CompanySchema.php b/app/Http/Controllers/OpenAPI/CompanySchema.php index 1fd03a7b4bfb..e584dff63259 100644 --- a/app/Http/Controllers/OpenAPI/CompanySchema.php +++ b/app/Http/Controllers/OpenAPI/CompanySchema.php @@ -4,8 +4,15 @@ * schema="Company", * type="object", * @OA\Property(property="id", type="string", example="WJxbojagwO", description="The company hash id"), - * @OA\Property(property="name", type="string", example="The local shop", description="The company name"), + * @OA\Property(property="size_id", type="string", example="1", description="The company size ID"), + * @OA\Property(property="industry_id", type="string", example="1", description="The company industry ID"), + * @OA\Property(property="enabled_tax_rates", type="integer", example="1", description="Number of taxes rates used per entity"), + * @OA\Property(property="fill_products", type="boolean", example=true, description="Toggles filling a product description based on product key"), + * @OA\Property(property="convert_products", type="boolean", example=true, description="___________"), + * @OA\Property(property="update_products", type="boolean", example=true, description="Toggles updating a product description which description changes"), + * @OA\Property(property="custom_surcharge_taxes", type="boolean", example=true, description="Toggles charging taxes on custom surcharge amounts"), * @OA\Property(property="logo", type="object", example="logo.png", description="The company logo - binary"), * @OA\Property(property="settings",ref="#/components/schemas/CompanySettings"), * ) - */ \ No newline at end of file + */ + diff --git a/app/Http/Controllers/OpenAPI/CompanySettingsSchema.php b/app/Http/Controllers/OpenAPI/CompanySettingsSchema.php index eb03ddd55792..4831e3fc6265 100644 --- a/app/Http/Controllers/OpenAPI/CompanySettingsSchema.php +++ b/app/Http/Controllers/OpenAPI/CompanySettingsSchema.php @@ -35,6 +35,8 @@ * @OA\Property(property="custom_message_unapproved_quote", type="string", example="Please approve quote", description="____________"), * @OA\Property(property="lock_sent_invoices", type="boolean", example=true, description="____________"), * @OA\Property(property="auto_archive_invoice", type="boolean", example=true, description="____________"), + * @OA\Property(property="auto_archive_quote", type="boolean", example=true, description="____________"), + * @OA\Property(property="auto_convert_quote", type="boolean", example=true, description="____________"), * @OA\Property(property="inclusive_taxes", type="boolean", example=true, description="____________"), * @OA\Property(property="translations", type="object", example="", description="JSON payload of customized translations"), * @OA\Property(property="task_number_prefix", type="string", example="R", description="This string is prepended to the task number"), @@ -102,9 +104,11 @@ * @OA\Property(property="email_subject_reminder1", type="string", example="", description="Email subject for Reminder"), * @OA\Property(property="email_subject_reminder2", type="string", example="", description="Email subject for Reminder"), * @OA\Property(property="email_subject_reminder3", type="string", example="", description="Email subject for Reminder"), + * @OA\Property(property="email_subject_reminder_endless", type="string", example="", description="Email subject for endless reminders"), * @OA\Property(property="email_template_reminder1", type="string", example="", description="The full template for Reminder 1"), * @OA\Property(property="email_template_reminder2", type="string", example="", description="The full template for Reminder 2"), * @OA\Property(property="email_template_reminder3", type="string", example="", description="The full template for Reminder 3"), + * @OA\Property(property="email_template_reminder_endless", type="string", example="", description="The full template for enless reminders"), * @OA\Property(property="enable_portal_password", type="boolean", example=true, description="Toggles whether a password is required to log into the client portal"), * @OA\Property(property="show_accept_invoice_terms", type="boolean", example=true, description="Toggles whether the terms dialogue is shown to the client"), * @OA\Property(property="show_accept_quote_terms", type="boolean", example=true, description="Toggles whether the terms dialogue is shown to the client"), diff --git a/app/Http/Controllers/OpenAPI/InvoiceSchema.php b/app/Http/Controllers/OpenAPI/InvoiceSchema.php index 9f5da93e85f7..4466d75f6ea0 100644 --- a/app/Http/Controllers/OpenAPI/InvoiceSchema.php +++ b/app/Http/Controllers/OpenAPI/InvoiceSchema.php @@ -39,5 +39,6 @@ * @OA\Property(property="last_viewed", type="number", format="integer", example="1434342123", description="Timestamp"), * @OA\Property(property="updated_at", type="number", format="integer", example="1434342123", description="Timestamp"), * @OA\Property(property="archived_at", type="number", format="integer", example="1434342123", description="Timestamp"), + * @OA\Property(property="custom_surcharge_taxes", type="boolean", example=true, description="Toggles charging taxes on custom surcharge amounts"), * ) */ \ No newline at end of file diff --git a/app/Http/Controllers/OpenAPI/TaxRateSchema.php b/app/Http/Controllers/OpenAPI/TaxRateSchema.php new file mode 100644 index 000000000000..22c137be8430 --- /dev/null +++ b/app/Http/Controllers/OpenAPI/TaxRateSchema.php @@ -0,0 +1,10 @@ +listResponse($tax_rates); + } + + /** + * Show the form for creating a new resource. + * + * @return \Illuminate\Http\Response + * + * + * + * @OA\Get( + * path="/api/v1/tax_rates/create", + * operationId="getTaxRateCreate", + * tags={"tax_rates"}, + * summary="Gets a new blank Tax Rate object", + * description="Returns a blank object with default values", + * @OA\Parameter(ref="#/components/parameters/X-Api-Secret"), + * @OA\Parameter(ref="#/components/parameters/X-Api-Token"), + * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), + * @OA\Response( + * response=200, + * description="A blank Tax Rate object", + * @OA\Header(header="X-API-Version", ref="#/components/headers/X-API-Version"), + * @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"), + * @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"), + * @OA\JsonContent(ref="#/components/schemas/TaxRate"), + * ), + * @OA\Response( + * response=422, + * description="Validation error", + * @OA\JsonContent(ref="#/components/schemas/ValidationError"), + * + * ), + * @OA\Response( + * response="default", + * description="Unexpected Error", + * @OA\JsonContent(ref="#/components/schemas/Error"), + * ), + * ) + * + */ + public function create(CreateTaxRateRequest $request) + { + $tax_rate = TaxRateFactory::create(auth()->user()->company()->id, auth()->user()->id); + return $this->itemResponse($tax_rate); + + } + + /** + * Store a newly created resource in storage. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\Response + */ + public function store(StoreTaxRateRequest $request) + { + $tax_rate = TaxRateFactory::create(auth()->user()->company()->id, auth()->user()->id); + $tax_rate->fill($request->all()); + $tax_rate->save(); + + return $this->itemResponse($tax_rate); + } + + /** + * Display the specified resource. + * + * @param int $id + * @return \Illuminate\Http\Response + * + * + * @OA\Get( + * path="/api/v1/tax_rates/{id}", + * operationId="showTaxRate", + * tags={"tax_rates"}, + * summary="Shows a Tax Rate", + * description="Displays an TaxRate by id", + * @OA\Parameter(ref="#/components/parameters/X-Api-Secret"), + * @OA\Parameter(ref="#/components/parameters/X-Api-Token"), + * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), + * @OA\Parameter( + * name="id", + * in="path", + * description="The TaxRate Hashed ID", + * example="D2J234DFA", + * required=true, + * @OA\Schema( + * type="string", + * format="string", + * ), + * ), + * @OA\Response( + * response=200, + * description="Returns the Tax Rate object", + * @OA\Header(header="X-API-Version", ref="#/components/headers/X-API-Version"), + * @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"), + * @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"), + * @OA\JsonContent(ref="#/components/schemas/TaxRate"), + * ), + * @OA\Response( + * response=422, + * description="Validation error", + * @OA\JsonContent(ref="#/components/schemas/ValidationError"), + * + * ), + * @OA\Response( + * response="default", + * description="Unexpected Error", + * @OA\JsonContent(ref="#/components/schemas/Error"), + * ), + * ) + * + */ + public function show(ShowTaxRateRequest $request, TaxRate $tax_rate) + { + return $this->itemResponse($tax_rate); + } + + /** + * Show the form for editing the specified resource. + * + * @param int $id + * @return \Illuminate\Http\Response + * + * + * @OA\Get( + * path="/api/v1/tax_rates/{id}/edit", + * operationId="editTaxRate", + * tags={"tax_rates"}, + * summary="Shows a Tax Rate for editting", + * description="Displays a Tax Rate by id", + * @OA\Parameter(ref="#/components/parameters/X-Api-Secret"), + * @OA\Parameter(ref="#/components/parameters/X-Api-Token"), + * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), + * @OA\Parameter( + * name="id", + * in="path", + * description="The TaxRate Hashed ID", + * example="D2J234DFA", + * required=true, + * @OA\Schema( + * type="string", + * format="string", + * ), + * ), + * @OA\Response( + * response=200, + * description="Returns the Tax Rate object", + * @OA\Header(header="X-API-Version", ref="#/components/headers/X-API-Version"), + * @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"), + * @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"), + * @OA\JsonContent(ref="#/components/schemas/TaxRate"), + * ), + * @OA\Response( + * response=422, + * description="Validation error", + * @OA\JsonContent(ref="#/components/schemas/ValidationError"), + * + * ), + * @OA\Response( + * response="default", + * description="Unexpected Error", + * @OA\JsonContent(ref="#/components/schemas/Error"), + * ), + * ) + * + */ + public function edit(EditTaxRateRequest $request, TaxRate $tax_rate) + { + return $this->itemResponse($tax_rate); + } + + /** + * Update the specified resource in storage. + * + * @param \Illuminate\Http\Request $request + * @param App\Models\Client $client + * @return \Illuminate\Http\Response + * + * + * + * @OA\Put( + * path="/api/v1/tax_rates/{id}", + * operationId="updateTaxRate", + * tags={"tax_rates"}, + * summary="Updates a tax rate", + * description="Handles the updating of a tax rate by id", + * @OA\Parameter(ref="#/components/parameters/X-Api-Secret"), + * @OA\Parameter(ref="#/components/parameters/X-Api-Token"), + * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), + * @OA\Parameter( + * name="id", + * in="path", + * description="The TaxRate Hashed ID", + * example="D2J234DFA", + * required=true, + * @OA\Schema( + * type="string", + * format="string", + * ), + * ), + * @OA\Response( + * response=200, + * description="Returns the TaxRate object", + * @OA\Header(header="X-API-Version", ref="#/components/headers/X-API-Version"), + * @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"), + * @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"), + * @OA\JsonContent(ref="#/components/schemas/TaxRate"), + * ), + * @OA\Response( + * response=422, + * description="Validation error", + * @OA\JsonContent(ref="#/components/schemas/ValidationError"), + * + * ), + * @OA\Response( + * response="default", + * description="Unexpected Error", + * @OA\JsonContent(ref="#/components/schemas/Error"), + * ), + * ) + * + */ + public function update(UpdateTaxRateRequest $request, TaxRate $tax_rate) + { + $tax_rate->fill($request->all()); + $tax_rate->save(); + + return $this->itemResponse($tax_rate); + + } + + /** + * Remove the specified resource from storage. + * + * @param int $id + * @return \Illuminate\Http\Response + * + * + * @OA\Delete( + * path="/api/v1/tax_rates/{id}", + * operationId="deleteTaxRate", + * tags={"tax_rates"}, + * summary="Deletes a TaxRate", + * description="Handles the deletion of an TaxRate by id", + * @OA\Parameter(ref="#/components/parameters/X-Api-Secret"), + * @OA\Parameter(ref="#/components/parameters/X-Api-Token"), + * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), + * @OA\Parameter( + * name="id", + * in="path", + * description="The TaxRate Hashed ID", + * example="D2J234DFA", + * required=true, + * @OA\Schema( + * type="string", + * format="string", + * ), + * ), + * @OA\Response( + * response=200, + * description="Returns a HTTP status", + * @OA\Header(header="X-API-Version", ref="#/components/headers/X-API-Version"), + * @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"), + * @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"), + * ), + * @OA\Response( + * response=422, + * description="Validation error", + * @OA\JsonContent(ref="#/components/schemas/ValidationError"), + * + * ), + * @OA\Response( + * response="default", + * description="Unexpected Error", + * @OA\JsonContent(ref="#/components/schemas/Error"), + * ), + * ) + * + */ + public function destroy(DestroyTaxRateRequest $request, TaxRate $tax_rate) + { + $tax_rate->delete(); + + return response()->json([], 200); + + } +} diff --git a/app/Http/Requests/TaxRate/CreateTaxRateRequest.php b/app/Http/Requests/TaxRate/CreateTaxRateRequest.php new file mode 100644 index 000000000000..859d8dfd691f --- /dev/null +++ b/app/Http/Requests/TaxRate/CreateTaxRateRequest.php @@ -0,0 +1,31 @@ +user()->isAdmin(); + } + + +} \ No newline at end of file diff --git a/app/Http/Requests/TaxRate/DestroyTaxRateRequest.php b/app/Http/Requests/TaxRate/DestroyTaxRateRequest.php new file mode 100644 index 000000000000..6c5853e2d5f6 --- /dev/null +++ b/app/Http/Requests/TaxRate/DestroyTaxRateRequest.php @@ -0,0 +1,29 @@ +user()->isAdmin(); + } + +} \ No newline at end of file diff --git a/app/Http/Requests/TaxRate/EditTaxRateRequest.php b/app/Http/Requests/TaxRate/EditTaxRateRequest.php new file mode 100644 index 000000000000..cda60db0f870 --- /dev/null +++ b/app/Http/Requests/TaxRate/EditTaxRateRequest.php @@ -0,0 +1,39 @@ +user()->isAdmin(); + } + + /** + * Get the validation rules that apply to the request. + * + * @return array + */ + public function rules() + { + return [ + // + ]; + } +} diff --git a/app/Http/Requests/TaxRate/ShowTaxRateRequest.php b/app/Http/Requests/TaxRate/ShowTaxRateRequest.php new file mode 100644 index 000000000000..2a202556f57a --- /dev/null +++ b/app/Http/Requests/TaxRate/ShowTaxRateRequest.php @@ -0,0 +1,38 @@ +user()->isAdmin(); + } + + /** + * Get the validation rules that apply to the request. + * + * @return array + */ + public function rules() + { + return [ + // + ]; + } +} diff --git a/app/Http/Requests/TaxRate/StoreTaxRateRequest.php b/app/Http/Requests/TaxRate/StoreTaxRateRequest.php new file mode 100644 index 000000000000..a070c0f7f9a2 --- /dev/null +++ b/app/Http/Requests/TaxRate/StoreTaxRateRequest.php @@ -0,0 +1,40 @@ +user()->isAdmin(); + } + + public function rules() + { + return [ + //'name' => 'required', + 'name' => 'required|unique:tax_rates,name,null,null,company_id,'.auth()->user()->companyId(), + 'rate' => 'required|numeric', + ]; + } + + +} \ No newline at end of file diff --git a/app/Http/Requests/TaxRate/UpdateTaxRateRequest.php b/app/Http/Requests/TaxRate/UpdateTaxRateRequest.php new file mode 100644 index 000000000000..920449c2b48e --- /dev/null +++ b/app/Http/Requests/TaxRate/UpdateTaxRateRequest.php @@ -0,0 +1,41 @@ +user()->isAdmin(); + } + + public function rules() + { + return [ + 'name' => 'unique:tax_rates,name,'.$this->tax_rate->name.',id,company_id,'.auth()->user()->companyId(), + 'rate' => 'numeric', + ]; + } + + +} + diff --git a/app/Jobs/Invoice/UpdateInvoicePayment.php b/app/Jobs/Invoice/UpdateInvoicePayment.php index fa10d3a22c37..e2f7b432febc 100644 --- a/app/Jobs/Invoice/UpdateInvoicePayment.php +++ b/app/Jobs/Invoice/UpdateInvoicePayment.php @@ -13,6 +13,7 @@ namespace App\Jobs\Invoice; use App\Jobs\Company\UpdateCompanyLedgerWithInvoice; use App\Jobs\Company\UpdateCompanyLedgerWithPayment; +use App\Jobs\Util\SystemLogger; use App\Models\Payment; use App\Models\SystemLog; use App\Utils\Traits\SystemLogTrait; @@ -104,15 +105,19 @@ class UpdateInvoicePayment implements ShouldQueue } else { -/* - $this->sysLog([ - 'payment' => $this->payment, - 'invoices' => $invoices, - 'invoices_total' => $invoices_total, - 'payment_amount' => $this->payment->amount, - 'partial_check_amount' => $total, - ], SystemLog::GATEWAY_RESPONSE, SystemLog::PAYMENT_RECONCILIATION_FAILURE, $this->payment->client); -*/ + SystemLogger::dispatch( + [ + 'payment' => $this->payment, + 'invoices' => $invoices, + 'invoices_total' => $invoices_total, + 'payment_amount' => $this->payment->amount, + 'partial_check_amount' => $total, ], + SystemLog::CATEGORY_GATEWAY_RESPONSE, + SystemLog::EVENT_PAYMENT_RECONCILIATION_FAILURE, + SystemLog::TYPE_LEDGER, + $this->payment->client + ); + throw new \Exception("payment amount {$this->payment->amount} does not match invoice totals {$invoices_total}"); } diff --git a/app/Jobs/Util/SystemLogger.php b/app/Jobs/Util/SystemLogger.php new file mode 100644 index 000000000000..e8a3fa29068f --- /dev/null +++ b/app/Jobs/Util/SystemLogger.php @@ -0,0 +1,65 @@ +log = $log; + $this->category_id = $category_id; + $this->event_id = $event_id; + $this->type_id = $type_id; + $this->client = $client; + } + + public function handle() :void + { + + $sl = [ + 'client_id' => $this->client->id, + 'company_id' => $this->client->company->id, + 'user_id' => $this->client->user_id, + 'log' => $this->log, + 'category_id' => $this->category_id, + 'event_id' => $this->event_id, + 'type_id' => $this->type_id, + ]; + + SystemLog::create($sl); + } + +} \ No newline at end of file diff --git a/app/Models/SystemLog.php b/app/Models/SystemLog.php index 78c01399b9a8..89b5b7240a07 100644 --- a/app/Models/SystemLog.php +++ b/app/Models/SystemLog.php @@ -16,14 +16,32 @@ use Illuminate\Database\Eloquent\Model; class SystemLog extends Model { /* Category IDs */ - const GATEWAY_RESPONSE = 1; + const CATEGORY_GATEWAY_RESPONSE = 1; /* Event IDs*/ - const PAYMENT_RECONCILIATION_FAILURE = 10; - const PAYMENT_RECONCILIATION_SUCCESS = 11; - - const GATEWAY_SUCCESS = 21; - const GATEWAY_FAILURE = 22; - const GATEWAY_ERROR = 23; + const EVENT_PAYMENT_RECONCILIATION_FAILURE = 10; + const EVENT_PAYMENT_RECONCILIATION_SUCCESS = 11; + const EVENT_GATEWAY_SUCCESS = 21; + const EVENT_GATEWAY_FAILURE = 22; + const EVENT_GATEWAY_ERROR = 23; + + /*Type IDs*/ + const TYPE_PAYPAL = 300; + const TYPE_STRIPE = 301; + const TYPE_LEDGER = 302; + + protected $fillable = [ + 'client_id', + 'company_id', + 'user_id', + 'log', + 'category_id', + 'event_id', + 'type_id', + ]; + + protected $casts = [ + 'log' => 'array' + ]; } diff --git a/app/PaymentDrivers/PayPalExpressPaymentDriver.php b/app/PaymentDrivers/PayPalExpressPaymentDriver.php index a00fd4a26f25..cc2027b55c68 100644 --- a/app/PaymentDrivers/PayPalExpressPaymentDriver.php +++ b/app/PaymentDrivers/PayPalExpressPaymentDriver.php @@ -13,6 +13,7 @@ namespace App\PaymentDrivers; use App\Events\Payment\PaymentWasCreated; use App\Jobs\Invoice\UpdateInvoicePayment; +use App\Jobs\Util\SystemLogger; use App\Models\ClientGatewayToken; use App\Models\GatewayType; use App\Models\Payment; @@ -105,10 +106,15 @@ class PayPalExpressPaymentDriver extends BasePaymentDriver } else { // payment failed: display message to customer - $this->sysLog([ + SystemLogger::dispatch([ 'server_response' => $response->getData(), 'data' => $data - ]); + ], + SystemLog::CATEGORY_GATEWAY_RESPONSE, + SystemLog::EVENT_GATEWAY_FAILURE, + SystemLog::TYPE_PAYPAL, + $this->client + ); throw new \Exception("Error Processing Payment", 1); @@ -127,10 +133,15 @@ class PayPalExpressPaymentDriver extends BasePaymentDriver } elseif (! $response->isSuccessful()) { - $this->sysLog([ - 'request' => $request->all(), + SystemLogger::dispatch([ + 'data' => $request->all(), 'server_response' => $response->getData() - ]); + ], + SystemLog::CATEGORY_GATEWAY_RESPONSE, + SystemLog::EVENT_GATEWAY_FAILURE, + SystemLog::TYPE_PAYPAL, + $this->client + ); throw new \Exception($response->getMessage()); } diff --git a/app/PaymentDrivers/StripePaymentDriver.php b/app/PaymentDrivers/StripePaymentDriver.php index 65c2393c2234..51fe3ea542fe 100644 --- a/app/PaymentDrivers/StripePaymentDriver.php +++ b/app/PaymentDrivers/StripePaymentDriver.php @@ -14,11 +14,13 @@ namespace App\PaymentDrivers; use App\Events\Payment\PaymentWasCreated; use App\Factory\PaymentFactory; use App\Jobs\Invoice\UpdateInvoicePayment; +use App\Jobs\Util\SystemLogger; use App\Models\ClientGatewayToken; use App\Models\GatewayType; use App\Models\Invoice; use App\Models\Payment; use App\Models\PaymentType; +use App\Models\SystemLog; use App\Utils\Traits\MakesHash; use Illuminate\Http\Request; use Illuminate\Support\Carbon; @@ -364,18 +366,32 @@ class StripePaymentDriver extends BasePaymentDriver event(new PaymentWasCreated($payment)); UpdateInvoicePayment::dispatchNow($payment); - + + SystemLogger::dispatch([ + 'server_response' => $payment_intent, + 'data' => $data + ], + SystemLog::CATEGORY_GATEWAY_RESPONSE, + SystemLog::EVENT_GATEWAY_SUCCESS, + SystemLog::TYPE_STRIPE, + $this->client + ); + return redirect()->route('client.payments.show', ['payment' => $this->encodePrimaryKey($payment->id)]); } else { /*Fail and log*/ - - $this->sysLog([ - 'server_response' => $server_response, - 'invoices' => $invoices, - ]); + SystemLogger::dispatch([ + 'server_response' => $server_response, + 'data' => $data + ], + SystemLog::CATEGORY_GATEWAY_RESPONSE, + SystemLog::EVENT_GATEWAY_FAILURE, + SystemLog::TYPE_STRIPE, + $this->client + ); throw new \Exception("Failed to process payment", 1); diff --git a/app/Transformers/CompanyTransformer.php b/app/Transformers/CompanyTransformer.php index bdcd1527f3dd..526728e9ea77 100644 --- a/app/Transformers/CompanyTransformer.php +++ b/app/Transformers/CompanyTransformer.php @@ -68,22 +68,15 @@ class CompanyTransformer extends EntityTransformer { return [ 'id' => (string)$this->encodePrimaryKey($company->id), - // 'name' => (string)$company->name ?: '', - // 'website' => (string)$company->website ?: '', - // 'logo_url' => (string)$company->getLogo(), 'company_key' => (string)$company->company_key ?: '', - // 'address1' => (string)$company->address1 ?: '', - // 'address2' => (string)$company->address2 ?: '', - // 'city' => (string)$company->city ?: '', - // 'state' => (string)$company->state ?: '', - // 'postal_code' => (string)$company->postal_code ?: '', - // 'phone' => (string)$company->phone ?: '', - // 'email' => (string)$company->email ?: '', - // 'country_id' => (string) $company->country_id ?: '', - // 'vat_number' => (string)$company->vat_number ?: '', - // 'id_number' => (string)$company->id_number ?: '', + 'update_products' => (bool)$company->update_products, + 'fill_products' => (bool)$company->fill_products, + 'convert_products' => (bool)$company->convert_products, + 'custom_surcharge_taxes' => (bool)$company->custom_surcharge_taxes, 'size_id' => (string) $company->size_id ?: '', 'industry_id' => (string) $company->industry_id ?: '', + 'first_month_of_year' => (string) $company->first_month_of_year ?: '', + 'first_day_of_week' => (string) $company->first_day_of_week ?: '', 'settings' => $company->settings ?: '', 'updated_at' => (int)$company->updated_at, 'deleted_at' => (int)$company->deleted_at, diff --git a/app/Transformers/InvoiceTransformer.php b/app/Transformers/InvoiceTransformer.php index c6cac02452cd..dc7fb9618296 100644 --- a/app/Transformers/InvoiceTransformer.php +++ b/app/Transformers/InvoiceTransformer.php @@ -120,6 +120,7 @@ class InvoiceTransformer extends EntityTransformer 'custom_surcharge2' => $invoice->custom_surcharge2 ?: '', 'custom_surcharge3' => $invoice->custom_surcharge3 ?: '', 'custom_surcharge4' => $invoice->custom_surcharge4 ?: '', + 'custom_surcharge_taxes' => (bool) $invoice->custom_surcharge_taxes, 'line_items' => $invoice->line_items ?: '', 'backup' => $invoice->backup ?: '', 'settings' => $invoice->settings ?: '', diff --git a/app/Transformers/TaxRateTransformer.php b/app/Transformers/TaxRateTransformer.php new file mode 100644 index 000000000000..330ca685c88d --- /dev/null +++ b/app/Transformers/TaxRateTransformer.php @@ -0,0 +1,25 @@ + (string) $this->encodePrimaryKey($tax_rate->id), + 'name' => (string) $tax_rate->name, + 'rate' => (float) $tax_rate->rate, + 'updated_at' => $tax_rate->updated_at, + 'deleted_at' => $tax_rate->deleted_at, + ]; + } +} diff --git a/config/ninja.php b/config/ninja.php index b4c51725b9c3..ef1496e60672 100644 --- a/config/ninja.php +++ b/config/ninja.php @@ -49,7 +49,7 @@ return [ 'payment_terms' => env('DEFAULT_PAYMENT_TERMS', 1), 'military_time' => env('MILITARY_TIME', 0), 'first_day_of_week' => env('FIRST_DATE_OF_WEEK',0), - 'financial_year_start' => env('FINANCIAL_YEAR_START', '2000-01-01') + 'first_month_of_year' => env('FIRST_MONTH_OF_YEAR', '2000-01-01') ], 'testvars' => [ 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 b32be6acea9b..6f92b7a2481a 100644 --- a/database/migrations/2014_10_13_000000_create_users_table.php +++ b/database/migrations/2014_10_13_000000_create_users_table.php @@ -141,6 +141,15 @@ class CreateUsersTable extends Migration $table->string('ip')->nullable(); $table->string('company_key',100)->unique(); $table->string('logo')->nullable(); + $table->boolean('convert_products')->default(false); + $table->boolean('fill_products')->default(false); + $table->boolean('update_products')->default(false); + $table->boolean('custom_surcharge_taxes1')->default(false); + $table->boolean('custom_surcharge_taxes2')->default(false); + $table->boolean('custom_surcharge_taxes3')->default(false); + $table->boolean('custom_surcharge_taxes4')->default(false); + $table->unsignedInteger('enabled_tax_rates')->default(1); + // $table->string('website')->nullable(); // $table->string('address1')->nullable(); // $table->string('address2')->nullable(); @@ -156,7 +165,7 @@ class CreateUsersTable extends Migration // $table->string('id_number')->nullable(); $table->unsignedInteger('size_id')->nullable(); $table->string('first_day_of_week')->nullable(); - $table->string('financial_year_start')->nullable(); + $table->string('first_month_of_year')->nullable(); $table->smallInteger('enable_modules')->default(0); $table->text('custom_fields'); $table->text('settings'); @@ -276,8 +285,8 @@ class CreateUsersTable extends Migration $table->string('logo', 255)->nullable(); $table->string('phone', 255)->nullable(); - $table->decimal('balance', 13, 2)->default(0); - $table->decimal('paid_to_date', 13, 2)->default(0); + $table->decimal('balance', 16, 4)->default(0); + $table->decimal('paid_to_date', 16, 4)->default(0); $table->timestamp('last_login')->nullable(); $table->unsignedInteger('industry_id')->nullable(); $table->unsignedInteger('size_id')->nullable(); @@ -372,16 +381,16 @@ class CreateUsersTable extends Migration $table->text('config'); $table->unsignedInteger('priority')->default(0); - $table->decimal('min_limit', 13, 2)->nullable(); - $table->decimal('max_limit', 13, 2)->nullable(); - $table->decimal('fee_amount', 13, 2)->nullable(); - $table->decimal('fee_percent', 13, 2)->nullable(); + $table->decimal('min_limit', 16, 4)->nullable(); + $table->decimal('max_limit', 16, 4)->nullable(); + $table->decimal('fee_amount', 16, 4)->nullable(); + $table->decimal('fee_percent', 16, 4)->nullable(); $table->string('fee_tax_name1')->nullable(); $table->string('fee_tax_name2')->nullable(); $table->string('fee_tax_name3')->nullable(); - $table->decimal('fee_tax_rate1', 13, 2)->nullable(); - $table->decimal('fee_tax_rate2', 13, 2)->nullable(); - $table->decimal('fee_tax_rate3', 13, 2)->nullable(); + $table->decimal('fee_tax_rate1', 16, 4)->nullable(); + $table->decimal('fee_tax_rate2', 16, 4)->nullable(); + $table->decimal('fee_tax_rate3', 16, 4)->nullable(); $table->unsignedInteger('fee_cap')->nullable(); $table->boolean('adjust_fee_percent')->default(false); @@ -443,10 +452,11 @@ class CreateUsersTable extends Migration $t->string('custom_surcharge2')->nullable(); $t->string('custom_surcharge3')->nullable(); $t->string('custom_surcharge4')->nullable(); + $t->boolean('custom_surcharge_taxes')->default(false); - $t->decimal('amount', 13, 2); - $t->decimal('balance', 13, 2); - $t->decimal('partial', 13, 2)->nullable(); + $t->decimal('amount', 16, 4); + $t->decimal('balance', 16, 4); + $t->decimal('partial', 16, 4)->nullable(); $t->datetime('partial_due_date')->nullable(); $t->datetime('last_viewed')->nullable(); @@ -503,9 +513,9 @@ class CreateUsersTable extends Migration $t->string('custom_value3')->nullable(); $t->string('custom_value4')->nullable(); - $t->decimal('amount', 13, 2); - $t->decimal('balance', 13, 2); - $t->decimal('partial', 13, 2)->nullable(); + $t->decimal('amount', 16, 4); + $t->decimal('balance', 16, 4); + $t->decimal('partial', 16, 4)->nullable(); $t->datetime('last_viewed')->nullable(); @@ -565,8 +575,8 @@ class CreateUsersTable extends Migration $t->string('custom_value3')->nullable(); $t->string('custom_value4')->nullable(); - $t->decimal('amount', 13, 2)->default(0); - $t->decimal('balance', 13, 2)->default(0); + $t->decimal('amount', 16, 4)->default(0); + $t->decimal('balance', 16, 4)->default(0); $t->datetime('last_viewed')->nullable(); @@ -627,9 +637,9 @@ class CreateUsersTable extends Migration $t->string('custom_value3')->nullable(); $t->string('custom_value4')->nullable(); - $t->decimal('amount', 13, 2)->default(0); - $t->decimal('balance', 13, 2)->default(0); - $t->decimal('partial', 13, 2)->nullable(); + $t->decimal('amount', 16, 4)->default(0); + $t->decimal('balance', 16, 4)->default(0); + $t->decimal('partial', 16, 4)->nullable(); $t->datetime('partial_due_date')->nullable(); $t->datetime('last_viewed')->nullable(); @@ -682,7 +692,7 @@ class CreateUsersTable extends Migration $t->timestamps(6); $t->softDeletes(); - $t->string('name',100)->unique(); + $t->string('name',100); $t->decimal('rate', 13, 3)->default(0); $t->foreign('company_id')->references('id')->on('companies')->onDelete('cascade'); @@ -703,9 +713,9 @@ class CreateUsersTable extends Migration $t->string('product_key')->nullable(); $t->text('notes')->nullable(); - $t->decimal('cost', 13, 2)->default(0); - $t->decimal('price', 13, 2)->default(0); - $t->decimal('quantity', 13, 2)->default(0); + $t->decimal('cost', 16, 4)->default(0); + $t->decimal('price', 16, 4)->default(0); + $t->decimal('quantity', 16, 4)->default(0); $t->string('tax_name1')->nullable(); $t->decimal('tax_rate1', 13, 3)->default(0); @@ -735,7 +745,7 @@ class CreateUsersTable extends Migration $t->unsignedInteger('company_gateway_id')->nullable(); $t->unsignedInteger('payment_type_id')->nullable(); $t->unsignedInteger('status_id')->index(); - $t->decimal('amount', 13, 2)->default(0); + $t->decimal('amount', 16, 4)->default(0); $t->datetime('payment_date')->nullable(); $t->string('transaction_reference')->nullable(); $t->string('payer_id')->nullable(); @@ -900,8 +910,8 @@ class CreateUsersTable extends Migration $table->unsignedInteger('client_id')->nullable(); $table->unsignedInteger('user_id')->nullable(); - $table->decimal('adjustment', 13, 2)->nullable(); - $table->decimal('balance', 13, 2)->nullable(); //this is the clients balance carried forward + $table->decimal('adjustment', 16, 4)->nullable(); + $table->decimal('balance', 16, 4)->nullable(); //this is the clients balance carried forward $table->text('notes')->nullable(); $table->text('hash')->nullable(); @@ -962,13 +972,14 @@ class CreateUsersTable extends Migration $table->string('format_dart'); }); - Schema::create('system_log', function ($table){ + Schema::create('system_logs', function ($table){ $table->increments('id'); $table->unsignedInteger('company_id'); $table->unsignedInteger('user_id')->nullable(); $table->unsignedInteger('client_id')->nullable(); $table->unsignedInteger('category_id')->nullable(); $table->unsignedInteger('event_id')->nullable(); + $table->unsignedInteger('type_id')->nullable(); $table->text('log'); $table->timestamps(6); diff --git a/routes/api.php b/routes/api.php index bd8132c4f4be..28dc956d22c7 100644 --- a/routes/api.php +++ b/routes/api.php @@ -79,6 +79,8 @@ Route::group(['middleware' => ['api_db','api_secret_check','token_auth'], 'prefi Route::resource('group_settings', 'GroupSettingController'); + Route::resource('tax_rates', 'TaxRateController'); // name = (tasks. index / create / show / update / destroy / edit + Route::post('refresh', 'Auth\LoginController@refresh'); /* Route::resource('tasks', 'TaskController'); // name = (tasks. index / create / show / update / destroy / edit diff --git a/tests/Integration/MarkInvoicePaidTest.php b/tests/Integration/MarkInvoicePaidTest.php index 26e564caed7e..bbe7bc5018f3 100644 --- a/tests/Integration/MarkInvoicePaidTest.php +++ b/tests/Integration/MarkInvoicePaidTest.php @@ -46,7 +46,7 @@ class MarkInvoicePaidTest extends TestCase $this->assertEquals(1, count($invoice->payments)); foreach($invoice->payments as $payment) { - $this->assertEquals($this->invoice->amount, $payment->amount); + $this->assertEquals(round($this->invoice->amount,2), $payment->amount); } //events are not firing which makes this impossible to control. diff --git a/tests/Unit/CompanySettingsTest.php b/tests/Unit/CompanySettingsTest.php index 7ff580c741b6..74f87859ac37 100644 --- a/tests/Unit/CompanySettingsTest.php +++ b/tests/Unit/CompanySettingsTest.php @@ -55,7 +55,7 @@ class CompanySettingsTest extends TestCase $casts = CompanySettings::$casts; $diff = array_diff_key($company_settings, $casts); - +\Log::error($diff); $this->assertEquals(1, count($diff)); } diff --git a/tests/Unit/GeneratesCounterTest.php b/tests/Unit/GeneratesCounterTest.php index 620c7be10f03..d81ef7f8bda8 100644 --- a/tests/Unit/GeneratesCounterTest.php +++ b/tests/Unit/GeneratesCounterTest.php @@ -112,8 +112,8 @@ class GeneratesCounterTest extends TestCase $invoice_number = $this->getNextInvoiceNumber($this->client); $invoice_number2 = $this->getNextInvoiceNumber($this->client); - $this->assertEquals($invoice_number, '2019-1'); - $this->assertEquals($invoice_number2, '2019-2'); + $this->assertEquals($invoice_number, '2019-0001'); + $this->assertEquals($invoice_number2, '2019-0002'); $this->assertEquals($this->client->company->settings->invoice_number_counter,3); } @@ -136,10 +136,10 @@ class GeneratesCounterTest extends TestCase $invoice_number = $this->getNextInvoiceNumber($this->client); - $this->assertEquals($invoice_number, '2019-10'); + $this->assertEquals($invoice_number, '2019-0010'); $invoice_number = $this->getNextInvoiceNumber($this->client); - $this->assertEquals($invoice_number, '2019-11'); + $this->assertEquals($invoice_number, '2019-0011'); } @@ -181,11 +181,11 @@ class GeneratesCounterTest extends TestCase $invoice_number = $this->getNextInvoiceNumber($this->client); - $this->assertEquals($invoice_number, 'X1'); + $this->assertEquals($invoice_number, 'X0001'); $invoice_number = $this->getNextInvoiceNumber($this->client); - $this->assertEquals($invoice_number, 'X2'); + $this->assertEquals($invoice_number, 'X0002'); } @@ -194,11 +194,11 @@ class GeneratesCounterTest extends TestCase { $client_number = $this->getNextClientNumber($this->client); - $this->assertEquals($client_number, '1'); + $this->assertEquals($client_number, '0001'); $client_number = $this->getNextClientNumber($this->client); - $this->assertEquals($client_number, '2'); + $this->assertEquals($client_number, '0002'); } @@ -212,11 +212,11 @@ class GeneratesCounterTest extends TestCase $client_number = $this->getNextClientNumber($this->client); - $this->assertEquals($client_number, 'C1'); + $this->assertEquals($client_number, 'C0001'); $client_number = $this->getNextClientNumber($this->client); - $this->assertEquals($client_number, 'C2'); + $this->assertEquals($client_number, 'C0002'); } @@ -233,11 +233,11 @@ class GeneratesCounterTest extends TestCase $client_number = $this->getNextClientNumber($this->client); - $this->assertEquals($client_number, date('Y') . '-' . $this->client->user_id . '-1'); + $this->assertEquals($client_number, date('Y') . '-' . $this->client->user_id . '-0001'); $client_number = $this->getNextClientNumber($this->client); - $this->assertEquals($client_number, date('Y') . '-' . $this->client->user_id . '-2'); + $this->assertEquals($client_number, date('Y') . '-' . $this->client->user_id . '-0002'); } /*