* Insert generic for client country if not set

* Invoice fixes

* fixes

* Schema changes

* Refactor Schema and implement fixes for testS

* Use Dispatcher for system logs

* Add TaxRateController

* Update OpenAPI definitions for Tax Rates
This commit is contained in:
David Bomba 2019-10-29 13:55:26 +11:00 committed by GitHub
parent 0f9aae454b
commit 5fafbac36f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 888 additions and 106 deletions

View File

@ -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',

View File

@ -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;

View File

@ -0,0 +1,31 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com)
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2019. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\Factory;
use App\Models\TaxRate;
class TaxRateFactory
{
public static function create($company_id, $user_id) :TaxRate
{
$tax_rate = new TaxRate;
$tax_rate->name = '';
$tax_rate->rate = '';
$tax_rate->company_id = $company_id;
$tax_rate->user_id = $user_id;
return $tax_rate;
}
}

View File

@ -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;

View File

@ -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;

View File

@ -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"),
* )
*/
*/

View File

@ -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="<HTML></HTML>", description="Email subject for Reminder"),
* @OA\Property(property="email_subject_reminder2", type="string", example="<HTML></HTML>", description="Email subject for Reminder"),
* @OA\Property(property="email_subject_reminder3", type="string", example="<HTML></HTML>", description="Email subject for Reminder"),
* @OA\Property(property="email_subject_reminder_endless", type="string", example="<HTML></HTML>", description="Email subject for endless reminders"),
* @OA\Property(property="email_template_reminder1", type="string", example="<HTML></HTML>", description="The full template for Reminder 1"),
* @OA\Property(property="email_template_reminder2", type="string", example="<HTML></HTML>", description="The full template for Reminder 2"),
* @OA\Property(property="email_template_reminder3", type="string", example="<HTML></HTML>", description="The full template for Reminder 3"),
* @OA\Property(property="email_template_reminder_endless", type="string", example="<HTML></HTML>", 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"),

View File

@ -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"),
* )
*/

View File

@ -0,0 +1,10 @@
<?php
/**
* @OA\Schema(
* schema="TaxRate",
* type="object",
* @OA\Property(property="id", type="string", example="Opnel5aKBz", description="______"),
* @OA\Property(property="name", type="string", example="GST", description="______"),
* @OA\Property(property="rate", type="number", example="10", description="______"),
* )
*/

View File

@ -0,0 +1,366 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com)
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2019. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\Http\Controllers;
use App\Factory\TaxRateFactory;
use App\Http\Requests\TaxRate\DestroyTaxRateRequest;
use App\Http\Requests\TaxRate\EditTaxRateRequest;
use App\Http\Requests\TaxRate\ShowTaxRateRequest;
use App\Http\Requests\TaxRate\StoreTaxRateRequest;
use App\Http\Requests\TaxRate\UpdateTaxRateRequest;
use App\Http\Requests\TaxRate\CreateTaxRateRequest;
use App\Models\TaxRate;
use App\Transformers\TaxRateTransformer;
use Illuminate\Http\Request;
/**
* Class TaxRateController
* @package App\Http\Controllers
*/
class TaxRateController extends BaseController
{
protected $entity_type = TaxRate::class;
protected $entity_transformer = TaxRateTransformer::class;
public function __construct()
{
parent::__construct();
}
/**
* @OA\Get(
* path="/api/v1/tax_rates",
* operationId="getTaxRates",
* tags={"tax_rates"},
* summary="Gets a list of tax_rates",
* description="Lists tax rates",
* @OA\Parameter(ref="#/components/parameters/index"),
* @OA\Response(
* response=200,
* description="A list of tax_rates",
* @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"),
* ),
* )
*
*
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
$tax_rates = TaxRate::all();
return $this->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);
}
}

View File

@ -0,0 +1,31 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com)
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2019. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\Http\Requests\TaxRate;
use App\Http\Requests\Request;
use App\Models\TaxRate;
class CreateTaxRateRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize() : bool
{
return auth()->user()->isAdmin();
}
}

View File

@ -0,0 +1,29 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com)
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2019. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\Http\Requests\TaxRate;
use App\Http\Requests\Request;
class DestroyTaxRateRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize() : bool
{
return auth()->user()->isAdmin();
}
}

View File

@ -0,0 +1,39 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com)
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2019. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\Http\Requests\TaxRate;
use App\Http\Requests\Request;
class EditTaxRateRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize() : bool
{
return auth()->user()->isAdmin();
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
//
];
}
}

View File

@ -0,0 +1,38 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com)
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2019. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\Http\Requests\TaxRate;
use App\Http\Requests\Request;
class ShowTaxRateRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize() : bool
{
return auth()->user()->isAdmin();
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
//
];
}
}

View File

@ -0,0 +1,40 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com)
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2019. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\Http\Requests\TaxRate;
use App\Http\Requests\Request;
use App\Models\TaxRate;
class StoreTaxRateRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize() : bool
{
return auth()->user()->isAdmin();
}
public function rules()
{
return [
//'name' => 'required',
'name' => 'required|unique:tax_rates,name,null,null,company_id,'.auth()->user()->companyId(),
'rate' => 'required|numeric',
];
}
}

View File

@ -0,0 +1,41 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com)
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2019. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\Http\Requests\TaxRate;
use App\Http\Requests\Request;
use App\Models\TaxRate;
use Illuminate\Support\Facades\Log;
class UpdateTaxRateRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize() : bool
{
return auth()->user()->isAdmin();
}
public function rules()
{
return [
'name' => 'unique:tax_rates,name,'.$this->tax_rate->name.',id,company_id,'.auth()->user()->companyId(),
'rate' => 'numeric',
];
}
}

View File

@ -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}");
}

View File

@ -0,0 +1,65 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com)
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2019. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\Jobs\Util;
use App\Models\Client;
use App\Models\SystemLog;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Http\File;
use Illuminate\Http\Request;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Storage;
use Intervention\Image\ImageManager;
class SystemLogger implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
protected $log;
protected $category_id;
protected $event_id;
protected $type_id;
protected $client;
public function __construct($log, $category_id, $event_id, $type_id, Client $client)
{
$this->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);
}
}

View File

@ -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'
];
}

View File

@ -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());
}

View File

@ -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);

View File

@ -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,

View File

@ -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 ?: '',

View File

@ -0,0 +1,25 @@
<?php
namespace App\Transformers;
use App\Models\TaxRate;
use App\Utils\Traits\MakesHash;
/**
* @SWG\Definition(definition="TaxRate", @SWG\Xml(name="TaxRate"))
*/
class TaxRateTransformer extends EntityTransformer
{
use MakesHash;
public function transform(TaxRate $tax_rate)
{
return [
'id' => (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,
];
}
}

View File

@ -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' => [

View File

@ -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);

View File

@ -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

View File

@ -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.

View File

@ -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));
}

View File

@ -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');
}
/*