1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-11-05 18:52:44 +01:00

Merge pull request #4659 from turbo124/v5-develop

Version bump
This commit is contained in:
David Bomba 2021-01-10 09:15:47 +11:00 committed by GitHub
commit 7c1d751e56
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 607 additions and 200 deletions

View File

@ -1 +1 @@
5.0.44
5.0.45

View File

@ -29,3 +29,11 @@ function nlog($output, $context = []): void
\Illuminate\Support\Facades\Log::channel('invoiceninja')->info($output, $context);
}
}
if (!function_exists('ray')) {
function ray($payload)
{
return true;
}
}

View File

@ -76,12 +76,15 @@ class InvitationController extends Controller
{
switch ($entity_string) {
case 'invoice':
$invitation->invoice->service()->markSent()->save();
event(new InvoiceWasViewed($invitation, $invitation->company, Ninja::eventVars()));
break;
case 'quote':
$invitation->quote->service()->markSent()->save();
event(new QuoteWasViewed($invitation, $invitation->company, Ninja::eventVars()));
break;
case 'credit':
$invitation->credit->service()->markSent()->save();
event(new CreditWasViewed($invitation, $invitation->company, Ninja::eventVars()));
break;
default:

View File

@ -88,17 +88,30 @@ class InvoiceController extends Controller
->whereClientId(auth()->user()->client->id)
->get();
$total = $invoices->sum('balance');
//filter invoices which are payable
$invoices = $invoices->filter(function ($invoice) {
return $invoice->isPayable() && $invoice->balance > 0;
});
//return early if no invoices.
if ($invoices->count() == 0) {
return back()
->with('message', ctrans('texts.no_payable_invoices_selected'));
}
//iterate and sum the payable amounts either partial or balance
$total = 0;
foreach($invoices as $invoice)
{
if($invoice->partial > 0)
$total += $invoice->partial;
else
$total += $invoice->balance;
}
//format data
$invoices->map(function ($invoice) {
$invoice->service()->removeUnpaidGatewayFees()->save();
$invoice->balance = Number::formatValue($invoice->balance, $invoice->client->currency());
@ -107,6 +120,7 @@ class InvoiceController extends Controller
return $invoice;
});
//format totals
$formatted_total = Number::formatMoney($total, auth()->user()->client);
$payment_methods = auth()->user()->client->getPaymentMethods($total);

View File

@ -66,6 +66,10 @@ class UpdateExpenseRequest extends Request
$input['category_id'] = $this->decodePrimaryKey($input['category_id']);
}
if (array_key_exists('documents', $input)) {
unset($input['documents']);
}
if (! array_key_exists('currency_id', $input) || strlen($input['currency_id']) == 0) {
$input['currency_id'] = (string)auth()->user()->company()->settings->currency_id;
}

View File

@ -42,6 +42,9 @@ class StoreExpenseCategoryRequest extends Request
$input = $this->decodePrimaryKeys($input);
if(array_key_exists('color', $input) && is_null($input['color']))
$input['color'] = '#fff';
$this->replace($input);
}
}
}

View File

@ -13,6 +13,7 @@ namespace App\Http\Requests\ExpenseCategory;
use App\Http\Requests\Request;
use App\Utils\Traits\ChecksEntityStatus;
use Illuminate\Validation\Rule;
class UpdateExpenseCategoryRequest extends Request
{
@ -33,9 +34,21 @@ class UpdateExpenseCategoryRequest extends Request
$rules = [];
if ($this->input('name')) {
$rules['name'] = 'unique:expense_categories,name,'.$this->id.',id,company_id,'.$this->expense_category->company_id;
// $rules['name'] = 'unique:expense_categories,name,'.$this->id.',id,company_id,'.$this->expense_category->company_id;
$rules['name'] = Rule::unique('expense_categories')->where('company_id', auth()->user()->company()->id)->ignore($this->expense_category->id);
}
return $rules;
}
protected function prepareForValidation()
{
$input = $this->all();
if(array_key_exists('color', $input) && is_null($input['color']))
$input['color'] = '#fff';
$this->replace($input);
}
}

View File

@ -67,6 +67,10 @@ class UpdateInvoiceRequest extends Request
$input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : [];
if (array_key_exists('documents', $input)) {
unset($input['documents']);
}
$this->replace($input);
}

