Merge pull request #3791 from turbo124/v2

Fixes for payments affecting the client ledger balance
This commit is contained in:
David Bomba 2020-06-09 08:14:25 +10:00 committed by GitHub
commit 0dc8b59ecc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 99 additions and 78 deletions

View File

@ -26,7 +26,7 @@ npm i
npm run production
```
Please Note: Your APP_KEY in the .env file is used to encrypt data, if you loose this you will not be able to run the application.
Please Note: Your APP_KEY in the .env file is used to encrypt data, if you lose this you will not be able to run the application.
Run if you want to load sample data, remember to configure .env
```

View File

@ -291,7 +291,7 @@ class BaseController extends Controller
* Thresholds for displaying large account on first load
*/
if (request()->has('first_load') && request()->input('first_load') == 'true') {
if (auth()->user()->getCompany()->invoices->count() > 1000) {
if (auth()->user()->getCompany()->invoices->count() > 1000 || auth()->user()->getCompany()->products->count() > 1000) {
$data = $mini_load;
} else {
$data = $first_load;

View File

@ -11,6 +11,7 @@
namespace App\Http\Controllers;
use App\DataMapper\CompanySettings;
use App\DataMapper\DefaultSettings;
use App\Http\Requests\Company\CreateCompanyRequest;
use App\Http\Requests\Company\DestroyCompanyRequest;
@ -218,6 +219,7 @@ class CompanyController extends BaseController
'is_locked' => 0,
'permissions' => '',
'settings' => null,
'notifications' => CompanySettings::notificationDefaults(),
//'settings' => DefaultSettings::userSettings(),
]);

View File

@ -662,6 +662,13 @@ class InvoiceController extends BaseController
case 'download':
return response()->download(TempFile::path($invoice->pdf_file_path()), basename($invoice->pdf_file_path()));
break;
case 'restore':
$this->invoice_repo->restore($invoice);
if (!$bulk) {
return $this->listResponse($invoice);
}
break;
case 'archive':
$this->invoice_repo->archive($invoice);

View File

@ -38,6 +38,7 @@ class UpdatePaymentRequest extends Request
{
return [
'invoices' => ['required','array','min:1',new PaymentAppliedValidAmount,new ValidCreditsPresentRule],
'invoices.*.invoice_id' => 'distinct',
'documents' => 'mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx',
];
}
@ -77,4 +78,11 @@ class UpdatePaymentRequest extends Request
}
$this->replace($input);
}
public function messages()
{
return [
'distinct' => 'Attemping duplicate payment on the same invoice Invoice',
];
}
}

View File

@ -50,6 +50,7 @@ class UpdateInvoiceActivity implements ShouldQueue
$fields->user_id = $event->invoice->user_id;
$fields->company_id = $event->invoice->company_id;
$fields->activity_type_id = Activity::UPDATE_INVOICE;
$fields->invoice_id = $event->invoice->id;
$this->activity_repo->save($fields, $event->invoice);
}

View File

