mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2024-11-05 18:52:44 +01:00
commit
7c1d751e56
@ -1 +1 @@
|
||||
5.0.44
|
||||
5.0.45
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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']]);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 = [
|
||||
|
@ -266,10 +266,6 @@ class CompanyGateway extends BaseModel
|
||||
{
|
||||
$label = '';
|
||||
|
||||
if (! $this->feesEnabled()) {
|
||||
return $label;
|
||||
}
|
||||
|
||||
$fee = $this->calcGatewayFee($amount, $gateway_type_id);
|
||||
|
||||
if ($fee > 0) {
|
||||
|
@ -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 = [
|
||||
|
@ -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,
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -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
521
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@ -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),
|
||||
|
@ -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()
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user