View File

@ -73,6 +73,10 @@ class UpdateQuoteRequest extends Request
$input['assigned_user_id'] = $this->decodePrimaryKey($input['assigned_user_id']);
}
if (array_key_exists('documents', $input)) {
unset($input['documents']);
}
$input['id'] = $this->quote->id;
$this->replace($input);

View File

@ -91,6 +91,10 @@ class UpdateRecurringInvoiceRequest extends Request
$input['auto_bill_enabled'] = $this->setAutoBillFlag($input['auto_bill']);
}
if (array_key_exists('documents', $input)) {
unset($input['documents']);
}
$this->replace($input);
}

View File

@ -13,6 +13,7 @@ namespace App\Http\Requests\TaskStatus;
use App\Http\Requests\Request;
use App\Utils\Traits\MakesHash;
use Illuminate\Validation\Rule;
class UpdateTaskStatusRequest extends Request
{
@ -33,9 +34,11 @@ class UpdateTaskStatusRequest extends Request
$rules = [];
if ($this->input('name')) {
$rules['name'] = 'unique:task_statuses,name,'.$this->id.',id,company_id,'.$this->task_status->company_id;
//$rules['name'] = 'unique:task_statuses,name,'.$this->id.',id,company_id,'.$this->task_status->company_id;
$rules['name'] = Rule::unique('task_statuses')->where('company_id', auth()->user()->company()->id)->ignore($this->task_status->id);
}
return $rules;
}

View File

@ -11,6 +11,7 @@
namespace App\Jobs\Util;
use Illuminate\Http\UploadedFile;
use App\DataMapper\Analytics\MigrationFailure;
use App\DataMapper\CompanySettings;
use App\Exceptions\MigrationValidatorFailed;
@ -124,7 +125,7 @@ class Import implements ShouldQueue
'task_statuses',
'expenses',
'tasks',
// 'documents',
'documents',
];
/**
@ -964,56 +965,41 @@ class Import implements ShouldQueue
$entity = Expense::where('id', $expense_id)->withTrashed()->first();
}
$this->saveDocument(file_get_contents($resource['url']), $entity, $is_public = true);
$file_url = $resource['url'];
$file_name = basename($file_url);
$file_path = sys_get_temp_dir().'/'.$file_name;
file_put_contents($file_path, file_get_contents($file_url), LOCK_EX);
$finfo = new \finfo(FILEINFO_MIME_TYPE);
$file_info = $finfo->file($file_path);
nlog($resource['url']);
nlog($file_url);
nlog($file_name);
nlog($file_path);
nlog($file_info);
nlog(filesize($file_path));
$uploaded_file = new UploadedFile(
$file_path,
$file_name,
$file_info,
filesize($file_path),
0,
false
);
$this->saveDocument($uploaded_file, $entity, $is_public = true);
$uploaded_file = null;
$finfo = null;
$file_url = null;
$file_name = null;
$file_path = null;
$file_info = null;
}
// foreach ($data as $resource) {
// $modified = $resource;
// if (array_key_exists('invoice_id', $resource) && $resource['invoice_id'] && ! array_key_exists('invoices', $this->ids)) {
// throw new ResourceDependencyMissing('Processing documents failed, because of missing dependency - invoices.');
// }
// if (array_key_exists('expense_id', $resource) && $resource['expense_id'] && ! array_key_exists('expenses', $this->ids)) {
// throw new ResourceDependencyMissing('Processing documents failed, because of missing dependency - expenses.');
// }
// /* Remove because of polymorphic joins. */
// unset($modified['invoice_id']);
// unset($modified['expense_id']);
// if (array_key_exists('invoice_id', $resource) && $resource['invoice_id'] && array_key_exists('invoices', $this->ids)) {
// $modified['documentable_id'] = $this->transformId('invoices', $resource['invoice_id']);
// $modified['documentable_type'] = Invoice::class;
// }
// if (array_key_exists('expense_id', $resource) && $resource['expense_id'] && array_key_exists('expenses', $this->ids)) {
// $modified['documentable_id'] = $this->transformId('expenses', $resource['expense_id']);
// $modified['documentable_type'] = Expense::class;
// }
// $modified['user_id'] = $this->processUserId($resource);
// $modified['company_id'] = $this->company->id;
// $document = Document::create($modified);
// // $entity = $modified['documentable_type']::find($modified['documentable_id']);
// // $entity->documents()->save($modified);
// $old_user_key = array_key_exists('user_id', $resource) ?? $this->user->id;
// $this->ids['documents'] = [
// "documents_{$old_user_key}" => [
// 'old' => $resource['id'],
// 'new' => $document->id,
// ],
// ];
// }
// Document::reguard();
// /*Improve memory handling by setting everything to null when we have finished*/
// $data = null;
}
private function processPaymentTerms(array $data) :void
@ -1157,12 +1143,11 @@ class Import implements ShouldQueue
$old_user_key = array_key_exists('user_id', $resource) ?? $this->user->id;
$key = "expense_categories_{$resource['id']}";
$this->ids['expense_categories'][$key] = [
'old' => $resource['id'],
'new' => $expense_category->id,
'new' => $expense_category->id,
];
// $this->ids['expense_categories'] = [
@ -1290,12 +1275,13 @@ class Import implements ShouldQueue
$old_user_key = array_key_exists('user_id', $resource) ?? $this->user->id;
$this->ids['expenses'] = [
"expenses_{$old_user_key}" => [
'old' => $resource['id'],
'new' => $expense->id,
],
$key = "expenses_{$resource['id']}";
$this->ids['expenses'][$key] = [
'old' => $resource['id'],
'new' => $expense->id,
];
}
Expense::reguard();