@ -251,12 +251,12 @@ class Client extends BaseModel implements HasLocalePreference
* @param float $amount Adjustment amount
* @return Client
*/
public function processUnappliedPayment($amount) :Client
{
return $this->service()->updatePaidToDate($amount)
->adjustCreditBalance($amount)
->save();
}
// public function processUnappliedPayment($amount) :Client
// {
// return $this->service()->updatePaidToDate($amount)
// ->adjustCreditBalance($amount)
// ->save();
// }
/**
*

View File

@ -25,15 +25,20 @@ class ClientPresenter extends EntityPresenter
*/
public function name()
{
if($this->entity->name)
return $this->entity->name;
$contact = $this->entity->primary_contact->first();
$contact_name = 'No Contact Set';
if ($contact) {
if ($contact && (strlen($contact->first_name) >=1 || strlen($contact->last_name) >=1)) {
$contact_name = $contact->first_name. ' '. $contact->last_name;
}
elseif($contact && (strlen($contact->email)))
$contact_name = $contact->email;
return $this->entity->name ?: $contact_name;
return $contact_name;
}
public function primary_contact_name()

View File

@ -263,7 +263,7 @@ class BaseRepository
//make sure we are creating an invite for a contact who belongs to the client only!
$contact = ClientContact::find($invitation['client_contact_id']);
if ($model->client_id == $contact->client_id);
if ($contact && $model->client_id == $contact->client_id);
{
$new_invitation = $invitation_factory_class::create($model->company_id, $model->user_id);
$new_invitation->{$lcfirst_resource_id} = $model->id;

View File

@ -46,7 +46,6 @@ class PaymentRepository extends BaseRepository
/**
* Saves and updates a payment. //todo refactor to handle refunds and payments.
*
*
* @param array $data the request object
* @param Payment $payment The Payment object
* @return Payment|null Payment $payment
@ -57,7 +56,7 @@ class PaymentRepository extends BaseRepository
return $this->applyPayment($data, $payment);
}
return $this->refundPayment($data, $payment);
return $payment;
}
/**
@ -68,50 +67,74 @@ class PaymentRepository extends BaseRepository
*/
private function applyPayment(array $data, Payment $payment): ?Payment
{
//check currencies here and fill the exchange rate data if necessary
if (!$payment->id) {
$this->processExchangeRates($data, $payment);
/*We only update the paid to date ONCE per payment*/
if (array_key_exists('invoices', $data) && is_array($data['invoices']) && count($data['invoices']) > 0) {
if($data['amount'] == '')
$data['amount'] = array_sum(array_column($data['invoices'], 'amount'));
$client = Client::find($data['client_id']);
$client->service()->updatePaidToDate($data['amount'])->save();
}
}
/*Fill the payment*/
$payment->fill($data);
$payment->status_id = Payment::STATUS_COMPLETED;
$payment->save();
/*Ensure payment number generated*/
if (!$payment->number || strlen($payment->number) == 0) {
$payment->number = $payment->client->getNextPaymentNumber($payment->client);
}
$payment->client->service()->updatePaidToDate($payment->amount)->save();
$invoice_totals = 0;
$credit_totals = 0;
if (array_key_exists('invoices', $data) && is_array($data['invoices'])) {
/*Iterate through invoices and apply payments*/
if (array_key_exists('invoices', $data) && is_array($data['invoices']) && count($data['invoices']) > 0) {
$invoice_totals = array_sum(array_column($data['invoices'], 'amount'));
$invoices = Invoice::whereIn('id', array_column($data['invoices'], 'invoice_id'))->get();
$payment->invoices()->saveMany($invoices);
info("iterating through payment invoices");
foreach ($data['invoices'] as $paid_invoice) {
$invoice = Invoice::whereId($paid_invoice['invoice_id'])->first();
$invoice = Invoice::whereId($paid_invoice['invoice_id'])->with('client')->first();
info("current client balance = {$invoice->client->balance}");
if ($invoice) {
$invoice->service()->applyPayment($payment, $paid_invoice['amount'])->save();
info("apply payment amount {$paid_invoice['amount']}");
$invoice = $invoice->service()->markSent()->applyPayment($payment, $paid_invoice['amount'])->save();
info("after processing invoice the client balance is now {$invoice->client->balance}");
}
}
} else {
//payment is made, but not to any invoice, therefore we are applying the payment to the clients credit
$payment->client->processUnappliedPayment($payment->amount);
//payment is made, but not to any invoice, therefore we are applying the payment to the clients paid_to_date only
$payment->client->service()->updatePaidToDate($payment->amount)->save();
}
if (array_key_exists('credits', $data) && is_array($data['credits'])) {
$credit_totals = array_sum(array_column($data['credits'], 'amount'));
$credits = Credit::whereIn('id', $this->transformKeys(array_column($data['credits'], 'credit_id')))->get();
$payment->credits()->saveMany($credits);
foreach ($data['credits'] as $paid_credit) {
@ -136,57 +159,9 @@ class PaymentRepository extends BaseRepository
}
$payment->save();
return $payment->fresh();
}
/**
* @deprecated Refundable trait replaces this.
*/
private function refundPayment(array $data, Payment $payment): string
{
// //temp variable to sum the total refund/credit amount
// $invoice_total_adjustment = 0;
// if (array_key_exists('invoices', $data) && is_array($data['invoices'])) {
// foreach ($data['invoices'] as $adjusted_invoice) {
// $invoice = Invoice::whereId($adjusted_invoice['invoice_id'])->first();
// $invoice_total_adjustment += $adjusted_invoice['amount'];
// if (array_key_exists('credits', $adjusted_invoice)) {
// //process and insert credit notes
// foreach ($adjusted_invoice['credits'] as $credit) {
// $credit = $this->credit_repo->save($credit, CreditFactory::create(auth()->user()->id, auth()->user()->id), $invoice);
// }
// } else {
// //todo - generate Credit Note for $amount on $invoice - the assumption here is that it is a FULL refund
// }
// }
// if (array_key_exists('amount', $data) && $data['amount'] != $invoice_total_adjustment)
// return 'Amount must equal the sum of invoice adjustments';
// }
// //adjust applied amount
// $payment->applied += $invoice_total_adjustment;
// //adjust clients paid to date
// $client = $payment->client;
// $client->paid_to_date += $invoice_total_adjustment;
// $payment->save();
// $client->save();
}
/**
* If the client is paying in a currency other than

View File

@ -37,7 +37,14 @@ class ApplyPayment extends AbstractService
->ledger()
->updatePaymentBalance($this->payment_amount*-1);
$this->payment->client->service()->updateBalance($this->payment_amount*-1)->save();
info("apply paymenet method - current client balance = {$this->payment->client->balance}");
info("reducing client balance by payment amount {$this->payment_amount}");
$this->invoice->client->service()->updateBalance($this->payment_amount*-1)->save();
// $this->invoice->client->service()->updateBalance($this->payment_amount*-1)->updatePaidToDate($this->payment_amount)->save();
info("post client balance = {$this->invoice->client->balance}");
/* Update Pivot Record amount */
$this->payment->invoices->each(function ($inv) {
@ -47,6 +54,10 @@ class ApplyPayment extends AbstractService
}
});
$this->invoice->fresh('client');
info("1 end of apply payment method the client balnace = {$this->invoice->client->balance}");
if ($this->invoice->hasPartial()) {
//is partial and amount is exactly the partial amount
if ($this->invoice->partial == $this->payment_amount) {
@ -61,9 +72,11 @@ class ApplyPayment extends AbstractService
} elseif ($this->payment_amount < $this->invoice->balance) { //partial invoice payment made
$this->invoice->service()->clearPartial()->setStatus(Invoice::STATUS_PARTIAL)->updateBalance($this->payment_amount*-1);
}
info("2 end of apply payment method the client balnace = {$this->invoice->client->balance}");
$this->invoice->service()->applyNumber()->save();
info("3 end of apply payment method the client balnace = {$this->invoice->client->balance}");
return $this->invoice;
}
}

View File

@ -48,10 +48,14 @@ class MarkSent extends AbstractService
->setDueDate()
->save();
info("marking invoice sent currently client balance = {$this->client->balance}");
$this->client->service()->updateBalance($this->invoice->balance)->save();
info("after marking invoice sent currently client balance = {$this->client->balance}");
$this->invoice->ledger()->updateInvoiceBalance($this->invoice->balance);
return $this->invoice;
return $this->invoice->fresh();
}
}

