mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2024-11-12 06:02:39 +01:00
commit
e86c5e126a
@ -1 +1 @@
|
|||||||
5.5.11
|
5.5.12
|
@ -587,21 +587,21 @@ class CreditController extends BaseController
|
|||||||
$this->credit_repository->archive($credit);
|
$this->credit_repository->archive($credit);
|
||||||
|
|
||||||
if (! $bulk) {
|
if (! $bulk) {
|
||||||
return $this->listResponse($credit);
|
return $this->itemResponse($credit);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'restore':
|
case 'restore':
|
||||||
$this->credit_repository->restore($credit);
|
$this->credit_repository->restore($credit);
|
||||||
|
|
||||||
if (! $bulk) {
|
if (! $bulk) {
|
||||||
return $this->listResponse($credit);
|
return $this->itemResponse($credit);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'delete':
|
case 'delete':
|
||||||
$this->credit_repository->delete($credit);
|
$this->credit_repository->delete($credit);
|
||||||
|
|
||||||
if (! $bulk) {
|
if (! $bulk) {
|
||||||
return $this->listResponse($credit);
|
return $this->itemResponse($credit);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'email':
|
case 'email':
|
||||||
|
@ -722,14 +722,14 @@ class InvoiceController extends BaseController
|
|||||||
$this->invoice_repo->restore($invoice);
|
$this->invoice_repo->restore($invoice);
|
||||||
|
|
||||||
if (! $bulk) {
|
if (! $bulk) {
|
||||||
return $this->listResponse($invoice);
|
return $this->itemResponse($invoice);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'archive':
|
case 'archive':
|
||||||
$this->invoice_repo->archive($invoice);
|
$this->invoice_repo->archive($invoice);
|
||||||
|
|
||||||
if (! $bulk) {
|
if (! $bulk) {
|
||||||
return $this->listResponse($invoice);
|
return $this->itemResponse($invoice);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'delete':
|
case 'delete':
|
||||||
@ -737,7 +737,7 @@ class InvoiceController extends BaseController
|
|||||||
$this->invoice_repo->delete($invoice);
|
$this->invoice_repo->delete($invoice);
|
||||||
|
|
||||||
if (! $bulk) {
|
if (! $bulk) {
|
||||||
return $this->listResponse($invoice);
|
return $this->itemResponse($invoice);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'cancel':
|
case 'cancel':
|
||||||
|
@ -623,14 +623,14 @@ class PurchaseOrderController extends BaseController
|
|||||||
$this->purchase_order_repository->restore($purchase_order);
|
$this->purchase_order_repository->restore($purchase_order);
|
||||||
|
|
||||||
if (! $bulk) {
|
if (! $bulk) {
|
||||||
return $this->listResponse($purchase_order);
|
return $this->itemResponse($purchase_order);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'archive':
|
case 'archive':
|
||||||
$this->purchase_order_repository->archive($purchase_order);
|
$this->purchase_order_repository->archive($purchase_order);
|
||||||
|
|
||||||
if (! $bulk) {
|
if (! $bulk) {
|
||||||
return $this->listResponse($purchase_order);
|
return $this->itemResponse($purchase_order);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'delete':
|
case 'delete':
|
||||||
@ -638,7 +638,7 @@ class PurchaseOrderController extends BaseController
|
|||||||
$this->purchase_order_repository->delete($purchase_order);
|
$this->purchase_order_repository->delete($purchase_order);
|
||||||
|
|
||||||
if (! $bulk) {
|
if (! $bulk) {
|
||||||
return $this->listResponse($purchase_order);
|
return $this->itemResponse($purchase_order);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -684,7 +684,7 @@ class PurchaseOrderController extends BaseController
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (! $bulk) {
|
if (! $bulk) {
|
||||||
return $this->listResponse($purchase_order);
|
return $this->itemResponse($purchase_order);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -728,7 +728,7 @@ class QuoteController extends BaseController
|
|||||||
$this->quote_repo->restore($quote);
|
$this->quote_repo->restore($quote);
|
||||||
|
|
||||||
if (! $bulk) {
|
if (! $bulk) {
|
||||||
return $this->listResponse($quote);
|
return $this->itemResponse($quote);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@ -736,7 +736,7 @@ class QuoteController extends BaseController
|
|||||||
$this->quote_repo->archive($quote);
|
$this->quote_repo->archive($quote);
|
||||||
|
|
||||||
if (! $bulk) {
|
if (! $bulk) {
|
||||||
return $this->listResponse($quote);
|
return $this->itemResponse($quote);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@ -744,7 +744,7 @@ class QuoteController extends BaseController
|
|||||||
$this->quote_repo->delete($quote);
|
$this->quote_repo->delete($quote);
|
||||||
|
|
||||||
if (! $bulk) {
|
if (! $bulk) {
|
||||||
return $this->listResponse($quote);
|
return $this->itemResponse($quote);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -511,21 +511,21 @@ class RecurringExpenseController extends BaseController
|
|||||||
$this->recurring_expense_repo->archive($recurring_expense);
|
$this->recurring_expense_repo->archive($recurring_expense);
|
||||||
|
|
||||||
if (! $bulk) {
|
if (! $bulk) {
|
||||||
return $this->listResponse($recurring_expense);
|
return $this->itemResponse($recurring_expense);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'restore':
|
case 'restore':
|
||||||
$this->recurring_expense_repo->restore($recurring_expense);
|
$this->recurring_expense_repo->restore($recurring_expense);
|
||||||
|
|
||||||
if (! $bulk) {
|
if (! $bulk) {
|
||||||
return $this->listResponse($recurring_expense);
|
return $this->itemResponse($recurring_expense);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'delete':
|
case 'delete':
|
||||||
$this->recurring_expense_repo->delete($recurring_expense);
|
$this->recurring_expense_repo->delete($recurring_expense);
|
||||||
|
|
||||||
if (! $bulk) {
|
if (! $bulk) {
|
||||||
return $this->listResponse($recurring_expense);
|
return $this->itemResponse($recurring_expense);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'email':
|
case 'email':
|
||||||
|
@ -662,21 +662,21 @@ class RecurringInvoiceController extends BaseController
|
|||||||
$this->recurring_invoice_repo->archive($recurring_invoice);
|
$this->recurring_invoice_repo->archive($recurring_invoice);
|
||||||
|
|
||||||
if (! $bulk) {
|
if (! $bulk) {
|
||||||
return $this->listResponse($recurring_invoice);
|
return $this->itemResponse($recurring_invoice);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'restore':
|
case 'restore':
|
||||||
$this->recurring_invoice_repo->restore($recurring_invoice);
|
$this->recurring_invoice_repo->restore($recurring_invoice);
|
||||||
|
|
||||||
if (! $bulk) {
|
if (! $bulk) {
|
||||||
return $this->listResponse($recurring_invoice);
|
return $this->itemResponse($recurring_invoice);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'delete':
|
case 'delete':
|
||||||
$this->recurring_invoice_repo->delete($recurring_invoice);
|
$this->recurring_invoice_repo->delete($recurring_invoice);
|
||||||
|
|
||||||
if (! $bulk) {
|
if (! $bulk) {
|
||||||
return $this->listResponse($recurring_invoice);
|
return $this->itemResponse($recurring_invoice);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'email':
|
case 'email':
|
||||||
|
@ -47,15 +47,4 @@ class LoginRequest extends Request
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
// public function prepareForValidation()
|
|
||||||
// {
|
|
||||||
// $input = $this->all();
|
|
||||||
|
|
||||||
// // if(base64_decode(base64_encode($input['password'])) === $input['password'])
|
|
||||||
// // $input['password'] = base64_decode($input['password']);
|
|
||||||
|
|
||||||
// // nlog($input['password']);
|
|
||||||
|
|
||||||
// $this->replace($input);
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
namespace App\Http\Requests\Task;
|
namespace App\Http\Requests\Task;
|
||||||
|
|
||||||
use App\Http\Requests\Request;
|
use App\Http\Requests\Request;
|
||||||
|
use App\Models\Project;
|
||||||
use App\Models\Task;
|
use App\Models\Task;
|
||||||
use App\Utils\Traits\MakesHash;
|
use App\Utils\Traits\MakesHash;
|
||||||
use Illuminate\Validation\Rule;
|
use Illuminate\Validation\Rule;
|
||||||
@ -38,19 +39,49 @@ class StoreTaskRequest extends Request
|
|||||||
$rules['number'] = Rule::unique('tasks')->where('company_id', auth()->user()->company()->id);
|
$rules['number'] = Rule::unique('tasks')->where('company_id', auth()->user()->company()->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(isset($this->client_id))
|
||||||
|
$rules['client_id'] = 'bail|required|exists:clients,id,company_id,'.auth()->user()->company()->id.',is_deleted,0';
|
||||||
|
|
||||||
|
if(isset($this->project_id))
|
||||||
|
$rules['project_id'] = 'bail|required|exists:projects,id,company_id,'.auth()->user()->company()->id.',is_deleted,0';
|
||||||
|
|
||||||
|
$rules['timelog'] = ['bail','array',function ($attribute, $values, $fail) {
|
||||||
|
|
||||||
|
foreach($values as $k)
|
||||||
|
{
|
||||||
|
if(!is_int($k[0]) || !is_int($k[1]))
|
||||||
|
$fail('The '.$attribute.' - '.print_r($k,1).' is invalid. Unix timestamps only.');
|
||||||
|
}
|
||||||
|
|
||||||
|
}];
|
||||||
|
|
||||||
|
|
||||||
return $this->globalRules($rules);
|
return $this->globalRules($rules);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function prepareForValidation()
|
public function prepareForValidation()
|
||||||
{
|
{
|
||||||
$input = $this->all();
|
$input = $this->all();
|
||||||
|
|
||||||
$input = $this->decodePrimaryKeys($this->all());
|
$input = $this->decodePrimaryKeys($this->all());
|
||||||
|
|
||||||
if (array_key_exists('status_id', $input) && is_string($input['status_id'])) {
|
if (array_key_exists('status_id', $input) && is_string($input['status_id'])) {
|
||||||
$input['status_id'] = $this->decodePrimaryKey($input['status_id']);
|
$input['status_id'] = $this->decodePrimaryKey($input['status_id']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Ensure the project is related */
|
||||||
|
if (array_key_exists('project_id', $input) && isset($input['project_id'])) {
|
||||||
|
$project = Project::withTrashed()->find($input['project_id'])->company()->first();
|
||||||
|
|
||||||
|
if($project){
|
||||||
|
$input['client_id'] = $project->client_id;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
unset($input['project_id']);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
$this->replace($input);
|
$this->replace($input);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
namespace App\Http\Requests\Task;
|
namespace App\Http\Requests\Task;
|
||||||
|
|
||||||
use App\Http\Requests\Request;
|
use App\Http\Requests\Request;
|
||||||
|
use App\Models\Project;
|
||||||
use App\Utils\Traits\ChecksEntityStatus;
|
use App\Utils\Traits\ChecksEntityStatus;
|
||||||
use App\Utils\Traits\MakesHash;
|
use App\Utils\Traits\MakesHash;
|
||||||
use Illuminate\Validation\Rule;
|
use Illuminate\Validation\Rule;
|
||||||
@ -39,6 +40,22 @@ class UpdateTaskRequest extends Request
|
|||||||
$rules['number'] = Rule::unique('tasks')->where('company_id', auth()->user()->company()->id)->ignore($this->task->id);
|
$rules['number'] = Rule::unique('tasks')->where('company_id', auth()->user()->company()->id)->ignore($this->task->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(isset($this->client_id))
|
||||||
|
$rules['client_id'] = 'bail|required|exists:clients,id,company_id,'.auth()->user()->company()->id.',is_deleted,0';
|
||||||
|
|
||||||
|
if(isset($this->project_id))
|
||||||
|
$rules['project_id'] = 'bail|required|exists:projects,id,company_id,'.auth()->user()->company()->id.',is_deleted,0';
|
||||||
|
|
||||||
|
$rules['timelog'] = ['bail','array',function ($attribute, $values, $fail) {
|
||||||
|
|
||||||
|
foreach($values as $k)
|
||||||
|
{
|
||||||
|
if(!is_int($k[0]) || !is_int($k[1]))
|
||||||
|
$fail('The '.$attribute.' - '.print_r($k,1).' is invalid. Unix timestamps only.');
|
||||||
|
}
|
||||||
|
|
||||||
|
}];
|
||||||
|
|
||||||
return $this->globalRules($rules);
|
return $this->globalRules($rules);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,6 +67,20 @@ class UpdateTaskRequest extends Request
|
|||||||
$input['status_id'] = $this->decodePrimaryKey($input['status_id']);
|
$input['status_id'] = $this->decodePrimaryKey($input['status_id']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Ensure the project is related */
|
||||||
|
if (array_key_exists('project_id', $input) && isset($input['project_id'])) {
|
||||||
|
$project = Project::withTrashed()->find($input['project_id'])->company()->first();
|
||||||
|
|
||||||
|
if($project){
|
||||||
|
$input['client_id'] = $project->client_id;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
unset($input['project_id']);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
if (array_key_exists('color', $input) && is_null($input['color'])) {
|
if (array_key_exists('color', $input) && is_null($input['color'])) {
|
||||||
$input['color'] = '';
|
$input['color'] = '';
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,8 @@ class BlackListRule implements Rule
|
|||||||
'vusra.com',
|
'vusra.com',
|
||||||
'fourthgenet.com',
|
'fourthgenet.com',
|
||||||
'arxxwalls.com',
|
'arxxwalls.com',
|
||||||
'superhostforumla.com'
|
'superhostforumla.com',
|
||||||
|
'wnpop.com',
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -324,6 +324,10 @@ class NinjaMailerJob implements ShouldQueue
|
|||||||
if($this->company->is_disabled && !$this->override)
|
if($this->company->is_disabled && !$this->override)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
/* To handle spam users we drop all emails from flagged accounts */
|
||||||
|
if(Ninja::isHosted() && $this->company->account && $this->company->account->is_flagged)
|
||||||
|
return true;
|
||||||
|
|
||||||
/* On the hosted platform we set default contacts a @example.com email address - we shouldn't send emails to these types of addresses */
|
/* On the hosted platform we set default contacts a @example.com email address - we shouldn't send emails to these types of addresses */
|
||||||
if(Ninja::isHosted() && $this->nmo->to_user && strpos($this->nmo->to_user->email, '@example.com') !== false)
|
if(Ninja::isHosted() && $this->nmo->to_user && strpos($this->nmo->to_user->email, '@example.com') !== false)
|
||||||
return true;
|
return true;
|
||||||
@ -336,10 +340,6 @@ class NinjaMailerJob implements ShouldQueue
|
|||||||
if(Ninja::isHosted() && $this->company->account && $this->company->account->emailQuotaExceeded())
|
if(Ninja::isHosted() && $this->company->account && $this->company->account->emailQuotaExceeded())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
/* To handle spam users we drop all emails from flagged accounts */
|
|
||||||
if(Ninja::isHosted() && $this->company->account && $this->company->account->is_flagged)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
/* If the account is verified, we allow emails to flow */
|
/* If the account is verified, we allow emails to flow */
|
||||||
if(Ninja::isHosted() && $this->company->account && $this->company->account->is_verified_account) {
|
if(Ninja::isHosted() && $this->company->account && $this->company->account->is_verified_account) {
|
||||||
|
|
||||||
|
@ -236,6 +236,11 @@ class Client extends BaseModel implements HasLocalePreference
|
|||||||
return $this->hasMany(Task::class)->withTrashed();
|
return $this->hasMany(Task::class)->withTrashed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function payments()
|
||||||
|
{
|
||||||
|
return $this->hasMany(Payment::class)->withTrashed();
|
||||||
|
}
|
||||||
|
|
||||||
public function recurring_invoices()
|
public function recurring_invoices()
|
||||||
{
|
{
|
||||||
return $this->hasMany(RecurringInvoice::class)->withTrashed();
|
return $this->hasMany(RecurringInvoice::class)->withTrashed();
|
||||||
@ -627,11 +632,6 @@ class Client extends BaseModel implements HasLocalePreference
|
|||||||
return $defaults;
|
return $defaults;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function payments()
|
|
||||||
{
|
|
||||||
return $this->hasMany(Payment::class)->withTrashed();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function timezone_offset()
|
public function timezone_offset()
|
||||||
{
|
{
|
||||||
$offset = 0;
|
$offset = 0;
|
||||||
|
@ -15,6 +15,7 @@ use App\Models\Invoice;
|
|||||||
use App\Services\AbstractService;
|
use App\Services\AbstractService;
|
||||||
use App\Utils\Ninja;
|
use App\Utils\Ninja;
|
||||||
use App\Utils\Traits\GeneratesCounter;
|
use App\Utils\Traits\GeneratesCounter;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
class HandleRestore extends AbstractService
|
class HandleRestore extends AbstractService
|
||||||
{
|
{
|
||||||
@ -24,6 +25,10 @@ class HandleRestore extends AbstractService
|
|||||||
|
|
||||||
private $payment_total = 0;
|
private $payment_total = 0;
|
||||||
|
|
||||||
|
private $total_payments = 0;
|
||||||
|
|
||||||
|
private $adjustment_amount = 0;
|
||||||
|
|
||||||
public function __construct(Invoice $invoice)
|
public function __construct(Invoice $invoice)
|
||||||
{
|
{
|
||||||
$this->invoice = $invoice;
|
$this->invoice = $invoice;
|
||||||
@ -47,16 +52,90 @@ class HandleRestore extends AbstractService
|
|||||||
//adjust ledger balance
|
//adjust ledger balance
|
||||||
$this->invoice->ledger()->updateInvoiceBalance($this->invoice->balance, "Restored invoice {$this->invoice->number}")->save();
|
$this->invoice->ledger()->updateInvoiceBalance($this->invoice->balance, "Restored invoice {$this->invoice->number}")->save();
|
||||||
|
|
||||||
$this->invoice->client->service()->updateBalance($this->invoice->balance)->save();
|
$this->invoice->client
|
||||||
|
->service()
|
||||||
|
->updateBalance($this->invoice->balance)
|
||||||
|
->updatePaidToDate($this->invoice->paid_to_date)
|
||||||
|
->save();
|
||||||
|
|
||||||
$this->windBackInvoiceNumber();
|
$this->windBackInvoiceNumber();
|
||||||
|
|
||||||
$this->invoice->is_deleted = false;
|
$this->invoice->is_deleted = false;
|
||||||
$this->invoice->save();
|
$this->invoice->save();
|
||||||
|
|
||||||
|
$this->restorePaymentables()
|
||||||
|
->setAdjustmentAmount()
|
||||||
|
->adjustPayments();
|
||||||
|
|
||||||
return $this->invoice;
|
return $this->invoice;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Touches all paymentables as deleted */
|
||||||
|
private function restorePaymentables()
|
||||||
|
{
|
||||||
|
$this->invoice->payments->each(function ($payment) {
|
||||||
|
$payment->paymentables()
|
||||||
|
->where('paymentable_type', '=', 'invoices')
|
||||||
|
->where('paymentable_id', $this->invoice->id)
|
||||||
|
->update(['deleted_at' => false]);
|
||||||
|
});
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private function setAdjustmentAmount()
|
||||||
|
{
|
||||||
|
foreach ($this->invoice->payments as $payment) {
|
||||||
|
$this->adjustment_amount += $payment->paymentables
|
||||||
|
->where('paymentable_type', '=', 'invoices')
|
||||||
|
->where('paymentable_id', $this->invoice->id)
|
||||||
|
->sum(DB::raw('amount'));
|
||||||
|
|
||||||
|
$this->adjustment_amount += $payment->paymentables
|
||||||
|
->where('paymentable_type', '=', 'invoices')
|
||||||
|
->where('paymentable_id', $this->invoice->id)
|
||||||
|
->sum(DB::raw('refunded'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->total_payments = $this->invoice->payments->sum('amount') - $this->invoice->payments->sum('refunded');
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function adjustPayments()
|
||||||
|
{
|
||||||
|
//if total payments = adjustment amount - that means we need to delete the payments as well.
|
||||||
|
|
||||||
|
if ($this->adjustment_amount == $this->total_payments) {
|
||||||
|
$this->invoice->payments()->update(['payments.deleted_at' => null, 'payments.is_deleted' => false]);
|
||||||
|
} else {
|
||||||
|
|
||||||
|
//adjust payments down by the amount applied to the invoice payment.
|
||||||
|
|
||||||
|
$this->invoice->payments->each(function ($payment) {
|
||||||
|
$payment_adjustment = $payment->paymentables
|
||||||
|
->where('paymentable_type', '=', 'invoices')
|
||||||
|
->where('paymentable_id', $this->invoice->id)
|
||||||
|
->sum(DB::raw('amount'));
|
||||||
|
|
||||||
|
$payment_adjustment -= $payment->paymentables
|
||||||
|
->where('paymentable_type', '=', 'invoices')
|
||||||
|
->where('paymentable_id', $this->invoice->id)
|
||||||
|
->sum(DB::raw('refunded'));
|
||||||
|
|
||||||
|
$payment->amount += $payment_adjustment;
|
||||||
|
$payment->applied += $payment_adjustment;
|
||||||
|
$payment->is_deleted = false;
|
||||||
|
$payment->restore();
|
||||||
|
$payment->save();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private function windBackInvoiceNumber()
|
private function windBackInvoiceNumber()
|
||||||
{
|
{
|
||||||
$findme = '_'.ctrans('texts.deleted');
|
$findme = '_'.ctrans('texts.deleted');
|
||||||
|
@ -394,7 +394,7 @@ class Design extends BaseDesign
|
|||||||
public function productTable(): array
|
public function productTable(): array
|
||||||
{
|
{
|
||||||
$product_items = collect($this->entity->line_items)->filter(function ($item) {
|
$product_items = collect($this->entity->line_items)->filter(function ($item) {
|
||||||
return $item->type_id == 1 || $item->type_id == 6;
|
return $item->type_id == 1 || $item->type_id == 6 || $item->type_id == 5;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (count($product_items) == 0) {
|
if (count($product_items) == 0) {
|
||||||
|
@ -282,9 +282,9 @@ trait MakesInvoiceValues
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($table_type == '$task' && $item->type_id != 2) {
|
if ($table_type == '$task' && $item->type_id != 2) {
|
||||||
if ($item->type_id != 4 && $item->type_id != 5) {
|
// if ($item->type_id != 4 && $item->type_id != 5) {
|
||||||
continue;
|
continue;
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
$helpers = new Helpers();
|
$helpers = new Helpers();
|
||||||
|
@ -136,7 +136,6 @@
|
|||||||
"if [ \"${IS_DOCKER:-false}\" != \"true\" ]; then vendor/bin/snappdf download; fi"
|
"if [ \"${IS_DOCKER:-false}\" != \"true\" ]; then vendor/bin/snappdf download; fi"
|
||||||
],
|
],
|
||||||
"post-update-cmd": [
|
"post-update-cmd": [
|
||||||
"vendor/bin/snappdf download",
|
|
||||||
"@php artisan vendor:publish --tag=laravel-assets --ansi --force"
|
"@php artisan vendor:publish --tag=laravel-assets --ansi --force"
|
||||||
],
|
],
|
||||||
"post-root-package-install": [
|
"post-root-package-install": [
|
||||||
|
@ -14,8 +14,8 @@ return [
|
|||||||
'require_https' => env('REQUIRE_HTTPS', true),
|
'require_https' => env('REQUIRE_HTTPS', true),
|
||||||
'app_url' => rtrim(env('APP_URL', ''), '/'),
|
'app_url' => rtrim(env('APP_URL', ''), '/'),
|
||||||
'app_domain' => env('APP_DOMAIN', 'invoicing.co'),
|
'app_domain' => env('APP_DOMAIN', 'invoicing.co'),
|
||||||
'app_version' => '5.5.11',
|
'app_version' => '5.5.12',
|
||||||
'app_tag' => '5.5.11',
|
'app_tag' => '5.5.12',
|
||||||
'minimum_client_version' => '5.0.16',
|
'minimum_client_version' => '5.0.16',
|
||||||
'terms_version' => '1.0.1',
|
'terms_version' => '1.0.1',
|
||||||
'api_secret' => env('API_SECRET', ''),
|
'api_secret' => env('API_SECRET', ''),
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
</script>
|
</script>
|
||||||
<script>
|
<script>
|
||||||
window.flutterConfiguration = {
|
window.flutterConfiguration = {
|
||||||
canvasKitBaseUrl: "/canvaskit/"
|
canvasKitBaseUrl: "{{config('ninja.app_url')}}/canvaskit/"
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
|
@ -15,6 +15,7 @@ use App\Factory\InvoiceItemFactory;
|
|||||||
use App\Models\Client;
|
use App\Models\Client;
|
||||||
use App\Models\Invoice;
|
use App\Models\Invoice;
|
||||||
use App\Models\Payment;
|
use App\Models\Payment;
|
||||||
|
use App\Repositories\InvoiceRepository;
|
||||||
use App\Utils\Traits\MakesHash;
|
use App\Utils\Traits\MakesHash;
|
||||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||||
use Illuminate\Routing\Middleware\ThrottleRequests;
|
use Illuminate\Routing\Middleware\ThrottleRequests;
|
||||||
@ -41,6 +42,150 @@ class DeleteInvoiceTest extends TestCase
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testDeleteAndRestoreInvoice()
|
||||||
|
{
|
||||||
|
//create an invoice for 36000 with a partial of 6000
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'name' => 'A Nice Client - About to be deleted',
|
||||||
|
];
|
||||||
|
|
||||||
|
$response = $this->withHeaders([
|
||||||
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
|
'X-API-TOKEN' => $this->token,
|
||||||
|
])->post('/api/v1/clients', $data);
|
||||||
|
|
||||||
|
$response->assertStatus(200);
|
||||||
|
|
||||||
|
$arr = $response->json();
|
||||||
|
|
||||||
|
$client_hash_id = $arr['data']['id'];
|
||||||
|
$client = Client::find($this->decodePrimaryKey($client_hash_id));
|
||||||
|
|
||||||
|
$this->assertEquals($client->balance, 0);
|
||||||
|
$this->assertEquals($client->paid_to_date, 0);
|
||||||
|
|
||||||
|
$line_items = [];
|
||||||
|
|
||||||
|
$item = InvoiceItemFactory::create();
|
||||||
|
$item->quantity = 1;
|
||||||
|
$item->cost = 36000;
|
||||||
|
|
||||||
|
$line_items[] = (array) $item;
|
||||||
|
|
||||||
|
$invoice = [
|
||||||
|
'status_id' => 1,
|
||||||
|
'number' => '',
|
||||||
|
'discount' => 0,
|
||||||
|
'is_amount_discount' => 1,
|
||||||
|
'po_number' => '3434343',
|
||||||
|
'public_notes' => 'notes',
|
||||||
|
'is_deleted' => 0,
|
||||||
|
'partial' => 6000,
|
||||||
|
'custom_value1' => 0,
|
||||||
|
'custom_value2' => 0,
|
||||||
|
'custom_value3' => 0,
|
||||||
|
'custom_value4' => 0,
|
||||||
|
'client_id' => $client_hash_id,
|
||||||
|
'line_items' => (array) $line_items,
|
||||||
|
];
|
||||||
|
|
||||||
|
$response = $this->withHeaders([
|
||||||
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
|
'X-API-TOKEN' => $this->token,
|
||||||
|
])->post('/api/v1/invoices/', $invoice)
|
||||||
|
->assertStatus(200);
|
||||||
|
|
||||||
|
$arr = $response->json();
|
||||||
|
|
||||||
|
$invoice_one_hashed_id = $arr['data']['id'];
|
||||||
|
|
||||||
|
$invoice = Invoice::find($this->decodePrimaryKey($invoice_one_hashed_id));
|
||||||
|
|
||||||
|
$invoice = $invoice->service()->markSent()->save();
|
||||||
|
|
||||||
|
$this->assertEquals(6000, $invoice->partial);
|
||||||
|
$this->assertEquals(36000, $invoice->amount);
|
||||||
|
|
||||||
|
|
||||||
|
// apply a payment of 6000
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'amount' => 6000,
|
||||||
|
'client_id' => $client->hashed_id,
|
||||||
|
'invoices' => [
|
||||||
|
[
|
||||||
|
'invoice_id' => $invoice->hashed_id,
|
||||||
|
'amount' => 6000,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'date' => '2019/12/12',
|
||||||
|
];
|
||||||
|
|
||||||
|
try {
|
||||||
|
$response = $this->withHeaders([
|
||||||
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
|
'X-API-TOKEN' => $this->token,
|
||||||
|
])->post('/api/v1/payments?include=invoices', $data);
|
||||||
|
} catch (ValidationException $e) {
|
||||||
|
$message = json_decode($e->validator->getMessageBag(), 1);
|
||||||
|
$this->assertNotNull($message);
|
||||||
|
}
|
||||||
|
|
||||||
|
$response->assertStatus(200);
|
||||||
|
|
||||||
|
$arr = $response->json();
|
||||||
|
|
||||||
|
$payment_id = $arr['data']['id'];
|
||||||
|
|
||||||
|
$payment = Payment::withTrashed()->whereId($this->decodePrimaryKey($payment_id))->first();
|
||||||
|
|
||||||
|
$this->assertEquals(6000, $payment->amount);
|
||||||
|
$this->assertEquals(6000, $payment->applied);
|
||||||
|
|
||||||
|
$this->assertEquals(6000, $payment->client->paid_to_date);
|
||||||
|
|
||||||
|
$invoice = $invoice->fresh();
|
||||||
|
|
||||||
|
$this->assertEquals(30000, $invoice->balance);
|
||||||
|
$this->assertEquals(6000, $invoice->paid_to_date);
|
||||||
|
|
||||||
|
//delete the invoice an inspect the balances
|
||||||
|
|
||||||
|
$invoice_repo = new InvoiceRepository();
|
||||||
|
|
||||||
|
$invoice = $invoice_repo->delete($invoice);
|
||||||
|
$invoice = $invoice->fresh();
|
||||||
|
|
||||||
|
$this->assertTrue($invoice->is_deleted);
|
||||||
|
|
||||||
|
$payment = $payment->fresh();
|
||||||
|
|
||||||
|
$this->assertTrue($payment->is_deleted);
|
||||||
|
$this->assertEquals(4, $payment->status_id);
|
||||||
|
|
||||||
|
$client->fresh();
|
||||||
|
|
||||||
|
$this->assertEquals(0, $client->balance);
|
||||||
|
$this->assertEquals(0, $client->paid_to_date);
|
||||||
|
|
||||||
|
//restore the invoice. this should also rehydrate the payments and restore the correct paid to dates on the client record
|
||||||
|
|
||||||
|
$invoice_repo->restore($invoice);
|
||||||
|
$invoice = $invoice->fresh();
|
||||||
|
$client = $client->fresh();
|
||||||
|
$payment = $payment->fresh();
|
||||||
|
|
||||||
|
$this->assertEquals(30000, $invoice->balance);
|
||||||
|
$this->assertEquals(6000, $invoice->paid_to_date);
|
||||||
|
$this->assertEquals(6000, $client->paid_to_date);
|
||||||
|
$this->assertEquals(30000, $client->balance);
|
||||||
|
$this->assertEquals(6000, $payment->amount);
|
||||||
|
$this->assertFalse($payment->is_deleted);
|
||||||
|
$this->assertNull($payment->deleted_at);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public function testInvoiceDeletionAfterCancellation()
|
public function testInvoiceDeletionAfterCancellation()
|
||||||
{
|
{
|
||||||
$data = [
|
$data = [
|
||||||
|
@ -46,6 +46,18 @@ class InvoiceTest extends TestCase
|
|||||||
$this->makeTestData();
|
$this->makeTestData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function testInvoiceArchiveAction()
|
||||||
|
{
|
||||||
|
|
||||||
|
$response = $this->withHeaders([
|
||||||
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
|
'X-API-TOKEN' => $this->token,
|
||||||
|
])->get('/api/v1/invoices/'.$this->invoice->hashed_id.'/archive',)
|
||||||
|
->assertStatus(200);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public function testMarkingDeletedInvoiceAsSent()
|
public function testMarkingDeletedInvoiceAsSent()
|
||||||
{
|
{
|
||||||
Client::factory()->create(['user_id' => $this->user->id, 'company_id' => $this->company->id])->each(function ($c) {
|
Client::factory()->create(['user_id' => $this->user->id, 'company_id' => $this->company->id])->each(function ($c) {
|
||||||
@ -290,4 +302,6 @@ class InvoiceTest extends TestCase
|
|||||||
])->post('/api/v1/invoices/', $data)
|
])->post('/api/v1/invoices/', $data)
|
||||||
->assertStatus(200);
|
->assertStatus(200);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -42,6 +42,97 @@ class TaskApiTest extends TestCase
|
|||||||
Model::reguard();
|
Model::reguard();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testTimeLogValidation()
|
||||||
|
{
|
||||||
|
$data = [
|
||||||
|
'timelog' => $this->faker->firstName(),
|
||||||
|
];
|
||||||
|
|
||||||
|
try {
|
||||||
|
$response = $this->withHeaders([
|
||||||
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
|
'X-API-TOKEN' => $this->token,
|
||||||
|
])->post('/api/v1/tasks', $data);
|
||||||
|
|
||||||
|
$arr = $response->json();
|
||||||
|
} catch (ValidationException $e) {
|
||||||
|
$response->assertStatus(302);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testTimeLogValidation1()
|
||||||
|
{
|
||||||
|
$data = [
|
||||||
|
'timelog' => [[1,2],[3,4]],
|
||||||
|
];
|
||||||
|
|
||||||
|
$response = $this->withHeaders([
|
||||||
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
|
'X-API-TOKEN' => $this->token,
|
||||||
|
])->post('/api/v1/tasks', $data);
|
||||||
|
|
||||||
|
$arr = $response->json();
|
||||||
|
$response->assertStatus(200);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testTimeLogValidation2()
|
||||||
|
{
|
||||||
|
$data = [
|
||||||
|
'timelog' => [],
|
||||||
|
];
|
||||||
|
|
||||||
|
$response = $this->withHeaders([
|
||||||
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
|
'X-API-TOKEN' => $this->token,
|
||||||
|
])->post('/api/v1/tasks', $data);
|
||||||
|
|
||||||
|
$arr = $response->json();
|
||||||
|
$response->assertStatus(200);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testTimeLogValidation3()
|
||||||
|
{
|
||||||
|
$data = [
|
||||||
|
'timelog' => [["a","b"],["c","d"]],
|
||||||
|
];
|
||||||
|
|
||||||
|
try {
|
||||||
|
$response = $this->withHeaders([
|
||||||
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
|
'X-API-TOKEN' => $this->token,
|
||||||
|
])->post('/api/v1/tasks', $data);
|
||||||
|
|
||||||
|
$arr = $response->json();
|
||||||
|
} catch (ValidationException $e) {
|
||||||
|
$response->assertStatus(302);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testTimeLogValidation4()
|
||||||
|
{
|
||||||
|
$data = [
|
||||||
|
'timelog' => [[1,2],[3,0]],
|
||||||
|
];
|
||||||
|
|
||||||
|
$response = $this->withHeaders([
|
||||||
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
|
'X-API-TOKEN' => $this->token,
|
||||||
|
])->post('/api/v1/tasks', $data);
|
||||||
|
|
||||||
|
$arr = $response->json();
|
||||||
|
$response->assertStatus(200);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public function testStartTask()
|
public function testStartTask()
|
||||||
{
|
{
|
||||||
$log = [
|
$log = [
|
||||||
@ -76,6 +167,7 @@ class TaskApiTest extends TestCase
|
|||||||
$data = [
|
$data = [
|
||||||
'description' => $this->faker->firstName(),
|
'description' => $this->faker->firstName(),
|
||||||
'number' => 'taskynumber',
|
'number' => 'taskynumber',
|
||||||
|
'client_id' => $this->client->id,
|
||||||
];
|
];
|
||||||
|
|
||||||
$response = $this->withHeaders([
|
$response = $this->withHeaders([
|
||||||
@ -126,6 +218,24 @@ class TaskApiTest extends TestCase
|
|||||||
$this->assertNotEmpty($arr['data']['number']);
|
$this->assertNotEmpty($arr['data']['number']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testTaskWithBadClientId()
|
||||||
|
{
|
||||||
|
$data = [
|
||||||
|
'client_id' => $this->faker->firstName(),
|
||||||
|
];
|
||||||
|
|
||||||
|
try {
|
||||||
|
$response = $this->withHeaders([
|
||||||
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
|
'X-API-TOKEN' => $this->token,
|
||||||
|
])->post('/api/v1/tasks', $data);
|
||||||
|
$arr = $response->json();
|
||||||
|
} catch (ValidationException $e) {
|
||||||
|
$response->assertStatus(302);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public function testTaskPostWithActionStart()
|
public function testTaskPostWithActionStart()
|
||||||
{
|
{
|
||||||
$data = [
|
$data = [
|
||||||
|
@ -13,6 +13,7 @@ namespace Tests\Unit;
|
|||||||
|
|
||||||
use App\Factory\InvoiceItemFactory;
|
use App\Factory\InvoiceItemFactory;
|
||||||
use App\Helpers\Invoice\InvoiceSum;
|
use App\Helpers\Invoice\InvoiceSum;
|
||||||
|
use App\Helpers\Invoice\InvoiceSumInclusive;
|
||||||
use App\Models\Invoice;
|
use App\Models\Invoice;
|
||||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||||
use Tests\MockAccountData;
|
use Tests\MockAccountData;
|
||||||
@ -41,11 +42,47 @@ class InvoiceTest extends TestCase
|
|||||||
|
|
||||||
$this->invoice->line_items = $this->buildLineItems();
|
$this->invoice->line_items = $this->buildLineItems();
|
||||||
|
|
||||||
$this->invoice->usesinclusive_taxes = true;
|
$this->invoice->uses_inclusive_taxes = true;
|
||||||
|
|
||||||
$this->invoice_calc = new InvoiceSum($this->invoice);
|
$this->invoice_calc = new InvoiceSum($this->invoice);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testInclusiveRounding()
|
||||||
|
{
|
||||||
|
$this->invoice->line_items = [];
|
||||||
|
$this->invoice->discount = 0;
|
||||||
|
$this->invoice->uses_inclusive_taxes = true;
|
||||||
|
$this->invoice->save();
|
||||||
|
|
||||||
|
|
||||||
|
$item = InvoiceItemFactory::create();
|
||||||
|
$item->quantity = 1;
|
||||||
|
$item->cost = 50;
|
||||||
|
$item->tax_name1 = "taxy";
|
||||||
|
$item->tax_rate1 = 19;
|
||||||
|
|
||||||
|
$line_items[] = $item;
|
||||||
|
|
||||||
|
$item = InvoiceItemFactory::create();
|
||||||
|
$item->quantity = 1;
|
||||||
|
$item->cost = 50;
|
||||||
|
$item->tax_name1 = "taxy";
|
||||||
|
$item->tax_rate1 = 19;
|
||||||
|
|
||||||
|
$line_items[] = $item;
|
||||||
|
|
||||||
|
$this->invoice->line_items = $line_items;
|
||||||
|
$this->invoice->save();
|
||||||
|
|
||||||
|
$invoice_calc = new InvoiceSumInclusive($this->invoice);
|
||||||
|
|
||||||
|
$invoice_calc->build();
|
||||||
|
// $this->invoice->save();
|
||||||
|
|
||||||
|
$this->assertEquals($invoice_calc->getTotalTaxes(), 15.96);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private function buildLineItems()
|
private function buildLineItems()
|
||||||
{
|
{
|
||||||
$line_items = [];
|
$line_items = [];
|
||||||
|
Loading…
Reference in New Issue
Block a user