View File

@ -88,7 +88,18 @@ class CreditEmailEngine extends BaseEmailEngine
->setViewText(ctrans('texts.view_credit'));
if ($this->client->getSetting('pdf_email_attachment') !== false) {
$this->setAttachments([$this->credit->pdf_file_path()]);
$this->setAttachments(['path' => $this->credit->pdf_file_path(), 'name' => basename($this->credit->pdf_file_path())]);
}
//attach third party documents
if($this->client->getSetting('document_email_attachment') !== false){
// Storage::url
foreach($this->credit->documents as $document){
// $this->setAttachments(['path'=>$document->filePath(),'name'=>$document->name]);
$this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => $document->type]]);
}
}
return $this;

View File

@ -98,6 +98,19 @@ class InvoiceEmailEngine extends BaseEmailEngine
if ($this->client->getSetting('pdf_email_attachment') !== false) {
$this->setAttachments([$this->invoice->pdf_file_path()]);
// $this->setAttachments(['path' => $this->invoice->pdf_file_path(), 'name' => basename($this->invoice->pdf_file_path())]);
}
//attach third party documents
if($this->client->getSetting('document_email_attachment') !== false){
// Storage::url
foreach($this->invoice->documents as $document){
// $this->setAttachments(['path'=>$document->filePath(),'name'=>$document->name]);
$this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => $document->type]]);
}
}
return $this;

View File

@ -88,9 +88,22 @@ class QuoteEmailEngine extends BaseEmailEngine
->setViewText(ctrans('texts.view_quote'));
if ($this->client->getSetting('pdf_email_attachment') !== false) {
$this->setAttachments([$this->invitation->pdf_file_path()]);
// $this->setAttachments([$this->quote->pdf_file_path()]);
$this->setAttachments(['path' => $this->quote->pdf_file_path(), 'name' => basename($this->quote->pdf_file_path())]);
}
//attach third party documents
if($this->client->getSetting('document_email_attachment') !== false){
// Storage::url
foreach($this->quote->documents as $document){
// $this->setAttachments(['path'=>$document->filePath(),'name'=>$document->name]);
$this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => $document->type]]);
}
}
return $this;
}
}

View File

@ -84,8 +84,15 @@ class TemplateEmail extends Mailable
//conditionally attach files
if ($settings->pdf_email_attachment !== false && ! empty($this->build_email->getAttachments())) {
//hosted | plan check here
foreach ($this->build_email->getAttachments() as $file) {
$this->attach($file);
if(is_string($file))
$this->attach($file);
elseif(is_array($file))
$this->attach($file['path'], ['as' => $file['name'], 'mime' => $file['mime']]);
}
}

View File

