mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-07 17:44:29 -04:00
commit
4b5fb94474
@ -1 +1 @@
|
||||
5.3.10
|
||||
5.3.12
|
@ -654,6 +654,7 @@ class CompanySettings extends BaseSettings
|
||||
'$product.discount',
|
||||
'$product.tax',
|
||||
'$product.line_total',
|
||||
'$product.gross_line_total',
|
||||
],
|
||||
'task_columns' =>[
|
||||
'$task.service',
|
||||
@ -663,9 +664,11 @@ class CompanySettings extends BaseSettings
|
||||
'$task.discount',
|
||||
'$task.tax',
|
||||
'$task.line_total',
|
||||
'$task.gross_line_total',
|
||||
],
|
||||
'total_columns' => [
|
||||
'$net_subtotal',
|
||||
'$gross_subtotal',
|
||||
'$subtotal',
|
||||
'$discount',
|
||||
'$custom_surcharge1',
|
||||
|
@ -43,6 +43,8 @@ class InvoiceItem
|
||||
|
||||
public $line_total = 0;
|
||||
|
||||
public $gross_line_total = 0;
|
||||
|
||||
public $date = '';
|
||||
|
||||
public $custom_value1 = '';
|
||||
@ -72,6 +74,7 @@ class InvoiceItem
|
||||
'tax_rate3' => 'float',
|
||||
'sort_id' => 'string',
|
||||
'line_total' => 'float',
|
||||
'gross_line_total' => 'float',
|
||||
'date' => 'string',
|
||||
'custom_value1' => 'string',
|
||||
'custom_value2' => 'string',
|
||||
|
@ -35,6 +35,7 @@ class InvoiceItemFactory
|
||||
$item->tax_rate3 = 0;
|
||||
$item->sort_id = 0;
|
||||
$item->line_total = 0;
|
||||
$item->gross_line_total = 0;
|
||||
$item->custom_value1 = '';
|
||||
$item->custom_value2 = '';
|
||||
$item->custom_value3 = '';
|
||||
|
@ -28,6 +28,8 @@ class InvoiceItemSum
|
||||
|
||||
private $line_total;
|
||||
|
||||
private $gross_line_total;
|
||||
|
||||
private $currency;
|
||||
|
||||
private $total_taxes;
|
||||
@ -38,6 +40,8 @@ class InvoiceItemSum
|
||||
|
||||
private $sub_total;
|
||||
|
||||
private $gross_sub_total;
|
||||
|
||||
private $total_discount;
|
||||
|
||||
private $tax_collection;
|
||||
@ -83,6 +87,8 @@ class InvoiceItemSum
|
||||
{
|
||||
$this->sub_total += $this->getLineTotal();
|
||||
|
||||
$this->gross_sub_total += $this->getGrossLineTotal();
|
||||
|
||||
$this->line_items[] = $this->item;
|
||||
|
||||
return $this;
|
||||
@ -144,10 +150,11 @@ class InvoiceItemSum
|
||||
|
||||
if($item_tax_rate3_total != 0)
|
||||
$this->groupTax($this->item->tax_name3, $this->item->tax_rate3, $item_tax_rate3_total);
|
||||
|
||||
|
||||
$this->setTotalTaxes($this->formatValue($item_tax, $this->currency->precision));
|
||||
|
||||
$this->item->gross_line_total = $this->getLineTotal() + $item_tax;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@ -186,6 +193,11 @@ class InvoiceItemSum
|
||||
return $this->item->line_total;
|
||||
}
|
||||
|
||||
public function getGrossLineTotal()
|
||||
{
|
||||
return $this->item->gross_line_total;
|
||||
}
|
||||
|
||||
public function getLineItems()
|
||||
{
|
||||
return $this->line_items;
|
||||
@ -208,6 +220,11 @@ class InvoiceItemSum
|
||||
return $this->sub_total;
|
||||
}
|
||||
|
||||
public function getGrossSubTotal()
|
||||
{
|
||||
return $this->gross_sub_total;
|
||||
}
|
||||
|
||||
public function setSubTotal($value)
|
||||
{
|
||||
$this->sub_total = $value;
|
||||
@ -263,6 +280,7 @@ class InvoiceItemSum
|
||||
if ($item_tax_rate3_total != 0) {
|
||||
$this->groupTax($this->item->tax_name3, $this->item->tax_rate3, $item_tax_rate3_total);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$this->setTotalTaxes($item_tax);
|
||||
|
@ -177,6 +177,11 @@ class InvoiceItemSumInclusive
|
||||
return $this->item->line_total;
|
||||
}
|
||||
|
||||
public function getGrossLineTotal()
|
||||
{
|
||||
return $this->item->line_total;
|
||||
}
|
||||
|
||||
public function getLineItems()
|
||||
{
|
||||
return $this->line_items;
|
||||
@ -199,6 +204,11 @@ class InvoiceItemSumInclusive
|
||||
return $this->sub_total;
|
||||
}
|
||||
|
||||
public function getGrossSubTotal()
|
||||
{
|
||||
return $this->sub_total;
|
||||
}
|
||||
|
||||
public function setSubTotal($value)
|
||||
{
|
||||
$this->sub_total = $value;
|
||||
|
@ -42,6 +42,8 @@ class InvoiceSum
|
||||
|
||||
private $sub_total;
|
||||
|
||||
private $gross_sub_total;
|
||||
|
||||
/**
|
||||
* Constructs the object with Invoice and Settings object.
|
||||
*
|
||||
@ -75,7 +77,8 @@ class InvoiceSum
|
||||
$this->invoice->line_items = $this->invoice_items->getLineItems();
|
||||
$this->total = $this->invoice_items->getSubTotal();
|
||||
$this->setSubTotal($this->invoice_items->getSubTotal());
|
||||
|
||||
$this->setGrossSubTotal($this->invoice_items->getGrossSubTotal());
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@ -266,6 +269,18 @@ class InvoiceSum
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getGrossSubTotal()
|
||||
{
|
||||
return $this->gross_sub_total;
|
||||
}
|
||||
|
||||
public function setGrossSubTotal($value)
|
||||
{
|
||||
$this->gross_sub_total = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getTotalDiscount()
|
||||
{
|
||||
return $this->total_discount;
|
||||
|
@ -259,6 +259,11 @@ class InvoiceSumInclusive
|
||||
return $this->sub_total;
|
||||
}
|
||||
|
||||
public function getGrossSubTotal()
|
||||
{
|
||||
return $this->sub_total;
|
||||
}
|
||||
|
||||
public function setSubTotal($value)
|
||||
{
|
||||
$this->sub_total = $value;
|
||||
|
@ -738,6 +738,7 @@ class BaseController extends Controller
|
||||
//pass referral code to front end
|
||||
$data['rc'] = request()->has('rc') ? request()->input('rc') : '';
|
||||
$data['build'] = request()->has('build') ? request()->input('build') : '';
|
||||
$data['user_agent'] = request()->server('HTTP_USER_AGENT');
|
||||
|
||||
$data['path'] = $this->setBuild();
|
||||
|
||||
|
@ -512,6 +512,9 @@ class ClientController extends BaseController
|
||||
$ids = request()->input('ids');
|
||||
$clients = Client::withTrashed()->whereIn('id', $this->transformKeys($ids))->cursor();
|
||||
|
||||
if(!in_array($action, ['restore','archive','delete']))
|
||||
return response()->json(['message' => 'That action is not available.'], 400);
|
||||
|
||||
$clients->each(function ($client, $key) use ($action) {
|
||||
if (auth()->user()->can('edit', $client)) {
|
||||
$this->client_repo->{$action}($client);
|
||||
|
@ -210,7 +210,10 @@ class QuoteController extends BaseController
|
||||
|
||||
$quote = $this->quote_repo->save($request->all(), QuoteFactory::create(auth()->user()->company()->id, auth()->user()->id));
|
||||
|
||||
$quote = $quote->service()->fillDefaults()->save();
|
||||
$quote = $quote->service()
|
||||
->fillDefaults()
|
||||
->triggeredActions($request)
|
||||
->save();
|
||||
|
||||
event(new QuoteWasCreated($quote, $quote->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
||||
|
||||
|
@ -75,7 +75,6 @@ class Client extends BaseModel implements HasLocalePreference
|
||||
'shipping_postal_code',
|
||||
'shipping_country_id',
|
||||
'settings',
|
||||
'payment_terms',
|
||||
'vat_number',
|
||||
'id_number',
|
||||
'group_settings_id',
|
||||
|
@ -42,6 +42,7 @@ class SOFORT
|
||||
$data['return_url'] = $this->buildReturnUrl();
|
||||
$data['stripe_amount'] = $this->stripe->convertToStripeAmount($data['total']['amount_with_fee'], $this->stripe->client->currency()->precision, $this->stripe->client->currency());
|
||||
$data['client'] = $this->stripe->client;
|
||||
$data['customer'] = $this->stripe->findOrCreateCustomer()->id;
|
||||
$data['country'] = $this->stripe->client->country->iso_3166_2;
|
||||
|
||||
$this->stripe->payment_hash->data = array_merge((array) $this->stripe->payment_hash->data, ['stripe_amount' => $data['stripe_amount']]);
|
||||
|
@ -16,6 +16,7 @@ use App\Jobs\Util\UnlinkFile;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Quote;
|
||||
use App\Repositories\QuoteRepository;
|
||||
use App\Services\Quote\TriggeredActions;
|
||||
use App\Utils\Ninja;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
|
||||
@ -177,6 +178,13 @@ class QuoteService
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function triggeredActions($request)
|
||||
{
|
||||
$this->quote = (new TriggeredActions($this->quote, $request))->run();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function deletePdf()
|
||||
{
|
||||
$this->quote->invitations->each(function ($invitation){
|
||||
|
66
app/Services/Quote/TriggeredActions.php
Normal file
66
app/Services/Quote/TriggeredActions.php
Normal file
@ -0,0 +1,66 @@
|
||||
<?php
|
||||
/**
|
||||
* Quote Ninja (https://quoteninja.com).
|
||||
*
|
||||
* @link https://github.com/quoteninja/quoteninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Quote Ninja LLC (https://quoteninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Services\Quote;
|
||||
|
||||
use App\Events\Quote\QuoteWasEmailed;
|
||||
use App\Jobs\Entity\EmailEntity;
|
||||
use App\Models\Quote;
|
||||
use App\Services\AbstractService;
|
||||
use App\Utils\Ninja;
|
||||
use App\Utils\Traits\GeneratesCounter;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class TriggeredActions extends AbstractService
|
||||
{
|
||||
use GeneratesCounter;
|
||||
|
||||
private $request;
|
||||
|
||||
private $quote;
|
||||
|
||||
public function __construct(Quote $quote, Request $request)
|
||||
{
|
||||
$this->request = $request;
|
||||
|
||||
$this->quote = $quote;
|
||||
}
|
||||
|
||||
public function run()
|
||||
{
|
||||
|
||||
if ($this->request->has('send_email') && $this->request->input('send_email') == 'true') {
|
||||
$this->sendEmail();
|
||||
}
|
||||
|
||||
if ($this->request->has('mark_sent') && $this->request->input('mark_sent') == 'true') {
|
||||
$this->quote = $this->quote->service()->markSent()->save();
|
||||
}
|
||||
|
||||
|
||||
return $this->quote;
|
||||
}
|
||||
|
||||
private function sendEmail()
|
||||
{
|
||||
|
||||
$reminder_template = $this->quote->calculateTemplate('quote');
|
||||
//$reminder_template = 'payment';
|
||||
|
||||
$this->quote->invitations->load('contact.client.country', 'quote.client.country', 'quote.company')->each(function ($invitation) use ($reminder_template) {
|
||||
EmailEntity::dispatch($invitation, $this->quote->company, $reminder_template);
|
||||
});
|
||||
|
||||
if ($this->quote->invitations->count() > 0) {
|
||||
event(new QuoteWasEmailed($this->quote->invitations->first(), $this->quote->company, Ninja::eventVars(), 'quote'));
|
||||
}
|
||||
}
|
||||
}
|
@ -175,6 +175,7 @@ class HtmlEngine
|
||||
$data['$invoice.discount'] = ['value' => Number::formatMoney($this->entity_calc->getTotalDiscount(), $this->client) ?: ' ', 'label' => ctrans('texts.discount')];
|
||||
$data['$discount'] = &$data['$invoice.discount'];
|
||||
$data['$subtotal'] = ['value' => Number::formatMoney($this->entity_calc->getSubTotal(), $this->client) ?: ' ', 'label' => ctrans('texts.subtotal')];
|
||||
$data['$gross_subtotal'] = ['value' => Number::formatMoney($this->entity_calc->getGrossSubTotal(), $this->client) ?: ' ', 'label' => ctrans('texts.subtotal')];
|
||||
|
||||
if($this->entity->uses_inclusive_taxes)
|
||||
$data['$net_subtotal'] = ['value' => Number::formatMoney(($this->entity_calc->getSubTotal() - $this->entity->total_taxes), $this->client) ?: ' ', 'label' => ctrans('texts.net_subtotal')];
|
||||
@ -380,6 +381,7 @@ class HtmlEngine
|
||||
$data['$product.tax_name2'] = ['value' => '', 'label' => ctrans('texts.tax')];
|
||||
$data['$product.tax_name3'] = ['value' => '', 'label' => ctrans('texts.tax')];
|
||||
$data['$product.line_total'] = ['value' => '', 'label' => ctrans('texts.line_total')];
|
||||
$data['$product.gross_line_total'] = ['value' => '', 'label' => ctrans('texts.line_total')];
|
||||
$data['$product.description'] = ['value' => '', 'label' => ctrans('texts.description')];
|
||||
$data['$product.unit_cost'] = ['value' => '', 'label' => ctrans('texts.unit_cost')];
|
||||
$data['$product.product1'] = ['value' => '', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'product1')];
|
||||
@ -399,6 +401,7 @@ class HtmlEngine
|
||||
$data['$task.tax_name2'] = ['value' => '', 'label' => ctrans('texts.tax')];
|
||||
$data['$task.tax_name3'] = ['value' => '', 'label' => ctrans('texts.tax')];
|
||||
$data['$task.line_total'] = ['value' => '', 'label' => ctrans('texts.line_total')];
|
||||
$data['$task.gross_line_total'] = ['value' => '', 'label' => ctrans('texts.line_total')];
|
||||
$data['$task.service'] = ['value' => '', 'label' => ctrans('texts.service')];
|
||||
$data['$task.task1'] = ['value' => '', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'task1')];
|
||||
$data['$task.task2'] = ['value' => '', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'task2')];
|
||||
|
@ -311,6 +311,7 @@ trait MakesInvoiceValues
|
||||
$data[$key][$table_type.'.cost'] = Number::formatMoney($item->cost, $this->client);
|
||||
|
||||
$data[$key][$table_type.'.line_total'] = Number::formatMoney($item->line_total, $this->client);
|
||||
$data[$key][$table_type.'.gross_line_total'] = Number::formatMoney($item->gross_line_total, $this->client);
|
||||
|
||||
if (isset($item->discount) && $item->discount > 0) {
|
||||
if ($item->is_amount_discount) {
|
||||
|
@ -14,8 +14,8 @@ return [
|
||||
'require_https' => env('REQUIRE_HTTPS', true),
|
||||
'app_url' => rtrim(env('APP_URL', ''), '/'),
|
||||
'app_domain' => env('APP_DOMAIN', 'invoicing.co'),
|
||||
'app_version' => '5.3.10',
|
||||
'app_tag' => '5.3.10',
|
||||
'app_version' => '5.3.12',
|
||||
'app_tag' => '5.3.12',
|
||||
'minimum_client_version' => '5.0.16',
|
||||
'terms_version' => '1.0.1',
|
||||
'api_secret' => env('API_SECRET', ''),
|
||||
|
@ -27,6 +27,7 @@ class ProcessSOFORT {
|
||||
handle = () => {
|
||||
let data = {
|
||||
type: 'sofort',
|
||||
customer: document.querySelector('meta[name="customer"]').content,
|
||||
amount: document.querySelector('meta[name="amount"]').content,
|
||||
currency: 'eur',
|
||||
redirect: {
|
||||
|
@ -1,5 +1,5 @@
|
||||
<!DOCTYPE html>
|
||||
<html data-report-errors="{{ $report_errors }}" data-rc="{{ $rc }}" data-build="{{ $build }}">
|
||||
<html data-report-errors="{{ $report_errors }}" data-rc="{{ $rc }}" data-build="{{ $build }}" user-agent="{{ $user_agent }}">
|
||||
<head>
|
||||
<!-- Source: https://github.com/invoiceninja/invoiceninja -->
|
||||
<!-- Version: {{ config('ninja.app_version') }} -->
|
||||
|
@ -6,6 +6,7 @@
|
||||
<meta name="return-url" content="{{ $return_url }}">
|
||||
<meta name="amount" content="{{ $stripe_amount }}">
|
||||
<meta name="country" content="{{ $country }}">
|
||||
<meta name="customer" content="{{ $customer }}">
|
||||
@endsection
|
||||
|
||||
@section('gateway_content')
|
||||
|
@ -51,6 +51,29 @@ class InvoiceItemTest extends TestCase
|
||||
$this->assertEquals($item_calc->getLineTotal(), 10);
|
||||
}
|
||||
|
||||
public function testInvoiceItemTotalSimpleWithGrossTaxes()
|
||||
{
|
||||
$item = InvoiceItemFactory::create();
|
||||
$item->quantity = 1;
|
||||
$item->cost = 10;
|
||||
$item->is_amount_discount = true;
|
||||
$item->tax_rate1 = 10;
|
||||
|
||||
$settings = new \stdClass;
|
||||
$settings->inclusive_taxes = false;
|
||||
$settings->precision = 2;
|
||||
|
||||
$this->invoice->line_items = [$item];
|
||||
|
||||
$item_calc = new InvoiceItemSum($this->invoice, $settings);
|
||||
$item_calc->process();
|
||||
|
||||
$this->assertEquals($item_calc->getLineTotal(), 10);
|
||||
$this->assertEquals($item_calc->getGrossLineTotal(), 11);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function testInvoiceItemTotalSimpleWithDiscount()
|
||||
{
|
||||
$item = InvoiceItemFactory::create();
|
||||
@ -71,6 +94,29 @@ class InvoiceItemTest extends TestCase
|
||||
$this->assertEquals($item_calc->getLineTotal(), 8);
|
||||
}
|
||||
|
||||
public function testInvoiceItemTotalSimpleWithDiscountAndGrossLineTotal()
|
||||
{
|
||||
$item = InvoiceItemFactory::create();
|
||||
$item->quantity = 1;
|
||||
$item->cost = 10;
|
||||
$item->is_amount_discount = true;
|
||||
$item->discount = 2;
|
||||
$item->tax_rate1 = 10;
|
||||
|
||||
$this->invoice->line_items = [$item];
|
||||
|
||||
$settings = new \stdClass;
|
||||
$settings->inclusive_taxes = false;
|
||||
$settings->precision = 2;
|
||||
|
||||
$item_calc = new InvoiceItemSum($this->invoice, $settings);
|
||||
$item_calc->process();
|
||||
|
||||
$this->assertEquals($item_calc->getLineTotal(), 8);
|
||||
$this->assertEquals($item_calc->getGrossLineTotal(), 8.8);
|
||||
|
||||
}
|
||||
|
||||
public function testInvoiceItemTotalSimpleWithDiscountWithPrecision()
|
||||
{
|
||||
$item = InvoiceItemFactory::create();
|
||||
|
@ -123,6 +123,7 @@ class InvoiceTest extends TestCase
|
||||
$this->invoice_calc->build();
|
||||
|
||||
$this->assertEquals($this->invoice_calc->getSubTotal(), 20);
|
||||
// $this->assertEquals($this->invoice_calc->getGrossSubTotal(), 22);
|
||||
//$this->assertEquals($this->invoice_calc->getTotal(), 21.5);
|
||||
//$this->assertEquals($this->invoice_calc->getBalance(), 21.5);
|
||||
//$this->assertEquals($this->invoice_calc->getTotalTaxes(), 1.5);
|
||||
@ -216,6 +217,7 @@ class InvoiceTest extends TestCase
|
||||
$this->invoice_calc->build();
|
||||
|
||||
$this->assertEquals($this->invoice_calc->getSubTotal(), 20);
|
||||
$this->assertEquals($this->invoice_calc->getGrossSubTotal(), 22);
|
||||
//$this->assertEquals($this->invoice_calc->getTotal(), 26);
|
||||
//$this->assertEquals($this->invoice_calc->getBalance(), 26);
|
||||
//$this->assertEquals($this->invoice_calc->getTotalTaxes(), 4);
|
||||
|
Loading…
x
Reference in New Issue
Block a user