mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-09 03:14:30 -04:00
Merge pull request #4754 from turbo124/v5-develop
Fixes and improvements
This commit is contained in:
commit
6bc2ca470c
@ -12,7 +12,7 @@ services:
|
|||||||
group: deprecated-2017Q4
|
group: deprecated-2017Q4
|
||||||
|
|
||||||
php:
|
php:
|
||||||
- 7.3
|
# - 7.3
|
||||||
- 7.4
|
- 7.4
|
||||||
# - nightly
|
# - nightly
|
||||||
|
|
||||||
@ -38,7 +38,7 @@ env:
|
|||||||
before_install:
|
before_install:
|
||||||
# set GitHub token and update composer
|
# set GitHub token and update composer
|
||||||
- if [ -n "$GH_TOKEN" ]; then composer config github-oauth.github.com ${GH_TOKEN}; fi;
|
- if [ -n "$GH_TOKEN" ]; then composer config github-oauth.github.com ${GH_TOKEN}; fi;
|
||||||
- composer self-update && composer -V
|
- composer self-update 1.10.19 && composer -V
|
||||||
# - export USE_ZEND_ALLOC=0
|
# - export USE_ZEND_ALLOC=0
|
||||||
- rvm use 1.9.3 --install --fuzzy
|
- rvm use 1.9.3 --install --fuzzy
|
||||||
- cp .env.travis .env
|
- cp .env.travis .env
|
||||||
@ -77,7 +77,7 @@ before_script:
|
|||||||
|
|
||||||
script:
|
script:
|
||||||
- php ./vendor/bin/phpunit --debug --verbose --coverage-clover=coverage.xml
|
- php ./vendor/bin/phpunit --debug --verbose --coverage-clover=coverage.xml
|
||||||
- php artisan dusk
|
#- php artisan dusk
|
||||||
#- npm test
|
#- npm test
|
||||||
after_success:
|
after_success:
|
||||||
- bash <(curl -s https://codecov.io/bash)
|
- bash <(curl -s https://codecov.io/bash)
|
||||||
|
@ -1 +1 @@
|
|||||||
5.0.52
|
5.0.53
|
@ -15,6 +15,7 @@ use App\Jobs\Cron\RecurringInvoicesCron;
|
|||||||
use App\Jobs\Ninja\AdjustEmailQuota;
|
use App\Jobs\Ninja\AdjustEmailQuota;
|
||||||
use App\Jobs\Ninja\CompanySizeCheck;
|
use App\Jobs\Ninja\CompanySizeCheck;
|
||||||
use App\Jobs\Util\ReminderJob;
|
use App\Jobs\Util\ReminderJob;
|
||||||
|
use App\Jobs\Util\SchedulerCheck;
|
||||||
use App\Jobs\Util\SendFailedEmails;
|
use App\Jobs\Util\SendFailedEmails;
|
||||||
use App\Jobs\Util\UpdateExchangeRates;
|
use App\Jobs\Util\UpdateExchangeRates;
|
||||||
use App\Jobs\Util\VersionCheck;
|
use App\Jobs\Util\VersionCheck;
|
||||||
@ -42,7 +43,6 @@ class Kernel extends ConsoleKernel
|
|||||||
protected function schedule(Schedule $schedule)
|
protected function schedule(Schedule $schedule)
|
||||||
{
|
{
|
||||||
|
|
||||||
//$schedule->job(new RecurringInvoicesCron)->hourly();
|
|
||||||
$schedule->job(new VersionCheck)->daily()->withoutOverlapping();
|
$schedule->job(new VersionCheck)->daily()->withoutOverlapping();
|
||||||
|
|
||||||
$schedule->command('ninja:check-data')->daily()->withoutOverlapping();
|
$schedule->command('ninja:check-data')->daily()->withoutOverlapping();
|
||||||
@ -57,13 +57,20 @@ class Kernel extends ConsoleKernel
|
|||||||
|
|
||||||
/* Run hosted specific jobs */
|
/* Run hosted specific jobs */
|
||||||
if (Ninja::isHosted()) {
|
if (Ninja::isHosted()) {
|
||||||
|
|
||||||
$schedule->job(new AdjustEmailQuota())->daily()->withoutOverlapping();
|
$schedule->job(new AdjustEmailQuota())->daily()->withoutOverlapping();
|
||||||
$schedule->job(new SendFailedEmails())->daily()->withoutOverlapping();
|
$schedule->job(new SendFailedEmails())->daily()->withoutOverlapping();
|
||||||
|
|
||||||
}
|
}
|
||||||
/* Run queue's in shared hosting with this*/
|
/* Run queue's with this*/
|
||||||
if (Ninja::isSelfHost()) {
|
if (Ninja::isSelfHost()) {
|
||||||
|
|
||||||
$schedule->command('queue:work')->everyMinute()->withoutOverlapping();
|
$schedule->command('queue:work')->everyMinute()->withoutOverlapping();
|
||||||
$schedule->command('queue:restart')->everyFiveMinutes()->withoutOverlapping(); //we need to add this as we are seeing cached queues mess up the system on first load.
|
|
||||||
|
//we need to add this as we are seeing cached queues mess up the system on first load.
|
||||||
|
$schedule->command('queue:restart')->everyFiveMinutes()->withoutOverlapping();
|
||||||
|
$schedule->job(new SchedulerCheck)->everyFiveMinutes()->withoutOverlapping();
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,6 +44,7 @@ class InvoiceFactory
|
|||||||
$invoice->custom_value4 = 0;
|
$invoice->custom_value4 = 0;
|
||||||
$invoice->amount = 0;
|
$invoice->amount = 0;
|
||||||
$invoice->balance = 0;
|
$invoice->balance = 0;
|
||||||
|
$invoice->paid_to_date = 0;
|
||||||
$invoice->partial = 0;
|
$invoice->partial = 0;
|
||||||
$invoice->user_id = $user_id;
|
$invoice->user_id = $user_id;
|
||||||
$invoice->company_id = $company_id;
|
$invoice->company_id = $company_id;
|
||||||
|
@ -50,6 +50,7 @@ class InvoiceToRecurringInvoiceFactory
|
|||||||
$recurring_invoice->last_sent_date = null;
|
$recurring_invoice->last_sent_date = null;
|
||||||
$recurring_invoice->next_send_date = null;
|
$recurring_invoice->next_send_date = null;
|
||||||
$recurring_invoice->remaining_cycles = 0;
|
$recurring_invoice->remaining_cycles = 0;
|
||||||
|
$recurring_invoice->paid_to_date = 0;
|
||||||
|
|
||||||
return $recurring_invoice;
|
return $recurring_invoice;
|
||||||
}
|
}
|
||||||
|
@ -45,6 +45,7 @@ class QuoteFactory
|
|||||||
$quote->partial = 0;
|
$quote->partial = 0;
|
||||||
$quote->user_id = $user_id;
|
$quote->user_id = $user_id;
|
||||||
$quote->company_id = $company_id;
|
$quote->company_id = $company_id;
|
||||||
|
$quote->paid_to_date = 0;
|
||||||
|
|
||||||
return $quote;
|
return $quote;
|
||||||
}
|
}
|
||||||
|
@ -49,6 +49,7 @@ class RecurringInvoiceFactory
|
|||||||
$invoice->last_sent_date = null;
|
$invoice->last_sent_date = null;
|
||||||
$invoice->next_send_date = null;
|
$invoice->next_send_date = null;
|
||||||
$invoice->remaining_cycles = 0;
|
$invoice->remaining_cycles = 0;
|
||||||
|
$invoice->paid_to_date = 0;
|
||||||
|
|
||||||
return $invoice;
|
return $invoice;
|
||||||
}
|
}
|
||||||
|
@ -50,6 +50,7 @@ class RecurringInvoiceToInvoiceFactory
|
|||||||
$invoice->recurring_id = $recurring_invoice->id;
|
$invoice->recurring_id = $recurring_invoice->id;
|
||||||
$invoice->client_id = $client->id;
|
$invoice->client_id = $client->id;
|
||||||
$invoice->auto_bill_enabled = $recurring_invoice->auto_bill_enabled;
|
$invoice->auto_bill_enabled = $recurring_invoice->auto_bill_enabled;
|
||||||
|
$invoice->paid_to_date = 0;
|
||||||
|
|
||||||
return $invoice;
|
return $invoice;
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,7 @@
|
|||||||
* @OA\Property(property="line_items", type="object", example="", description="_________"),
|
* @OA\Property(property="line_items", type="object", example="", description="_________"),
|
||||||
* @OA\Property(property="amount", type="number", format="float", example="10.00", description="_________"),
|
* @OA\Property(property="amount", type="number", format="float", example="10.00", description="_________"),
|
||||||
* @OA\Property(property="balance", type="number", format="float", example="10.00", description="_________"),
|
* @OA\Property(property="balance", type="number", format="float", example="10.00", description="_________"),
|
||||||
|
* @OA\Property(property="paid_to_date", type="number", format="float", example="10.00", description="_________"),
|
||||||
* @OA\Property(property="discount", type="number", format="float", example="10.00", description="_________"),
|
* @OA\Property(property="discount", type="number", format="float", example="10.00", description="_________"),
|
||||||
* @OA\Property(property="partial", type="number", format="float", example="10.00", description="_________"),
|
* @OA\Property(property="partial", type="number", format="float", example="10.00", description="_________"),
|
||||||
* @OA\Property(property="is_amount_discount", type="boolean", example=true, description="_________"),
|
* @OA\Property(property="is_amount_discount", type="boolean", example=true, description="_________"),
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
* @OA\Property(property="line_items", type="object", example="", description="_________"),
|
* @OA\Property(property="line_items", type="object", example="", description="_________"),
|
||||||
* @OA\Property(property="amount", type="number", format="float", example="10.00", description="_________"),
|
* @OA\Property(property="amount", type="number", format="float", example="10.00", description="_________"),
|
||||||
* @OA\Property(property="balance", type="number", format="float", example="10.00", description="_________"),
|
* @OA\Property(property="balance", type="number", format="float", example="10.00", description="_________"),
|
||||||
|
* @OA\Property(property="paid_to_date", type="number", format="float", example="10.00", description="_________"),
|
||||||
* @OA\Property(property="discount", type="number", format="float", example="10.00", description="_________"),
|
* @OA\Property(property="discount", type="number", format="float", example="10.00", description="_________"),
|
||||||
* @OA\Property(property="partial", type="number", format="float", example="10.00", description="_________"),
|
* @OA\Property(property="partial", type="number", format="float", example="10.00", description="_________"),
|
||||||
* @OA\Property(property="is_amount_discount", type="boolean", example=true, description="_________"),
|
* @OA\Property(property="is_amount_discount", type="boolean", example=true, description="_________"),
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
* @OA\Property(property="line_items", type="object", example="", description="_________"),
|
* @OA\Property(property="line_items", type="object", example="", description="_________"),
|
||||||
* @OA\Property(property="amount", type="number", format="float", example="10.00", description="_________"),
|
* @OA\Property(property="amount", type="number", format="float", example="10.00", description="_________"),
|
||||||
* @OA\Property(property="balance", type="number", format="float", example="10.00", description="_________"),
|
* @OA\Property(property="balance", type="number", format="float", example="10.00", description="_________"),
|
||||||
|
* @OA\Property(property="paid_to_date", type="number", format="float", example="10.00", description="_________"),
|
||||||
* @OA\Property(property="discount", type="number", format="float", example="10.00", description="_________"),
|
* @OA\Property(property="discount", type="number", format="float", example="10.00", description="_________"),
|
||||||
* @OA\Property(property="partial", type="number", format="float", example="10.00", description="_________"),
|
* @OA\Property(property="partial", type="number", format="float", example="10.00", description="_________"),
|
||||||
* @OA\Property(property="is_amount_discount", type="boolean", example=true, description="_________"),
|
* @OA\Property(property="is_amount_discount", type="boolean", example=true, description="_________"),
|
||||||
|
@ -435,11 +435,6 @@ class PaymentController extends BaseController
|
|||||||
*/
|
*/
|
||||||
public function destroy(DestroyPaymentRequest $request, Payment $payment)
|
public function destroy(DestroyPaymentRequest $request, Payment $payment)
|
||||||
{
|
{
|
||||||
// $payment->service()->deletePayment();
|
|
||||||
|
|
||||||
// $payment->is_deleted = true;
|
|
||||||
// $payment->save();
|
|
||||||
// $payment->delete();
|
|
||||||
|
|
||||||
$this->payment_repo->delete($payment);
|
$this->payment_repo->delete($payment);
|
||||||
|
|
||||||
|
@ -33,6 +33,6 @@ class ValidCompanyQuantity implements Rule
|
|||||||
*/
|
*/
|
||||||
public function message()
|
public function message()
|
||||||
{
|
{
|
||||||
return 'Limit of 10 companies per account.';
|
return ctrans('texts.company_limit_reached');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,6 +48,6 @@ class CreditsSumRule implements Rule
|
|||||||
*/
|
*/
|
||||||
public function message()
|
public function message()
|
||||||
{
|
{
|
||||||
return "Total credits applied cannot be MORE than total of invoices";
|
return ctrans('texts.credits_applied_validation');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ class UniqueCreditNumberRule implements Rule
|
|||||||
*/
|
*/
|
||||||
public function message()
|
public function message()
|
||||||
{
|
{
|
||||||
return 'Credit number already taken';
|
return ctrans('texts.credit_number_taken');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -44,7 +44,8 @@ class ValidCreditsRules implements Rule
|
|||||||
private function checkCreditsAreHomogenous()
|
private function checkCreditsAreHomogenous()
|
||||||
{
|
{
|
||||||
if (! array_key_exists('client_id', $this->input)) {
|
if (! array_key_exists('client_id', $this->input)) {
|
||||||
$this->error_msg = 'Client id is required';
|
|
||||||
|
$this->error_msg = ctrans('texts.client_id_required');
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -57,44 +58,32 @@ class ValidCreditsRules implements Rule
|
|||||||
$cred = Credit::find($this->decodePrimaryKey($credit['credit_id']));
|
$cred = Credit::find($this->decodePrimaryKey($credit['credit_id']));
|
||||||
|
|
||||||
if (! $cred) {
|
if (! $cred) {
|
||||||
$this->error_msg = 'Credit not found ';
|
$this->error_msg = ctrans('texts.credit_not_found');
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($cred->client_id != $this->input['client_id']) {
|
if ($cred->client_id != $this->input['client_id']) {
|
||||||
$this->error_msg = 'Selected invoices are not from a single client';
|
$this->error_msg = ctrans('texts.invoices_dont_match_client');
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! (array_unique($unique_array) == $unique_array)) {
|
if (! (array_unique($unique_array) == $unique_array)) {
|
||||||
$this->error_msg = 'Duplicate credits submitted.';
|
$this->error_msg = ctrans('texts.duplicate_credits_submitted');
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (count($this->input['credits']) >= 1 && count($this->input['invoices']) == 0) {
|
if (count($this->input['credits']) >= 1 && count($this->input['invoices']) == 0) {
|
||||||
$this->error_msg = 'You must have an invoice set when using a credit in a payment';
|
$this->error_msg = ctrans('texts.credit_with_no_invoice');
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (count($this->input['credits']) >= 1) {
|
if (count($this->input['credits']) >= 1) {
|
||||||
|
|
||||||
// $total_payments = $this->input['amount'] + array_sum(array_column($this->input['credits'], 'amount'));
|
|
||||||
|
|
||||||
// nlog(print_r($this->input,1));
|
|
||||||
// nlog("total payments = {$total_payments}");
|
|
||||||
// nlog("total credits available = " . array_sum(array_column($this->input['credits'], 'amount')));
|
|
||||||
// nlog("total invoices payable = " . array_sum(array_column($this->input['invoices'], 'amount')));
|
|
||||||
|
|
||||||
// if($total_payments > array_sum(array_column($this->input['invoices'], 'amount'))){
|
|
||||||
|
|
||||||
// $this->error_msg = "Sum of total payments and credits is greater than the total of invoices";
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -41,7 +41,7 @@ class UniqueExpenseNumberRule implements Rule
|
|||||||
*/
|
*/
|
||||||
public function message()
|
public function message()
|
||||||
{
|
{
|
||||||
return 'Expense number already taken';
|
return ctrans('texts.expense_number_taken');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -57,20 +57,7 @@ class UniqueExpenseNumberRule implements Rule
|
|||||||
->where('number', $this->input['number'])
|
->where('number', $this->input['number'])
|
||||||
->withTrashed();
|
->withTrashed();
|
||||||
|
|
||||||
// if(isset($this->input['client_id']))
|
|
||||||
// $expense->where('client_id', $this->input['client_id']);
|
|
||||||
|
|
||||||
return $expense->exists();
|
return $expense->exists();
|
||||||
|
|
||||||
// $expense = Expense::where('client_id', $this->input['client_id'])
|
|
||||||
// ->where('number', $this->input['number'])
|
|
||||||
// ->withTrashed()
|
|
||||||
// ->exists();
|
|
||||||
|
|
||||||
// if ($expense) {
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ class UniqueInvoiceNumberRule implements Rule
|
|||||||
*/
|
*/
|
||||||
public function message()
|
public function message()
|
||||||
{
|
{
|
||||||
return 'Invoice number already taken';
|
return ctrans('texts.invoice_number_taken');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -44,7 +44,8 @@ class ValidInvoicesRules implements Rule
|
|||||||
private function checkInvoicesAreHomogenous()
|
private function checkInvoicesAreHomogenous()
|
||||||
{
|
{
|
||||||
if (! array_key_exists('client_id', $this->input)) {
|
if (! array_key_exists('client_id', $this->input)) {
|
||||||
$this->error_msg = 'Client id is required';
|
|
||||||
|
$this->error_msg = ctrans('texts.client_id_required');
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -60,14 +61,14 @@ class ValidInvoicesRules implements Rule
|
|||||||
|
|
||||||
if (! $inv) {
|
if (! $inv) {
|
||||||
|
|
||||||
$this->error_msg = 'Invoice not found ';
|
$this->error_msg = ctrans('texts.invoice_not_found');
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($inv->client_id != $this->input['client_id']) {
|
if ($inv->client_id != $this->input['client_id']) {
|
||||||
|
|
||||||
$this->error_msg = 'Selected invoices are not from a single client';
|
$this->error_msg = ctrans('texts.invoices_dont_match_client');
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -83,14 +84,14 @@ class ValidInvoicesRules implements Rule
|
|||||||
}
|
}
|
||||||
else if($invoice['amount'] > $inv->balance) {
|
else if($invoice['amount'] > $inv->balance) {
|
||||||
|
|
||||||
$this->error_msg = 'Amount cannot be greater than invoice balance';
|
$this->error_msg = ctrans('texts.amount_greater_than_balance');
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! (array_unique($unique_array) == $unique_array)) {
|
if (! (array_unique($unique_array) == $unique_array)) {
|
||||||
$this->error_msg = 'Duplicate invoices submitted.';
|
$this->error_msg = ctrans('texts.duplicate_invoices_submitted');
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ class ValidRefundableRequest implements Rule
|
|||||||
public function passes($attribute, $value)
|
public function passes($attribute, $value)
|
||||||
{
|
{
|
||||||
if (! array_key_exists('id', $this->input)) {
|
if (! array_key_exists('id', $this->input)) {
|
||||||
$this->error_msg = 'Payment `id` required.';
|
$this->error_msg = ctrans('texts.payment_id_required');
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -49,7 +49,7 @@ class ValidRefundableRequest implements Rule
|
|||||||
$payment = Payment::whereId($this->input['id'])->first();
|
$payment = Payment::whereId($this->input['id'])->first();
|
||||||
|
|
||||||
if (! $payment) {
|
if (! $payment) {
|
||||||
$this->error_msg = 'Unable to retrieve specified payment';
|
$this->error_msg = ctrans('texts.unable_to_retrieve_payment');
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -57,31 +57,17 @@ class ValidRefundableRequest implements Rule
|
|||||||
$request_invoices = request()->has('invoices') ? $this->input['invoices'] : [];
|
$request_invoices = request()->has('invoices') ? $this->input['invoices'] : [];
|
||||||
$request_credits = request()->has('credits') ? $this->input['credits'] : [];
|
$request_credits = request()->has('credits') ? $this->input['credits'] : [];
|
||||||
|
|
||||||
// foreach($request_invoices as $key => $value)
|
|
||||||
// $request_invoices[$key]['invoice_id'] = $this->decodePrimaryKey($value['invoice_id']);
|
|
||||||
|
|
||||||
// foreach($request_credits as $key => $value)
|
|
||||||
// $request_credits[$key]['credit_id'] = $this->decodePrimaryKey($value['credit_id']);
|
|
||||||
|
|
||||||
if ($payment->invoices()->exists()) {
|
if ($payment->invoices()->exists()) {
|
||||||
foreach ($payment->invoices as $paymentable_invoice) {
|
foreach ($payment->invoices as $paymentable_invoice) {
|
||||||
$this->checkInvoice($paymentable_invoice, $request_invoices);
|
$this->checkInvoice($paymentable_invoice, $request_invoices);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if($payment->credits()->exists())
|
|
||||||
// {
|
|
||||||
// foreach($payment->credits as $paymentable_credit)
|
|
||||||
// $this->checkCredit($paymentable_credit, $request_credits);
|
|
||||||
// }
|
|
||||||
|
|
||||||
foreach ($request_invoices as $request_invoice) {
|
foreach ($request_invoices as $request_invoice) {
|
||||||
$this->checkInvoiceIsPaymentable($request_invoice, $payment);
|
$this->checkInvoiceIsPaymentable($request_invoice, $payment);
|
||||||
}
|
}
|
||||||
|
|
||||||
// foreach($request_credits as $request_credit)
|
|
||||||
// $this->checkCreditIsPaymentable($request_credit, $payment);
|
|
||||||
|
|
||||||
if (strlen($this->error_msg) > 0) {
|
if (strlen($this->error_msg) > 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -97,12 +83,12 @@ class ValidRefundableRequest implements Rule
|
|||||||
$paymentable_invoice = $payment->invoices->where('id', $invoice->id)->first();
|
$paymentable_invoice = $payment->invoices->where('id', $invoice->id)->first();
|
||||||
|
|
||||||
if (! $paymentable_invoice) {
|
if (! $paymentable_invoice) {
|
||||||
$this->error_msg = 'Invoice id '.$invoice->hashed_id.' is not related to this payment';
|
$this->error_msg = ctrans('texts.invoice_not_related_to_payment', ['invoice' => $invoice->hashed_id]);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$this->error_msg = 'Invoice id '.$invoice->hashed_id.' is not related to this payment';
|
$this->error_msg = ctrans('texts.invoice_not_related_to_payment', ['invoice' => $invoice->hashed_id]);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -116,12 +102,12 @@ class ValidRefundableRequest implements Rule
|
|||||||
$paymentable_credit = $payment->credits->where('id', $credit->id)->first();
|
$paymentable_credit = $payment->credits->where('id', $credit->id)->first();
|
||||||
|
|
||||||
if (! $paymentable_invoice) {
|
if (! $paymentable_invoice) {
|
||||||
$this->error_msg = 'Credit id '.$credit->hashed_id.' is not related to this payment';
|
$this->error_msg = ctrans('texts.credit_not_related_to_payment', ['credit' => $credit->hashed_id]);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$this->error_msg = 'Credit id '.$credit->hashed_id.' is not related to this payment';
|
$this->error_msg = ctrans('texts.credit_not_related_to_payment', ['credit' => $credit->hashed_id]);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -140,7 +126,7 @@ class ValidRefundableRequest implements Rule
|
|||||||
if ($request_invoice['amount'] > $refundable_amount) {
|
if ($request_invoice['amount'] > $refundable_amount) {
|
||||||
$invoice = $paymentable;
|
$invoice = $paymentable;
|
||||||
|
|
||||||
$this->error_msg = 'Attempting to refund more than allowed for invoice id '.$invoice->hashed_id.', maximum refundable amount is '.$refundable_amount;
|
$this->error_msg = ctrans('texts.max_refundable_invoice', ['invoice' => $invoice->hashed_id, 'amount' => $refundable_amount]);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -148,7 +134,7 @@ class ValidRefundableRequest implements Rule
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (! $record_found) {
|
if (! $record_found) {
|
||||||
$this->error_msg = 'Attempting to refund a payment with invoices attached, please specify valid invoice/s to be refunded.';
|
$this->error_msg = ctrans('texts.refund_without_invoices');
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -167,7 +153,7 @@ class ValidRefundableRequest implements Rule
|
|||||||
if ($request_credit['amount'] > $refundable_amount) {
|
if ($request_credit['amount'] > $refundable_amount) {
|
||||||
$credit = $paymentable;
|
$credit = $paymentable;
|
||||||
|
|
||||||
$this->error_msg = 'Attempting to refund more than allowed for credit '.$credit->number.', maximum refundable amount is '.$refundable_amount;
|
$this->error_msg = ctrans('texts.max_refundable_credit',['credit' => $credit->hashed_id, 'amount' => $refundable_amount]);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -175,7 +161,7 @@ class ValidRefundableRequest implements Rule
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (! $record_found) {
|
if (! $record_found) {
|
||||||
$this->error_msg = 'Attempting to refund a payment with credits attached, please specify valid credit/s to be refunded.';
|
$this->error_msg = ctrans('texts.refund_without_credits');
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,7 @@ class PaymentAmountsBalanceRule implements Rule
|
|||||||
*/
|
*/
|
||||||
public function message()
|
public function message()
|
||||||
{
|
{
|
||||||
return 'Amounts do not balance correctly.';
|
return ctrans('texts.amounts_do_not_balance');
|
||||||
}
|
}
|
||||||
|
|
||||||
private function calculateAmounts() :bool
|
private function calculateAmounts() :bool
|
||||||
@ -69,10 +69,8 @@ class PaymentAmountsBalanceRule implements Rule
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return true;
|
return true;
|
||||||
} // if no invoices are present, then this is an unapplied payment, let this pass validation!
|
}
|
||||||
|
|
||||||
// nlog("payment amounts = {$payment_amounts}");
|
|
||||||
// nlog("invoice amounts = {$invoice_amounts}");
|
|
||||||
|
|
||||||
return $payment_amounts >= $invoice_amounts;
|
return $payment_amounts >= $invoice_amounts;
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,7 @@ class PaymentAppliedValidAmount implements Rule
|
|||||||
*/
|
*/
|
||||||
public function message()
|
public function message()
|
||||||
{
|
{
|
||||||
return 'Insufficient applied amount remaining to cover payment.';
|
return ctrans('texts.insufficient_applied_amount_remaining');
|
||||||
}
|
}
|
||||||
|
|
||||||
private function calculateAmounts() :bool
|
private function calculateAmounts() :bool
|
||||||
|
@ -53,6 +53,6 @@ class ValidProjectForClient implements Rule
|
|||||||
*/
|
*/
|
||||||
public function message()
|
public function message()
|
||||||
{
|
{
|
||||||
return "Project client does not match entity client";
|
return ctrans('texts.project_client_do_not_match');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ class UniqueQuoteNumberRule implements Rule
|
|||||||
*/
|
*/
|
||||||
public function message()
|
public function message()
|
||||||
{
|
{
|
||||||
return 'Quote number already taken';
|
return ctrans('texts.quote_number_taken');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -41,7 +41,7 @@ class UniqueRecurringInvoiceNumberRule implements Rule
|
|||||||
*/
|
*/
|
||||||
public function message()
|
public function message()
|
||||||
{
|
{
|
||||||
return "Recurring Invoice number {$this->input['number']} already taken";
|
return ctrans('texts.recurring_invoice_number_taken', ['number' => $this->input['number']]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -40,7 +40,7 @@ class RelatedUserRule implements Rule
|
|||||||
*/
|
*/
|
||||||
public function message()
|
public function message()
|
||||||
{
|
{
|
||||||
return 'User not associated with this account';
|
return ctrans('texts.user_not_associated_with_account');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -37,26 +37,13 @@ class ValidCreditsPresentRule implements Rule
|
|||||||
*/
|
*/
|
||||||
public function message()
|
public function message()
|
||||||
{
|
{
|
||||||
return 'Insufficient balance on credit.';
|
return ctrans('texts.insufficient_credit_balance');
|
||||||
}
|
}
|
||||||
|
|
||||||
private function validCreditsPresent() :bool
|
private function validCreditsPresent() :bool
|
||||||
{
|
{
|
||||||
//todo need to ensure the clients credits are here not random ones!
|
//todo need to ensure the clients credits are here not random ones!
|
||||||
|
|
||||||
// if (request()->input('credits') && is_array(request()->input('credits'))) {
|
|
||||||
// foreach (request()->input('credits') as $credit) {
|
|
||||||
// $cred = Credit::find($this->decodePrimaryKey($credit['credit_id']));
|
|
||||||
|
|
||||||
// if (! $cred || $cred->balance == 0) {
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return true;
|
|
||||||
|
|
||||||
|
|
||||||
if (request()->input('credits') && is_array(request()->input('credits'))) {
|
if (request()->input('credits') && is_array(request()->input('credits'))) {
|
||||||
$credit_collection = Credit::whereIn('id', $this->transformKeys(array_column(request()->input('credits'), 'credit_id')))
|
$credit_collection = Credit::whereIn('id', $this->transformKeys(array_column(request()->input('credits'), 'credit_id')))
|
||||||
->where('balance', '>', 0)
|
->where('balance', '>', 0)
|
||||||
|
@ -40,7 +40,7 @@ class ValidPayableInvoicesRule implements Rule
|
|||||||
|
|
||||||
foreach ($invoices as $invoice) {
|
foreach ($invoices as $invoice) {
|
||||||
if (! $invoice->isPayable()) {
|
if (! $invoice->isPayable()) {
|
||||||
$this->error_msg = 'One or more of these invoices have been paid';
|
$this->error_msg = ctrans('texts.one_or_more_invoices_paid');
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ class ValidRefundableInvoices implements Rule
|
|||||||
public function passes($attribute, $value)
|
public function passes($attribute, $value)
|
||||||
{
|
{
|
||||||
if (! array_key_exists('id', $this->input)) {
|
if (! array_key_exists('id', $this->input)) {
|
||||||
$this->error_msg = 'Payment `id` required.';
|
$this->error_msg = ctrans('texts.payment_id_required');
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -48,17 +48,11 @@ class ValidRefundableInvoices implements Rule
|
|||||||
$payment = Payment::whereId($this->input['id'])->first();
|
$payment = Payment::whereId($this->input['id'])->first();
|
||||||
|
|
||||||
if (! $payment) {
|
if (! $payment) {
|
||||||
$this->error_msg = "Payment couldn't be retrieved cannot be refunded ";
|
$this->error_msg = ctrans('texts.unable_to_retrieve_payment');
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*We are not sending the Refunded amount in the 'amount field, this is the Payment->amount, need to skip this check. */
|
|
||||||
// if (request()->has('amount') && (request()->input('amount') > ($payment->amount - $payment->refunded))) {
|
|
||||||
// $this->error_msg = "Attempting to refund more than payment amount, enter a value equal to or lower than the payment amount of ". $payment->amount;
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
|
|
||||||
/*If no invoices has been sent, then we apply the payment to the client account*/
|
/*If no invoices has been sent, then we apply the payment to the client account*/
|
||||||
$invoices = [];
|
$invoices = [];
|
||||||
|
|
||||||
@ -70,7 +64,7 @@ class ValidRefundableInvoices implements Rule
|
|||||||
|
|
||||||
foreach ($invoices as $invoice) {
|
foreach ($invoices as $invoice) {
|
||||||
if (! $invoice->isRefundable()) {
|
if (! $invoice->isRefundable()) {
|
||||||
$this->error_msg = 'Invoice id '.$invoice->hashed_id.' cannot be refunded';
|
$this->error_msg = ctrans('texts.invoice_cannot_be_refunded', ['invoice' => $invoice->hashed_id]);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -82,7 +76,7 @@ class ValidRefundableInvoices implements Rule
|
|||||||
$pivot_record = $payment->paymentables->where('paymentable_id', $invoice->id)->first();
|
$pivot_record = $payment->paymentables->where('paymentable_id', $invoice->id)->first();
|
||||||
|
|
||||||
if ($val['amount'] > ($pivot_record->amount - $pivot_record->refunded)) {
|
if ($val['amount'] > ($pivot_record->amount - $pivot_record->refunded)) {
|
||||||
$this->error_msg = 'Attempting to refund '.$val['amount'].' only '.($pivot_record->amount - $pivot_record->refunded).' available for refund';
|
$this->error_msg = ctrans('texts.attempted_refund_failed', ['amount' => $val['amount'], 'refundable_amount' => ($pivot_record->amount - $pivot_record->refunded)]);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,6 @@ class ValidUserForCompany implements Rule
|
|||||||
*/
|
*/
|
||||||
public function message()
|
public function message()
|
||||||
{
|
{
|
||||||
return 'This user is unable to be attached to this company. Perhaps they have already registered a user on another account?';
|
return ctrans('texts.user_not_associated_with_this_account');
|
||||||
//return ctrans('texts.email_already_register');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -78,8 +78,8 @@ class ClientMap
|
|||||||
16 => 'texts.custom_value',
|
16 => 'texts.custom_value',
|
||||||
17 => 'texts.custom_value',
|
17 => 'texts.custom_value',
|
||||||
18 => 'texts.custom_value',
|
18 => 'texts.custom_value',
|
||||||
19 => 'texts.address1',
|
19 => 'texts.shipping_address1',
|
||||||
20 => 'texts.address2',
|
20 => 'texts.shipping_address2',
|
||||||
21 => 'texts.shipping_city',
|
21 => 'texts.shipping_city',
|
||||||
22 => 'texts.shipping_state',
|
22 => 'texts.shipping_state',
|
||||||
23 => 'texts.shipping_postal_code',
|
23 => 'texts.shipping_postal_code',
|
||||||
|
@ -57,17 +57,31 @@ class ApplyCreditPayment implements ShouldQueue
|
|||||||
if ($cred->id == $this->credit->id) {
|
if ($cred->id == $this->credit->id) {
|
||||||
$cred->pivot->amount = $this->amount;
|
$cred->pivot->amount = $this->amount;
|
||||||
$cred->pivot->save();
|
$cred->pivot->save();
|
||||||
|
|
||||||
|
$cred->paid_to_date += $this->amount;
|
||||||
|
$cred->save();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$credit_balance = $this->credit->balance;
|
$credit_balance = $this->credit->balance;
|
||||||
|
|
||||||
if ($this->amount == $credit_balance) { //total credit applied.
|
if ($this->amount == $credit_balance) { //total credit applied.
|
||||||
$this->credit->setStatus(Credit::STATUS_APPLIED);
|
|
||||||
$this->credit->updateBalance($this->amount * -1);
|
$this->credit
|
||||||
|
->service()
|
||||||
|
->setStatus(Credit::STATUS_APPLIED)
|
||||||
|
->adjustBalance($this->amount * -1)
|
||||||
|
->updatePaidToDate($this->amount)
|
||||||
|
->save();
|
||||||
|
|
||||||
} elseif ($this->amount < $credit_balance) { //compare number appropriately
|
} elseif ($this->amount < $credit_balance) { //compare number appropriately
|
||||||
$this->credit->setStatus(Credit::STATUS_PARTIAL);
|
|
||||||
$this->credit->updateBalance($this->amount * -1);
|
$this->credit
|
||||||
|
->service()
|
||||||
|
->setStatus(Credit::STATUS_PARTIAL)
|
||||||
|
->adjustBalance($this->amount * -1)
|
||||||
|
->updatePaidToDate($this->amount)
|
||||||
|
->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Update Payment Applied Amount*/
|
/* Update Payment Applied Amount*/
|
||||||
|
@ -55,6 +55,17 @@ class UpdateOrCreateProduct implements ShouldQueue
|
|||||||
{
|
{
|
||||||
MultiDB::setDB($this->company->db);
|
MultiDB::setDB($this->company->db);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the invoice was generated from a Task or Expense then
|
||||||
|
* we do NOT update the product details this short block we
|
||||||
|
* check for the presence of a task_id and/or expense_id
|
||||||
|
*/
|
||||||
|
$expense_count = count(array_column((array)$this->products, 'expense_id'));
|
||||||
|
$task_count = count(array_column((array)$this->products, 'task_id'));
|
||||||
|
|
||||||
|
if($task_count >= 1 || $expense_count >= 1)
|
||||||
|
return;
|
||||||
|
|
||||||
//only update / create products - not tasks or gateway fees
|
//only update / create products - not tasks or gateway fees
|
||||||
$updateable_products = collect($this->products)->filter(function ($item) {
|
$updateable_products = collect($this->products)->filter(function ($item) {
|
||||||
return $item->type_id == 1;
|
return $item->type_id == 1;
|
||||||
|
@ -896,14 +896,6 @@ class Import implements ShouldQueue
|
|||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
//depending on the status, we do a final action.
|
|
||||||
//s$payment = $this->updatePaymentForStatus($payment, $modified['status_id']);
|
|
||||||
|
|
||||||
// if($modified['is_deleted'])
|
|
||||||
// $payment->service()->deletePayment();
|
|
||||||
|
|
||||||
// if(isset($modified['deleted_at']))
|
|
||||||
// $payment->delete();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Payment::reguard();
|
Payment::reguard();
|
||||||
|
70
app/Jobs/Util/SchedulerCheck.php
Normal file
70
app/Jobs/Util/SchedulerCheck.php
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Invoice Ninja (https://invoiceninja.com).
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://opensource.org/licenses/AAL
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Jobs\Util;
|
||||||
|
|
||||||
|
use App\Models\Account;
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use Illuminate\Support\Facades\Artisan;
|
||||||
|
|
||||||
|
class SchedulerCheck implements ShouldQueue
|
||||||
|
{
|
||||||
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the job.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
set_time_limit(0);
|
||||||
|
|
||||||
|
Account::whereNotNull('id')->update(['is_scheduler_running' => true]);
|
||||||
|
|
||||||
|
if(config('ninja.app_version') != base_path('VERSION.txt'))
|
||||||
|
{
|
||||||
|
|
||||||
|
try {
|
||||||
|
Artisan::call('migrate', ['--force' => true]);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
nlog("I wasn't able to migrate the data.");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
try {
|
||||||
|
Artisan::call('optimize');
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
nlog("I wasn't able to optimize.");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
try {
|
||||||
|
Artisan::call('view:clear');
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
nlog("I wasn't able to clear the views.");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
VersionCheck::dispatch();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -49,6 +49,6 @@ class CreditArchivedActivity implements ShouldQueue
|
|||||||
$fields->company_id = $event->credit->company_id;
|
$fields->company_id = $event->credit->company_id;
|
||||||
$fields->activity_type_id = Activity::ARCHIVE_CREDIT;
|
$fields->activity_type_id = Activity::ARCHIVE_CREDIT;
|
||||||
|
|
||||||
$this->activity_repo->save($fields, $$event->credit, $event->event_vars);
|
$this->activity_repo->save($fields, $event->credit, $event->event_vars);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -261,4 +261,5 @@ class Credit extends BaseModel
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -117,6 +117,10 @@ class PaymentMigrationRepository extends BaseRepository
|
|||||||
$inv->pivot->amount = $invoice_totals;
|
$inv->pivot->amount = $invoice_totals;
|
||||||
$inv->pivot->refunded = $refund_totals;
|
$inv->pivot->refunded = $refund_totals;
|
||||||
$inv->pivot->save();
|
$inv->pivot->save();
|
||||||
|
|
||||||
|
$inv->paid_to_date += $invoice_totals;
|
||||||
|
$inv->save();
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,6 +134,9 @@ class PaymentMigrationRepository extends BaseRepository
|
|||||||
$payment->credits->each(function ($cre) use ($credit_totals) {
|
$payment->credits->each(function ($cre) use ($credit_totals) {
|
||||||
$cre->pivot->amount = $credit_totals;
|
$cre->pivot->amount = $credit_totals;
|
||||||
$cre->pivot->save();
|
$cre->pivot->save();
|
||||||
|
|
||||||
|
$cre->paid_to_date += $invoice_totals;
|
||||||
|
$cre->save();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,8 +118,6 @@ class PaymentRepository extends BaseRepository
|
|||||||
if (array_key_exists('invoices', $data) && is_array($data['invoices']) && count($data['invoices']) > 0) {
|
if (array_key_exists('invoices', $data) && is_array($data['invoices']) && count($data['invoices']) > 0) {
|
||||||
$invoice_totals = array_sum(array_column($data['invoices'], 'amount'));
|
$invoice_totals = array_sum(array_column($data['invoices'], 'amount'));
|
||||||
|
|
||||||
nlog("invoice totals = {$invoice_totals}");
|
|
||||||
|
|
||||||
$invoices = Invoice::whereIn('id', array_column($data['invoices'], 'invoice_id'))->get();
|
$invoices = Invoice::whereIn('id', array_column($data['invoices'], 'invoice_id'))->get();
|
||||||
|
|
||||||
$payment->invoices()->saveMany($invoices);
|
$payment->invoices()->saveMany($invoices);
|
||||||
|
@ -209,7 +209,7 @@ class PaymentMethod
|
|||||||
$payment_urls = [];
|
$payment_urls = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
$payment_urls[] = [
|
$this->payment_urls[] = [
|
||||||
'label' => ctrans('texts.apply_credit'),
|
'label' => ctrans('texts.apply_credit'),
|
||||||
'company_gateway_id' => CompanyGateway::GATEWAY_CREDIT,
|
'company_gateway_id' => CompanyGateway::GATEWAY_CREDIT,
|
||||||
'gateway_type_id' => GatewayType::CREDIT,
|
'gateway_type_id' => GatewayType::CREDIT,
|
||||||
@ -238,6 +238,5 @@ class PaymentMethod
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -70,6 +70,7 @@ class ApplyPayment
|
|||||||
}
|
}
|
||||||
|
|
||||||
$this->credit->balance -= $this->amount_applied;
|
$this->credit->balance -= $this->amount_applied;
|
||||||
|
$this->credit->paid_to_date += $this->amount_applied;
|
||||||
|
|
||||||
if ((int)$this->credit->balance == 0) {
|
if ((int)$this->credit->balance == 0) {
|
||||||
$this->credit->status_id = Credit::STATUS_APPLIED;
|
$this->credit->status_id = Credit::STATUS_APPLIED;
|
||||||
@ -110,6 +111,8 @@ class ApplyPayment
|
|||||||
$this->payment->currency_id = $this->credit->client->getSetting('currency_id');
|
$this->payment->currency_id = $this->credit->client->getSetting('currency_id');
|
||||||
$this->payment->save();
|
$this->payment->save();
|
||||||
|
|
||||||
|
$this->payment->service()->applyNumber()->save();
|
||||||
|
|
||||||
$this->payment
|
$this->payment
|
||||||
->invoices()
|
->invoices()
|
||||||
->attach($this->invoice->id, ['amount' => $this->amount_applied]);
|
->attach($this->invoice->id, ['amount' => $this->amount_applied]);
|
||||||
@ -133,6 +136,7 @@ class ApplyPayment
|
|||||||
$this->invoice
|
$this->invoice
|
||||||
->service()
|
->service()
|
||||||
->updateBalance($this->amount_applied * -1)
|
->updateBalance($this->amount_applied * -1)
|
||||||
|
->updatePaidToDate($this->amount_applied)
|
||||||
->updateStatus()
|
->updateStatus()
|
||||||
->save();
|
->save();
|
||||||
|
|
||||||
|
@ -97,6 +97,12 @@ class CreditService
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function updatePaidToDate($adjustment)
|
||||||
|
{
|
||||||
|
$this->credit->paid_to_date += $adjustment;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
public function fillDefaults()
|
public function fillDefaults()
|
||||||
{
|
{
|
||||||
|
@ -104,14 +104,24 @@ class ApplyPayment extends AbstractService
|
|||||||
->ledger()
|
->ledger()
|
||||||
->updatePaymentBalance($amount_paid);
|
->updatePaymentBalance($amount_paid);
|
||||||
|
|
||||||
$this->invoice->client->service()->updateBalance($amount_paid)->save();
|
$this->invoice
|
||||||
|
->client
|
||||||
|
->service()
|
||||||
|
->updateBalance($amount_paid)
|
||||||
|
->save();
|
||||||
|
|
||||||
/* Update Pivot Record amount */
|
/* Update Pivot Record amount */
|
||||||
$this->payment->invoices->each(function ($inv) use($amount_paid){
|
$this->payment->invoices->each(function ($inv) use($amount_paid){
|
||||||
|
|
||||||
if ($inv->id == $this->invoice->id) {
|
if ($inv->id == $this->invoice->id) {
|
||||||
|
|
||||||
$inv->pivot->amount = ($amount_paid*-1);
|
$inv->pivot->amount = ($amount_paid*-1);
|
||||||
$inv->pivot->save();
|
$inv->pivot->save();
|
||||||
|
|
||||||
|
$inv->paid_to_date += floatval($amount_paid*-1);
|
||||||
|
$inv->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
$this->invoice->service()->applyNumber()->save();
|
$this->invoice->service()->applyNumber()->save();
|
||||||
|
@ -49,18 +49,12 @@ class AutoBillInvoice extends AbstractService
|
|||||||
$this->invoice = $this->invoice->service()->markSent()->save();
|
$this->invoice = $this->invoice->service()->markSent()->save();
|
||||||
|
|
||||||
/* Mark the invoice as paid if there is no balance */
|
/* Mark the invoice as paid if there is no balance */
|
||||||
if ((int)$this->invoice->balance == 0) {
|
if ((int)$this->invoice->balance == 0)
|
||||||
return $this->invoice->service()->markPaid()->save();
|
return $this->invoice->service()->markPaid()->save();
|
||||||
}
|
|
||||||
|
|
||||||
//if the credits cover the payments, we stop here, build the payment with credits and exit early
|
//if the credits cover the payments, we stop here, build the payment with credits and exit early
|
||||||
|
if ($this->client->getSetting('use_credits_payment') != 'off')
|
||||||
if ($this->client->getSetting('use_credits_payment') != 'off') {
|
|
||||||
$this->applyCreditPayment();
|
$this->applyCreditPayment();
|
||||||
}
|
|
||||||
|
|
||||||
// info("partial = {$this->invoice->partial}");
|
|
||||||
// info("balance = {$this->invoice->balance}");
|
|
||||||
|
|
||||||
/* Determine $amount */
|
/* Determine $amount */
|
||||||
if ($this->invoice->partial > 0) {
|
if ($this->invoice->partial > 0) {
|
||||||
@ -76,15 +70,12 @@ class AutoBillInvoice extends AbstractService
|
|||||||
$gateway_token = $this->getGateway($amount);
|
$gateway_token = $this->getGateway($amount);
|
||||||
|
|
||||||
/* Bail out if no payment methods available */
|
/* Bail out if no payment methods available */
|
||||||
if (! $gateway_token || ! $gateway_token->gateway->driver($this->client)->token_billing) {
|
if (! $gateway_token || ! $gateway_token->gateway->driver($this->client)->token_billing)
|
||||||
return $this->invoice;
|
return $this->invoice;
|
||||||
}
|
|
||||||
|
|
||||||
/* $gateway fee */
|
/* $gateway fee */
|
||||||
$fee = $gateway_token->gateway->calcGatewayFee($amount, $gateway_token->gateway_type_id, $this->invoice->uses_inclusive_taxes);
|
$fee = $gateway_token->gateway->calcGatewayFee($amount, $gateway_token->gateway_type_id, $this->invoice->uses_inclusive_taxes);
|
||||||
|
|
||||||
//todo determine exact fee as per PaymentController
|
|
||||||
|
|
||||||
/* Build payment hash */
|
/* Build payment hash */
|
||||||
$payment_hash = PaymentHash::create([
|
$payment_hash = PaymentHash::create([
|
||||||
'hash' => Str::random(128),
|
'hash' => Str::random(128),
|
||||||
@ -122,18 +113,24 @@ class AutoBillInvoice extends AbstractService
|
|||||||
|
|
||||||
$payment->invoices()->attach($this->invoice->id, ['amount' => $amount]);
|
$payment->invoices()->attach($this->invoice->id, ['amount' => $amount]);
|
||||||
|
|
||||||
$this->invoice->service()->setStatus(Invoice::STATUS_PAID)->save();
|
$this->invoice
|
||||||
|
->service()
|
||||||
|
->setStatus(Invoice::STATUS_PAID)
|
||||||
|
->save();
|
||||||
|
|
||||||
foreach ($this->used_credit as $credit) {
|
foreach ($this->used_credit as $credit) {
|
||||||
$current_credit = Credit::find($credit['credit_id']);
|
$current_credit = Credit::find($credit['credit_id']);
|
||||||
$payment->credits()->attach($current_credit->id, ['amount' => $credit['amount']]);
|
$payment->credits()
|
||||||
|
->attach($current_credit->id, ['amount' => $credit['amount']]);
|
||||||
|
|
||||||
info("adjusting credit balance {$current_credit->balance} by this amount ". $credit['amount']);
|
info("adjusting credit balance {$current_credit->balance} by this amount ". $credit['amount']);
|
||||||
|
|
||||||
$current_credit->balance -= $credit['amount'];
|
$current_credit->service()
|
||||||
|
->adjustBalance($credit['amount']*-1)
|
||||||
|
->updatePaidToDate($credit['amount'])
|
||||||
|
->setCalculatedStatus()
|
||||||
|
->save();
|
||||||
|
|
||||||
$current_credit->service()->setCalculatedStatus()->save();
|
|
||||||
// $this->applyPaymentToCredit($current_credit, $credit['amount']);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$payment->ledger()
|
$payment->ledger()
|
||||||
@ -153,7 +150,10 @@ class AutoBillInvoice extends AbstractService
|
|||||||
|
|
||||||
event(new PaymentWasCreated($payment, $payment->company, Ninja::eventVars()));
|
event(new PaymentWasCreated($payment, $payment->company, Ninja::eventVars()));
|
||||||
|
|
||||||
return $this->invoice->service()->setCalculatedStatus()->save();
|
return $this->invoice
|
||||||
|
->service()
|
||||||
|
->setCalculatedStatus()
|
||||||
|
->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -190,10 +190,11 @@ class AutoBillInvoice extends AbstractService
|
|||||||
if ($is_partial_amount) {
|
if ($is_partial_amount) {
|
||||||
|
|
||||||
//more credit than needed
|
//more credit than needed
|
||||||
if ($credit->balance >= $this->invoice->partial) {
|
if ($credit->balance > $this->invoice->partial) {
|
||||||
$this->used_credit[$key]['credit_id'] = $credit->id;
|
$this->used_credit[$key]['credit_id'] = $credit->id;
|
||||||
$this->used_credit[$key]['amount'] = $this->invoice->partial;
|
$this->used_credit[$key]['amount'] = $this->invoice->partial;
|
||||||
$this->invoice->balance -= $this->invoice->partial;
|
$this->invoice->balance -= $this->invoice->partial;
|
||||||
|
$this->invoice->paid_to_date += $this->invoice->partial;
|
||||||
$this->invoice->partial = 0;
|
$this->invoice->partial = 0;
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
@ -201,19 +202,23 @@ class AutoBillInvoice extends AbstractService
|
|||||||
$this->used_credit[$key]['amount'] = $credit->balance;
|
$this->used_credit[$key]['amount'] = $credit->balance;
|
||||||
$this->invoice->partial -= $credit->balance;
|
$this->invoice->partial -= $credit->balance;
|
||||||
$this->invoice->balance -= $credit->balance;
|
$this->invoice->balance -= $credit->balance;
|
||||||
|
$this->invoice->paid_to_date += $credit->balance;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
//more credit than needed
|
//more credit than needed
|
||||||
if ($credit->balance >= $this->invoice->balance) {
|
if ($credit->balance > $this->invoice->balance) {
|
||||||
$this->used_credit[$key]['credit_id'] = $credit->id;
|
$this->used_credit[$key]['credit_id'] = $credit->id;
|
||||||
$this->used_credit[$key]['amount'] = $this->invoice->balance;
|
$this->used_credit[$key]['amount'] = $this->invoice->balance;
|
||||||
|
$this->invoice->paid_to_date += $this->invoice->balance;
|
||||||
$this->invoice->balance = 0;
|
$this->invoice->balance = 0;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
$this->used_credit[$key]['credit_id'] = $credit->id;
|
$this->used_credit[$key]['credit_id'] = $credit->id;
|
||||||
$this->used_credit[$key]['amount'] = $credit->balance;
|
$this->used_credit[$key]['amount'] = $credit->balance;
|
||||||
$this->invoice->balance -= $credit->balance;
|
$this->invoice->balance -= $credit->balance;
|
||||||
|
$this->invoice->paid_to_date += $credit->balance;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,14 +38,12 @@ class HandleReversal extends AbstractService
|
|||||||
public function run()
|
public function run()
|
||||||
{
|
{
|
||||||
/* Check again!! */
|
/* Check again!! */
|
||||||
if (! $this->invoice->invoiceReversable($this->invoice)) {
|
if (! $this->invoice->invoiceReversable($this->invoice))
|
||||||
return $this->invoice;
|
return $this->invoice;
|
||||||
}
|
|
||||||
|
|
||||||
/* If the invoice has been cancelled - we need to unwind the cancellation before reversing*/
|
/* If the invoice has been cancelled - we need to unwind the cancellation before reversing*/
|
||||||
if ($this->invoice->status_id == Invoice::STATUS_CANCELLED) {
|
if ($this->invoice->status_id == Invoice::STATUS_CANCELLED)
|
||||||
$this->invoice = $this->invoice->service()->reverseCancellation()->save();
|
$this->invoice = $this->invoice->service()->reverseCancellation()->save();
|
||||||
}
|
|
||||||
|
|
||||||
$balance_remaining = $this->invoice->balance;
|
$balance_remaining = $this->invoice->balance;
|
||||||
|
|
||||||
@ -57,18 +55,19 @@ class HandleReversal extends AbstractService
|
|||||||
->get();
|
->get();
|
||||||
|
|
||||||
$paymentables->each(function ($paymentable) use ($total_paid) {
|
$paymentables->each(function ($paymentable) use ($total_paid) {
|
||||||
|
|
||||||
$reversable_amount = $paymentable->amount - $paymentable->refunded;
|
$reversable_amount = $paymentable->amount - $paymentable->refunded;
|
||||||
|
|
||||||
$total_paid -= $reversable_amount;
|
$total_paid -= $reversable_amount;
|
||||||
|
|
||||||
$paymentable->amount = $paymentable->refunded;
|
$paymentable->amount = $paymentable->refunded;
|
||||||
$paymentable->save();
|
$paymentable->save();
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/* Generate a credit for the $total_paid amount */
|
/* Generate a credit for the $total_paid amount */
|
||||||
$notes = 'Credit for reversal of '.$this->invoice->number;
|
$notes = 'Credit for reversal of '.$this->invoice->number;
|
||||||
|
|
||||||
if ($total_paid > 0) {
|
if ($total_paid > 0) {
|
||||||
|
|
||||||
$credit = CreditFactory::create($this->invoice->company_id, $this->invoice->user_id);
|
$credit = CreditFactory::create($this->invoice->company_id, $this->invoice->user_id);
|
||||||
$credit->client_id = $this->invoice->client_id;
|
$credit->client_id = $this->invoice->client_id;
|
||||||
$credit->invoice_id = $this->invoice->id;
|
$credit->invoice_id = $this->invoice->id;
|
||||||
@ -79,16 +78,13 @@ class HandleReversal extends AbstractService
|
|||||||
$item->notes = $notes;
|
$item->notes = $notes;
|
||||||
|
|
||||||
$line_items[] = $item;
|
$line_items[] = $item;
|
||||||
|
|
||||||
$credit->line_items = $line_items;
|
$credit->line_items = $line_items;
|
||||||
|
|
||||||
$credit->save();
|
$credit->save();
|
||||||
|
|
||||||
$credit_calc = new InvoiceSum($credit);
|
$credit_calc = new InvoiceSum($credit);
|
||||||
$credit_calc->build();
|
$credit_calc->build();
|
||||||
|
|
||||||
$credit = $credit_calc->purgeTaxes()->getCredit();
|
$credit = $credit_calc->purgeTaxes()->getCredit();
|
||||||
|
|
||||||
$credit->service()->markSent()->save();
|
$credit->service()->markSent()->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,6 +101,9 @@ class HandleReversal extends AbstractService
|
|||||||
//harvest the credit record and add in the amount for the credit.
|
//harvest the credit record and add in the amount for the credit.
|
||||||
$paymentable_credit->pivot->amount = $total_paid;
|
$paymentable_credit->pivot->amount = $total_paid;
|
||||||
$paymentable_credit->pivot->save();
|
$paymentable_credit->pivot->save();
|
||||||
|
|
||||||
|
$paymentable_credit->paid_to_date += $total_paid;
|
||||||
|
$paymentable_credit->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set invoice balance to 0 */
|
/* Set invoice balance to 0 */
|
||||||
@ -113,6 +112,7 @@ class HandleReversal extends AbstractService
|
|||||||
}
|
}
|
||||||
|
|
||||||
$this->invoice->balance = 0;
|
$this->invoice->balance = 0;
|
||||||
|
$this->invoice->paid_to_date = 0;
|
||||||
|
|
||||||
/* Set invoice status to reversed... somehow*/
|
/* Set invoice status to reversed... somehow*/
|
||||||
$this->invoice->service()->setStatus(Invoice::STATUS_REVERSED)->save();
|
$this->invoice->service()->setStatus(Invoice::STATUS_REVERSED)->save();
|
||||||
|
@ -112,6 +112,13 @@ class InvoiceService
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function updatePaidToDate($adjustment)
|
||||||
|
{
|
||||||
|
$this->invoice->paid_to_date += $adjustment;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
public function createInvitations()
|
public function createInvitations()
|
||||||
{
|
{
|
||||||
$this->invoice = (new CreateInvitations($this->invoice))->run();
|
$this->invoice = (new CreateInvitations($this->invoice))->run();
|
||||||
|
@ -70,6 +70,7 @@ class MarkPaid extends AbstractService
|
|||||||
|
|
||||||
$this->invoice->service()
|
$this->invoice->service()
|
||||||
->updateBalance($payment->amount * -1)
|
->updateBalance($payment->amount * -1)
|
||||||
|
->updatePaidToDate($payment->amount)
|
||||||
->setStatus(Invoice::STATUS_PAID)
|
->setStatus(Invoice::STATUS_PAID)
|
||||||
->applyNumber()
|
->applyNumber()
|
||||||
->save();
|
->save();
|
||||||
|
@ -55,7 +55,6 @@ class DeletePayment
|
|||||||
private function cleanupPayment()
|
private function cleanupPayment()
|
||||||
{
|
{
|
||||||
$this->payment->is_deleted = true;
|
$this->payment->is_deleted = true;
|
||||||
// $entity->save();
|
|
||||||
$this->payment->delete();
|
$this->payment->delete();
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
@ -78,10 +77,22 @@ class DeletePayment
|
|||||||
private function adjustInvoices()
|
private function adjustInvoices()
|
||||||
{
|
{
|
||||||
if ($this->payment->invoices()->exists()) {
|
if ($this->payment->invoices()->exists()) {
|
||||||
|
|
||||||
$this->payment->invoices()->each(function ($paymentable_invoice) {
|
$this->payment->invoices()->each(function ($paymentable_invoice) {
|
||||||
$paymentable_invoice->service()->updateBalance($paymentable_invoice->pivot->amount)->save();
|
|
||||||
$paymentable_invoice->ledger()->updateInvoiceBalance($paymentable_invoice->pivot->amount, "Adjusting invoice {$paymentable_invoice->number} due to deletion of Payment {$this->payment->number}")->save();
|
$paymentable_invoice->service()
|
||||||
$paymentable_invoice->client->service()->updateBalance($paymentable_invoice->pivot->amount)->save();
|
->updateBalance($paymentable_invoice->pivot->amount)
|
||||||
|
->updatePaidToDate($paymentable_invoice->pivot->amount * -1)
|
||||||
|
->save();
|
||||||
|
|
||||||
|
$paymentable_invoice->ledger()
|
||||||
|
->updateInvoiceBalance($paymentable_invoice->pivot->amount, "Adjusting invoice {$paymentable_invoice->number} due to deletion of Payment {$this->payment->number}")
|
||||||
|
->save();
|
||||||
|
|
||||||
|
$paymentable_invoice->client
|
||||||
|
->service()
|
||||||
|
->updateBalance($paymentable_invoice->pivot->amount)
|
||||||
|
->save();
|
||||||
|
|
||||||
if ($paymentable_invoice->balance == $paymentable_invoice->amount) {
|
if ($paymentable_invoice->balance == $paymentable_invoice->amount) {
|
||||||
$paymentable_invoice->service()->setStatus(Invoice::STATUS_SENT)->save();
|
$paymentable_invoice->service()->setStatus(Invoice::STATUS_SENT)->save();
|
||||||
@ -101,10 +112,12 @@ class DeletePayment
|
|||||||
{
|
{
|
||||||
if ($this->payment->credits()->exists()) {
|
if ($this->payment->credits()->exists()) {
|
||||||
$this->payment->credits()->each(function ($paymentable_credit) {
|
$this->payment->credits()->each(function ($paymentable_credit) {
|
||||||
$paymentable_credit->balance += $paymentable_credit->pivot->amount;
|
|
||||||
$paymentable_credit->setStatus(Credit::STATUS_SENT);
|
$paymentable_credit->service()
|
||||||
//fire event for this credit
|
->updateBalance($paymentable_credit->pivot->amount)
|
||||||
//
|
->updatePaidToDate($paymentable_credit->pivot->amount*-1)
|
||||||
|
->setStatus(Credit::STATUS_SENT)
|
||||||
|
->save();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,11 +59,16 @@ class PaymentService
|
|||||||
$client = $this->payment->client;
|
$client = $this->payment->client;
|
||||||
|
|
||||||
$invoices->each(function ($invoice) {
|
$invoices->each(function ($invoice) {
|
||||||
|
|
||||||
if ($invoice->pivot->amount > 0) {
|
if ($invoice->pivot->amount > 0) {
|
||||||
$invoice->status_id = Invoice::STATUS_SENT;
|
|
||||||
$invoice->balance = $invoice->pivot->amount;
|
$invoice->service()
|
||||||
$invoice->save();
|
->updateBalance($invoice->pivot->amount)
|
||||||
|
->updatePaidToDate($invoice->pivot->amount*-1)
|
||||||
|
->setStatus(Invoice::STATUS_SENT)
|
||||||
|
->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
$this->payment
|
$this->payment
|
||||||
|
@ -188,18 +188,26 @@ class RefundPayment
|
|||||||
if ($available_credit > $this->total_refund) {
|
if ($available_credit > $this->total_refund) {
|
||||||
$paymentable_credit->pivot->refunded += $this->total_refund;
|
$paymentable_credit->pivot->refunded += $this->total_refund;
|
||||||
$paymentable_credit->pivot->save();
|
$paymentable_credit->pivot->save();
|
||||||
$paymentable_credit->balance += $this->total_refund;
|
|
||||||
$paymentable_credit->service()->setStatus(Credit::STATUS_SENT)->save();
|
$paymentable_credit->service()
|
||||||
//$paymentable_credit->save();
|
->setStatus(Credit::STATUS_SENT)
|
||||||
|
->updateBalance($this->total_refund)
|
||||||
|
->updatePaidToDate($this->total_refund*-1)
|
||||||
|
->save();
|
||||||
|
|
||||||
$this->total_refund = 0;
|
$this->total_refund = 0;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
$paymentable_credit->pivot->refunded += $available_credit;
|
$paymentable_credit->pivot->refunded += $available_credit;
|
||||||
$paymentable_credit->pivot->save();
|
$paymentable_credit->pivot->save();
|
||||||
|
|
||||||
$paymentable_credit->balance += $available_credit;
|
$paymentable_credit->balance += $available_credit;
|
||||||
$paymentable_credit->service()->setStatus(Credit::STATUS_SENT)->save();
|
$paymentable_credit->service()
|
||||||
// $paymentable_credit->save();
|
->setStatus(Credit::STATUS_SENT)
|
||||||
|
->adjustBalance($available_credit)
|
||||||
|
->updatePaidToDate($available_credit*-1)
|
||||||
|
->save();
|
||||||
|
|
||||||
$this->total_refund -= $available_credit;
|
$this->total_refund -= $available_credit;
|
||||||
}
|
}
|
||||||
|
@ -79,6 +79,7 @@ class UpdateInvoicePayment
|
|||||||
$invoice->service() //caution what if we amount paid was less than partial - we wipe it!
|
$invoice->service() //caution what if we amount paid was less than partial - we wipe it!
|
||||||
->clearPartial()
|
->clearPartial()
|
||||||
->updateBalance($paid_amount * -1)
|
->updateBalance($paid_amount * -1)
|
||||||
|
->updatePaidToDate($paid_amount)
|
||||||
->updateStatus()
|
->updateStatus()
|
||||||
->save();
|
->save();
|
||||||
|
|
||||||
|
@ -76,6 +76,7 @@ class AccountTransformer extends EntityTransformer
|
|||||||
'report_errors' => (bool) $account->report_errors,
|
'report_errors' => (bool) $account->report_errors,
|
||||||
'debug_enabled' => (bool) config('ninja.debug_enabled'),
|
'debug_enabled' => (bool) config('ninja.debug_enabled'),
|
||||||
'is_docker' => (bool) config('ninja.is_docker'),
|
'is_docker' => (bool) config('ninja.is_docker'),
|
||||||
|
'is_scheduler_running' => (bool) $account->is_scheduler_running,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,6 +136,7 @@ class CreditTransformer extends EntityTransformer
|
|||||||
'line_items' => $credit->line_items ?: (array) [],
|
'line_items' => $credit->line_items ?: (array) [],
|
||||||
'entity_type' => 'credit',
|
'entity_type' => 'credit',
|
||||||
'exchange_rate' => (float) $credit->exchange_rate,
|
'exchange_rate' => (float) $credit->exchange_rate,
|
||||||
|
'paid_to_date' => (float) $credit->paid_to_date,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -140,6 +140,8 @@ class InvoiceTransformer extends EntityTransformer
|
|||||||
'reminder2_sent' => $invoice->reminder2_sent ?: '',
|
'reminder2_sent' => $invoice->reminder2_sent ?: '',
|
||||||
'reminder3_sent' => $invoice->reminder3_sent ?: '',
|
'reminder3_sent' => $invoice->reminder3_sent ?: '',
|
||||||
'reminder_last_sent' => $invoice->reminder_last_sent ?: '',
|
'reminder_last_sent' => $invoice->reminder_last_sent ?: '',
|
||||||
|
'paid_to_date' => (float) $invoice->paid_to_date,
|
||||||
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -137,6 +137,7 @@ class QuoteTransformer extends EntityTransformer
|
|||||||
'line_items' => $quote->line_items ?: (array) [],
|
'line_items' => $quote->line_items ?: (array) [],
|
||||||
'entity_type' => 'quote',
|
'entity_type' => 'quote',
|
||||||
'exchange_rate' => (float) $quote->exchange_rate,
|
'exchange_rate' => (float) $quote->exchange_rate,
|
||||||
|
'paid_to_date' => (float) $quote->paid_to_date,
|
||||||
'project_id' => $this->encodePrimaryKey($quote->project_id),
|
'project_id' => $this->encodePrimaryKey($quote->project_id),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -138,6 +138,7 @@ class RecurringInvoiceTransformer extends EntityTransformer
|
|||||||
'auto_bill' => (string) $invoice->auto_bill,
|
'auto_bill' => (string) $invoice->auto_bill,
|
||||||
'auto_bill_enabled' => (bool) $invoice->auto_bill_enabled,
|
'auto_bill_enabled' => (bool) $invoice->auto_bill_enabled,
|
||||||
'due_date_days' => (string) $invoice->due_date_days ?: '',
|
'due_date_days' => (string) $invoice->due_date_days ?: '',
|
||||||
|
'paid_to_date' => (float) $invoice->paid_to_date,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ 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', ''),
|
'app_domain' => env('APP_DOMAIN', ''),
|
||||||
'app_version' => '5.0.52',
|
'app_version' => '5.0.53',
|
||||||
'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', false),
|
'api_secret' => env('API_SECRET', false),
|
||||||
|
@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
class SchedulerIsRunningCheck extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::table('accounts', function (Blueprint $table) {
|
||||||
|
$table->boolean('is_scheduler_running')->default(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
class AddPaidToDateColumn extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::table('invoices', function (Blueprint $table) {
|
||||||
|
$table->decimal('paid_to_date', 20, 6)->default(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::table('quotes', function (Blueprint $table) {
|
||||||
|
$table->decimal('paid_to_date', 20, 6)->default(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::table('credits', function (Blueprint $table) {
|
||||||
|
$table->decimal('paid_to_date', 20, 6)->default(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::table('recurring_invoices', function (Blueprint $table) {
|
||||||
|
$table->decimal('paid_to_date', 20, 6)->default(0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -3374,4 +3374,37 @@ return [
|
|||||||
'required_client_info_save_label' => 'We will save this, so you don\'t have to enter it next time.',
|
'required_client_info_save_label' => 'We will save this, so you don\'t have to enter it next time.',
|
||||||
'notification_credit_bounced' => 'We were unable to deliver Credit :invoice to :contact. \n :error',
|
'notification_credit_bounced' => 'We were unable to deliver Credit :invoice to :contact. \n :error',
|
||||||
'notification_credit_bounced_subject' => 'Unable to deliver Credit :invoice',
|
'notification_credit_bounced_subject' => 'Unable to deliver Credit :invoice',
|
||||||
|
|
||||||
|
'company_limit_reached' => 'Limit of 10 companies per account.',
|
||||||
|
'credits_applied_validation' => 'Total credits applied cannot be MORE than total of invoices',
|
||||||
|
'credit_number_taken' => 'Credit number already taken',
|
||||||
|
'credit_not_found' => 'Credit not found',
|
||||||
|
'invoices_dont_match_client' => 'Selected invoices are not from a single client',
|
||||||
|
'duplicate_credits_submitted' => 'Duplicate credits submitted.',
|
||||||
|
'duplicate_invoices_submitted' => 'Duplicate invoices submitted.',
|
||||||
|
'credit_with_no_invoice' => 'You must have an invoice set when using a credit in a payment',
|
||||||
|
'client_id_required' => 'Client id is required',
|
||||||
|
'expense_number_taken' => 'Expense number already taken',
|
||||||
|
'invoice_number_taken' => 'Invoice number already taken',
|
||||||
|
'invoice_not_found' => 'Invoice not found',
|
||||||
|
'amount_greater_than_balance' => 'Amount cannot be greater than invoice balance',
|
||||||
|
'payment_id_required' => 'Payment `id` required.',
|
||||||
|
'unable_to_retrieve_payment' => 'Unable to retrieve specified payment',
|
||||||
|
'invoice_not_related_to_payment' => 'Invoice id :invoice is not related to this payment',
|
||||||
|
'credit_not_related_to_payment' => 'Credit id :credit is not related to this payment',
|
||||||
|
'max_refundable_invoice' => 'Attempting to refund more than allowed for invoice id :invoice, maximum refundable amount is :amount',
|
||||||
|
'refund_without_invoices' => 'Attempting to refund a payment with invoices attached, please specify valid invoice/s to be refunded.',
|
||||||
|
'refund_without_credits' => 'Attempting to refund a payment with credits attached, please specify valid credits/s to be refunded.',
|
||||||
|
'max_refundable_credit' => 'Attempting to refund more than allowed for credit :credit, maximum refundable amount is :amount',
|
||||||
|
'project_client_do_not_match' => "Project client does not match entity client",
|
||||||
|
'quote_number_taken' => 'Quote number already taken',
|
||||||
|
'recurring_invoice_number_taken' => 'Recurring Invoice number :number already taken',
|
||||||
|
'user_not_associated_with_account' => 'User not associated with this account',
|
||||||
|
'amounts_do_not_balance' => 'Amounts do not balance correctly.',
|
||||||
|
'insufficient_applied_amount_remaining' => 'Insufficient applied amount remaining to cover payment.',
|
||||||
|
'insufficient_credit_balance' => 'Insufficient balance on credit.',
|
||||||
|
'one_or_more_invoices_paid' => 'One or more of these invoices have been paid',
|
||||||
|
'invoice_cannot_be_refunded' => 'Invoice id :number cannot be refunded',
|
||||||
|
'attempted_refund_failed' => 'Attempting to refund :amount only :refundable_amount available for refund',
|
||||||
|
'user_not_associated_with_this_account' => 'This user is unable to be attached to this company. Perhaps they have already registered a user on another account?',
|
||||||
];
|
];
|
||||||
|
161
tests/Feature/EntityPaidToDateTest.php
Normal file
161
tests/Feature/EntityPaidToDateTest.php
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Invoice Ninja (https://invoiceninja.com).
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://opensource.org/licenses/AAL
|
||||||
|
*/
|
||||||
|
namespace Tests\Feature;
|
||||||
|
|
||||||
|
use App\DataMapper\ClientSettings;
|
||||||
|
use App\Factory\ClientFactory;
|
||||||
|
use App\Factory\CreditFactory;
|
||||||
|
use App\Factory\InvoiceFactory;
|
||||||
|
use App\Factory\InvoiceItemFactory;
|
||||||
|
use App\Factory\PaymentFactory;
|
||||||
|
use App\Helpers\Invoice\InvoiceSum;
|
||||||
|
use App\Models\Client;
|
||||||
|
use App\Models\ClientContact;
|
||||||
|
use App\Models\Credit;
|
||||||
|
use App\Models\Invoice;
|
||||||
|
use App\Models\Payment;
|
||||||
|
use App\Utils\Traits\MakesHash;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||||
|
use Illuminate\Foundation\Testing\WithoutEvents;
|
||||||
|
use Illuminate\Routing\Middleware\ThrottleRequests;
|
||||||
|
use Illuminate\Support\Facades\Session;
|
||||||
|
use Illuminate\Validation\ValidationException;
|
||||||
|
use Tests\MockAccountData;
|
||||||
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
class EntityPaidToDateTest extends TestCase
|
||||||
|
{
|
||||||
|
use MakesHash;
|
||||||
|
use DatabaseTransactions;
|
||||||
|
use MockAccountData;
|
||||||
|
use WithoutEvents;
|
||||||
|
|
||||||
|
public function setUp() :void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
Session::start();
|
||||||
|
|
||||||
|
$this->faker = \Faker\Factory::create();
|
||||||
|
|
||||||
|
Model::reguard();
|
||||||
|
|
||||||
|
$this->makeTestData();
|
||||||
|
$this->withoutExceptionHandling();
|
||||||
|
|
||||||
|
$this->withoutMiddleware(
|
||||||
|
ThrottleRequests::class
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPaidToDateWithMarkPaidAction()
|
||||||
|
{
|
||||||
|
|
||||||
|
$invoice = $this->bootNewInvoice();
|
||||||
|
|
||||||
|
$this->assertEquals($invoice->balance, 0);
|
||||||
|
$this->assertEquals($invoice->paid_to_date, 0);
|
||||||
|
|
||||||
|
$invoice->service()->markSent()->save();
|
||||||
|
|
||||||
|
$this->assertEquals($invoice->balance, 20);
|
||||||
|
|
||||||
|
$invoice->service()->markPaid()->save();
|
||||||
|
|
||||||
|
$this->assertEquals($invoice->paid_to_date, 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPaidToDateWithInvoiceCancellation()
|
||||||
|
{
|
||||||
|
|
||||||
|
$invoice = $this->bootNewInvoice();
|
||||||
|
|
||||||
|
$invoice->service()->markPaid()->save();
|
||||||
|
|
||||||
|
$this->assertEquals(20, $invoice->paid_to_date);
|
||||||
|
|
||||||
|
$invoice->service()->handleReversal()->save();
|
||||||
|
|
||||||
|
$this->assertEquals(0, $invoice->paid_to_date);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private function bootNewInvoice()
|
||||||
|
{
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'name' => 'A Nice Client',
|
||||||
|
];
|
||||||
|
|
||||||
|
$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);
|
||||||
|
//create new invoice.
|
||||||
|
|
||||||
|
$line_items = [];
|
||||||
|
|
||||||
|
$item = InvoiceItemFactory::create();
|
||||||
|
$item->quantity = 1;
|
||||||
|
$item->cost = 10;
|
||||||
|
|
||||||
|
$line_items[] = (array)$item;
|
||||||
|
|
||||||
|
$item = InvoiceItemFactory::create();
|
||||||
|
$item->quantity = 1;
|
||||||
|
$item->cost = 10;
|
||||||
|
|
||||||
|
$line_items[] = (array)$item;
|
||||||
|
|
||||||
|
$invoice = [
|
||||||
|
'status_id' => 1,
|
||||||
|
'number' => '',
|
||||||
|
'discount' => 0,
|
||||||
|
'is_amount_discount' => 1,
|
||||||
|
'po_number' => '3434343',
|
||||||
|
'public_notes' => 'notes',
|
||||||
|
'is_deleted' => 0,
|
||||||
|
'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'];
|
||||||
|
|
||||||
|
return Invoice::find($this->decodePrimaryKey($invoice_one_hashed_id));
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user