@ -82,6 +82,8 @@ class Company extends BaseModel
'is_disabled',
'default_task_is_date_based',
'enable_product_discount',
'expense_inclusive_taxes',
'expense_amount_is_pretax',
];
protected $hidden = [

View File

@ -266,10 +266,6 @@ class CompanyGateway extends BaseModel
{
$label = '';
if (! $this->feesEnabled()) {
return $label;
}
$fee = $this->calcGatewayFee($amount, $gateway_type_id);
if ($fee > 0) {

View File

@ -51,6 +51,11 @@ class Expense extends BaseModel
'custom_value3',
'custom_value4',
'number',
'tax_amount1',
'tax_amount2',
'tax_amount3',
'uses_inclusive_taxes',
'amount_is_pretax',
];
protected $casts = [

View File

@ -150,6 +150,8 @@ class CompanyTransformer extends EntityTransformer
'enable_product_discount' => (bool)$company->enable_product_discount,
'calculate_expense_tax_by_amount' =>(bool)$company->calculate_expense_tax_by_amount,
'hide_empty_columns_on_pdf' => false, //@deprecate
'expense_inclusive_taxes' => (bool)$company->expense_inclusive_taxes,
'expense_amount_is_pretax' =>( bool)$company->expense_amount_is_pretax,
];
}

View File

@ -91,6 +91,11 @@ class ExpenseTransformer extends EntityTransformer
'archived_at' => (int) $expense->deleted_at,
'created_at' => (int) $expense->created_at,
'project_id' => $this->encodePrimaryKey($expense->project_id),
'tax_amount1' => (float) $expense->tax_amount1,
'tax_amount2' => (float) $expense->tax_amount2,
'tax_amount3' => (float) $expense->tax_amount3,
'uses_inclusive_taxes' => (bool) $expense->uses_inclusive_taxes,
'amount_is_pretax' => (bool) $expense->amount_is_pretax,
];
}
}

View File

@ -68,6 +68,7 @@
"webpatser/laravel-countries": "dev-master#75992ad"
},
"require-dev": {
"php": "^7.4",
"anahkiasen/former": "^4.2",
"barryvdh/laravel-debugbar": "^3.4",
"darkaonline/l5-swagger": "^8.0",
@ -78,6 +79,7 @@
"mockery/mockery": "^1.3.1",
"nunomaduro/collision": "^5.0",
"phpunit/phpunit": "^9.0",
"spatie/laravel-ray": "^1.3",
"vimeo/psalm": "^4.0",
"wildbit/postmark-php": "^4.0"
},

521
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -13,7 +13,7 @@ return [
'require_https' => env('REQUIRE_HTTPS', true),
'app_url' => rtrim(env('APP_URL', ''), '/'),
'app_domain' => env('APP_DOMAIN', ''),
'app_version' => '5.0.44',
'app_version' => '5.0.45',
'minimum_client_version' => '5.0.16',
'terms_version' => '1.0.1',
'api_secret' => env('API_SECRET', false),

View File

@ -0,0 +1,40 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class ExpensesTableAdditionalFields extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('expenses', function (Blueprint $table) {
$table->decimal('tax_amount1', 20, 6)->default();
$table->decimal('tax_amount2', 20, 6)->default();
$table->decimal('tax_amount3', 20, 6)->default();
$table->boolean('uses_inclusive_taxes')->default(0);
$table->boolean('amount_is_pretax')->default(1);
});
Schema::table('companies', function (Blueprint $table) {
$table->boolean('expense_inclusive_taxes')->default(0);
$table->boolean('expense_amount_is_pretax')->default(1);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
//
}
}

View File

@ -99,8 +99,8 @@
function invokeServiceWorkerUpdateFlow() {
// you have a better UI here, reloading is not a great user experince here.
const confirmed = confirm('New version of the app is available. Refresh now');
if (confirmed) {
const confirmed = alert('New version of the app is available. Refresh now');
if (confirmed == true) {
window.location.reload();
}
}

View File

@ -540,7 +540,7 @@ trait MockAccountData
$data[1]['fee_tax_rate2'] = '';
$data[1]['fee_tax_name3'] = '';
$data[1]['fee_tax_rate3'] = 0;
$data[1]['fee_cap'] = '';
$cg = new CompanyGateway;
$cg->company_id = $this->company->id;
$cg->user_id = $this->user->id;