Merge branch 'v5-develop' into recurring_expenses

This commit is contained in:
David Bomba 2021-09-15 10:16:24 +10:00
commit dc790559e9
28 changed files with 386003 additions and 385454 deletions

View File

@ -662,6 +662,7 @@ class CompanySettings extends BaseSettings
'$product.discount',
'$product.tax',
'$product.line_total',
'$product.gross_line_total',
],
'task_columns' =>[
'$task.service',
@ -671,9 +672,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',

View File

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

View File

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

View File

@ -199,6 +199,11 @@ class InvoiceItemSumInclusive
return $this->sub_total;
}
public function getGrossSubTotal()
{
return $this->sub_total;
}
public function setSubTotal($value)
{
$this->sub_total = $value;

View File

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

View File

@ -746,6 +746,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();

View File

@ -62,10 +62,10 @@ class SupportMessageSent extends Mailable
$company = auth()->user()->company();
$user = auth()->user();
$db = str_replace("db-ninja-", "", $company->db);
$is_large = $company->is_large ? "L" : "";
$is_large = $company->is_large ? "L" : "S";
if(Ninja::isHosted())
$subject = "{$priority}Hosted-{$db}{$is_large} :: {$plan} :: ".date('M jS, g:ia');
$subject = "{$priority}Hosted-{$db}-{$is_large} :: {$plan} :: ".date('M jS, g:ia');
else
$subject = "{$priority}Self Hosted :: {$plan} :: ".date('M jS, g:ia');

View File

@ -12,6 +12,7 @@
namespace App\Models\Presenters;
use App\Models\Country;
use Illuminate\Support\Str;
/**
* Class CompanyPresenter.
@ -139,4 +140,24 @@ class CompanyPresenter extends EntityPresenter
{
return $this->entity->size ? $this->entity->size->name : '';
}
/**
* Return company website URL.
*
* @return string
*/
public function website(): string
{
$website = $this->entity->getSetting('website');
if (empty($website)) {
return $website;
}
if (Str::contains($website, ['http', 'https'])) {
return $website;
}
return \sprintf('http://%s', $website);
}
}

View File

@ -61,6 +61,13 @@ class CreditCard
$data['gateway'] = $this->braintree;
$data['client_token'] = $this->braintree->gateway->clientToken()->generate();
if ($this->braintree->company_gateway->getConfigField('merchantAccountId')) {
/** https://developer.paypal.com/braintree/docs/reference/request/client-token/generate#merchant_account_id */
$data['client_token'] = $this->braintree->gateway->clientToken()->generate([
'merchantAccountId' => $this->braintree->company_gateway->getConfigField('merchantAccountId')
]);
}
return render('gateways.braintree.credit_card.pay', $data);
}
@ -136,13 +143,15 @@ class CreditCard
$gateway_response = \json_decode($data['gateway_response']);
$response = $this->braintree->gateway->paymentMethod()->create([
$data = [
'customerId' => $customerId,
'paymentMethodNonce' => $gateway_response->nonce,
'options' => [
'verifyCard' => true,
],
]);
];
$response = $this->braintree->gateway->paymentMethod()->create($data);
if ($response->success) {
return $response->paymentMethod->token;

View File

@ -85,7 +85,7 @@ class SOFORT
'gateway_type_id' => GatewayType::SOFORT,
];
$payment = $this->stripe->createPayment($data, Payment::STATUS_PENDING);
$this->stripe->createPayment($data, Payment::STATUS_PENDING);
SystemLogger::dispatch(
['response' => $this->stripe->payment_hash->data, 'data' => $data],
@ -96,7 +96,7 @@ class SOFORT
$this->stripe->client->company,
);
return redirect()->route('client.payments.show', ['payment' => $this->stripe->encodePrimaryKey($payment->id)]);
return redirect()->route('client.payments.index');
}
public function processUnsuccessfulPayment()

View File