View File

@ -50,6 +50,9 @@ trait UserNotifies
$notifiable_methods = [];
$notifications = $company_user->notifications;
if(!$notifications)
return [];
if ($entity->user_id == $company_user->_user_id || $entity->assigned_user_id == $company_user->user_id) {
array_push($required_permissions, "all_user_notifications");
}

View File

@ -7,5 +7,6 @@ $factory->define(App\Models\Account::class, function (Faker $faker) {
return [
'default_company_id' => 1,
'key' => Str::random(32),
'report_errors' => 1,
];
});

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 35 KiB

View File

@ -171,6 +171,7 @@ class CompanyLedgerTest extends TestCase
$invoice = Invoice::find($this->decodePrimaryKey($acc['data']['id']));
//client->balance should = 10
$invoice->service()->markSent()->save();
$this->assertEquals($invoice->client->balance, 10);
@ -193,6 +194,7 @@ class CompanyLedgerTest extends TestCase
$invoice = Invoice::find($this->decodePrimaryKey($acc['data']['id']));
$invoice->service()->markSent()->save();
//client balance should = 20
$this->assertEquals($invoice->client->balance, 20);
$invoice_ledger = $invoice->company_ledger->sortByDesc('id')->first();
@ -211,7 +213,6 @@ class CompanyLedgerTest extends TestCase
],
],
'date' => '2020/12/11',
];
$response = $this->withHeaders([
@ -224,7 +225,8 @@ class CompanyLedgerTest extends TestCase
$payment = Payment::find($this->decodePrimaryKey($acc['data']['id']));
$payment_ledger = $payment->company_ledger->sortByDesc('id')->first();
$invoice->fresh();
info($payment->client->balance);
$this->assertEquals($payment->client->balance, $payment_ledger->balance);
$this->assertEquals($payment->client->paid_to_date, 10);