@ -22,6 +22,7 @@ use App\Models\ClientGatewayToken;
use App\Models\GatewayType;
use App\Models\Payment;
use App\Models\PaymentHash;
use App\Models\PaymentType;
use App\Models\SystemLog;
use App\PaymentDrivers\Stripe\ACH;
use App\PaymentDrivers\Stripe\Alipay;
@ -422,7 +423,8 @@ class StripePaymentDriver extends BaseDriver
// Allow app to catch up with webhook request.
sleep(2);
if ($request->type === 'charge.succeeded' || $request->type === 'source.chargeable') {
if ($request->type === 'charge.succeeded') {
foreach ($request->data as $transaction) {
$payment = Payment::query()
->where('transaction_reference', $transaction['id'])
@ -434,6 +436,28 @@ class StripePaymentDriver extends BaseDriver
$payment->save();
}
}
} elseif ($request->type === 'source.chargeable') {
$this->init();
foreach ($request->data as $transaction) {
$charge = \Stripe\Charge::create([
'amount' => $request->data['object']['amount'],
'currency' => $request->data['object']['currency'],
'source' => $request->data['object']['id'],
], $this->stripe_connect_auth);
if ($charge->captured) {
$payment = Payment::query()
->where('transaction_reference', $transaction['id'])
->where('company_id', $request->getCompany()->id)
->first();
if ($payment) {
$payment->status_id = Payment::STATUS_COMPLETED;
$payment->save();
}
}
}
}
return response()->json([], 200);
@ -540,6 +564,11 @@ class StripePaymentDriver extends BaseDriver
return Account::all();
}
public function setClientFromCustomer($customer)
{
$this->client = ClientGatewayToken::where('gateway_customer_reference', $customer)->client;
}
/**
* Pull all client payment methods and update
* the respective tokens in the system.

View File

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

View File

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

View File

@ -51,6 +51,7 @@
"hashids/hashids": "^4.0",
"hedii/laravel-gelf-logger": "^6.0",
"intervention/image": "^2.5",
"invoiceninja/inspector": "dev-main",
"laracasts/presenter": "^0.2.1",
"laravel/framework": "^8.0",
"laravel/slack-notification-channel": "^2.2",

68
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "0b4a7e813c3e563d631164bd24968964",
"content-hash": "b5675738fe061ea09b0bbd89e553d074",
"packages": [
{
"name": "asm/php-ansible",
@ -3326,6 +3326,69 @@
],
"time": "2021-07-22T14:31:53+00:00"
},
{
"name": "invoiceninja/inspector",
"version": "dev-main",
"source": {
"type": "git",
"url": "https://github.com/invoiceninja/inspector.git",
"reference": "3f5beb9854c0d1d6f65bc0d9ba847503e6e84583"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/invoiceninja/inspector/zipball/3f5beb9854c0d1d6f65bc0d9ba847503e6e84583",
"reference": "3f5beb9854c0d1d6f65bc0d9ba847503e6e84583",
"shasum": ""
},
"require": {
"doctrine/dbal": "^3.1",
"illuminate/support": "^8.0",
"php": "^7.4|^8.0"
},
"require-dev": {
"orchestra/testbench": "^6.0",
"phpunit/phpunit": "^9.0"
},
"default-branch": true,
"type": "library",
"extra": {
"laravel": {
"providers": [
"InvoiceNinja\\Inspector\\InspectorServiceProvider"
],
"aliases": {
"Inspector": "InvoiceNinja\\Inspector\\InspectorFacade"
}
}
},
"autoload": {
"psr-4": {
"InvoiceNinja\\Inspector\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Benjamin Beganović",
"email": "benjamin.beganovic4@outlook.com",
"role": "Developer"
}
],
"description": "Simplified database records management",
"homepage": "https://github.com/invoiceninja/inspector",
"keywords": [
"inspector",
"invoiceninja"
],
"support": {
"issues": "https://github.com/invoiceninja/inspector/issues",
"source": "https://github.com/invoiceninja/inspector/tree/main"
},
"time": "2021-09-11T11:35:02+00:00"
},
{
"name": "jean85/pretty-package-versions",
"version": "2.0.4",
@ -15598,6 +15661,7 @@
"minimum-stability": "dev",
"stability-flags": {
"asm/php-ansible": 20,
"invoiceninja/inspector": 20,
"webpatser/laravel-countries": 20
},
"prefer-stable": true,
@ -15611,5 +15675,5 @@
"platform-dev": {
"php": "^7.3|^7.4|^8.0"
},
"plugin-api-version": "2.0.0"
"plugin-api-version": "2.1.0"
}

View File

@ -3,12 +3,12 @@ const MANIFEST = 'flutter-app-manifest';
const TEMP = 'flutter-temp-cache';
const CACHE_NAME = 'flutter-app-cache';
const RESOURCES = {
"main.dart.js": "5737d30da23ff502a55061345fd08bd1",
"main.dart.js": "17e5b654f448781d79f55e1b223eda44",
"version.json": "9ec5e3813adc4bfd8713556c5059e97d",
"manifest.json": "ef43d90e57aa7682d7e2cfba2f484a40",
"icons/Icon-512.png": "0f9aff01367f0a0c69773d25ca16ef35",
"icons/Icon-192.png": "bb1cf5f6982006952211c7c8404ffbed",
"/": "1692e36290d6065cb75b3b2dd7dac863",
"/": "11547dbccd61ee164f3e727ec78c2fd4",
"assets/NOTICES": "9eb7e2eb2888ea5bae5f536720db37cd",
"assets/fonts/MaterialIcons-Regular.otf": "4e6447691c9509f7acdbf8a931a85ca1",
"assets/AssetManifest.json": "38d9aea341601f3a5c6fa7b5a1216ea5",

127967
public/main.dart.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

174321
public/main.foss.dart.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

147367
public/main.last.dart.js vendored

File diff suppressed because one or more lines are too long

140661
public/main.next.dart.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

174505
public/main.wasm.dart.js vendored

File diff suppressed because one or more lines are too long

View File

@ -6,6 +6,7 @@
<meta charset="UTF-8">
<title>Invoice Ninja</title>
<meta name="google-signin-client_id" content="{{ config('services.google.client_id') }}">
<meta name="user-agent" content="{{ $user_agent }}">
<link rel="manifest" href="manifest.json?v={{ config('ninja.app_version') }}">
<script src="{{ asset('js/pdf.min.js') }}"></script>
<script type="text/javascript">

View File

@ -77,10 +77,10 @@
</div>
@endif
@if(!is_null($company) && !empty($company->getSetting('website')))
@if(!empty($company->present()->website()))
<div class="mt-5 text-center">
<a class="button-link text-sm" href="{{ $company->getSetting('website') }}">
{{ ctrans('texts.back_to', ['url' => parse_url($company->getSetting('website'))['host'] ?? $company->getSetting('website') ]) }}
<a class="button-link text-sm" href="{{ $company->present()->website() }}">
{{ ctrans('texts.back_to', ['url' => parse_url($company->present()->website())['host'] ?? $company->present()->website() ]) }}
</a>
</div>
@endif

View File

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

View File

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