Merge pull request #4291 from turbo124/v5-develop

Fixes for credit PDF
This commit is contained in:
David Bomba 2020-11-11 11:14:01 +11:00 committed by GitHub
commit 0be452e144
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 136 additions and 54 deletions

View File

@ -364,10 +364,14 @@ class DemoMode extends Command
private function createProject($client, $assigned_user_id = null) private function createProject($client, $assigned_user_id = null)
{ {
$vendor = Project::factory()->create([ $project = Project::factory()->create([
'user_id' => $client->user->id, 'user_id' => $client->user->id,
'company_id' => $client->company_id, 'company_id' => $client->company_id,
'client_id' => $client->id,
]); ]);
$project->number = $this->getNextProjectNumber($project);
$project->save();
} }
private function createInvoice($client, $assigned_user_id = null) private function createInvoice($client, $assigned_user_id = null)

View File

@ -12,6 +12,10 @@
namespace App\Console\Commands; namespace App\Console\Commands;
use App\Jobs\Ninja\SendReminders; use App\Jobs\Ninja\SendReminders;
use App\Jobs\Util\WebHookHandler;
use App\Models\Invoice;
use App\Models\Quote;
use App\Models\Webhook;
use Illuminate\Console\Command; use Illuminate\Console\Command;
class SendRemindersCron extends Command class SendRemindersCron extends Command
@ -48,5 +52,34 @@ class SendRemindersCron extends Command
public function handle() public function handle()
{ {
SendReminders::dispatchNow(); SendReminders::dispatchNow();
$this->webHookOverdueInvoices();
$this->webHookExpiredQuotes();
}
private function webHookOverdueInvoices()
{
$invoices = Invoice::where('is_deleted', 0)
->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
->where('balance', '>', 0)
->whereDate('due_date', now()->subDays(1)->startOfDay())
->cursor();
$invoices->each(function ($invoice){
WebHookHandler::dispatch(Webhook::EVENT_LATE_INVOICE, $invoice);
});
}
private function webHookExpiredQuotes()
{
$quotes = Quote::where('is_deleted', 0)
->where('status_id', Quote::STATUS_SENT)
->whereDate('due_date', now()->subDays(1)->startOfDay())
->cursor();
$quotes->each(function ($quote){
WebHookHandler::dispatch(Webhook::EVENT_EXPIRED_QUOTE, $quote);
});
} }
} }

View File

@ -40,7 +40,7 @@ class InvitationController extends Controller
$key = $entity.'_id'; $key = $entity.'_id';
$entity_obj = 'App\Models\\'.ucfirst(Str::camel($entity)).'Invitation'; //todo sensitive to the route parameters here $entity_obj = 'App\Models\\'.ucfirst(Str::camel($entity)).'Invitation';
$invitation = $entity_obj::whereRaw('BINARY `key`= ?', [$invitation_key]) $invitation = $entity_obj::whereRaw('BINARY `key`= ?', [$invitation_key])
->with('contact.client') ->with('contact.client')

View File

@ -572,10 +572,10 @@ class CreditController extends BaseController
public function downloadPdf($invitation_key) public function downloadPdf($invitation_key)
{ {
$invitation = $this->credit_repository->getInvitationByKey($invitation_key); $invitation = $this->credit_repository->getInvitationByKey($invitation_key);
$contact = $invitation->contact; // $contact = $invitation->contact;
$credit = $invitation->credit; $credit = $invitation->credit;
$file_path = $credit->service()->getCreditPdf($contact); $file_path = $credit->service()->getCreditPdf($invitation);
return response()->download($file_path); return response()->download($file_path);
} }

View File

@ -34,8 +34,10 @@ use App\Jobs\Util\UnlinkFile;
use App\Models\Client; use App\Models\Client;
use App\Models\Invoice; use App\Models\Invoice;
use App\Models\InvoiceInvitation; use App\Models\InvoiceInvitation;
use App\Models\Quote;
use App\Repositories\InvoiceRepository; use App\Repositories\InvoiceRepository;
use App\Transformers\InvoiceTransformer; use App\Transformers\InvoiceTransformer;
use App\Transformers\QuoteTransformer;
use App\Utils\Ninja; use App\Utils\Ninja;
use App\Utils\TempFile; use App\Utils\TempFile;
use App\Utils\Traits\MakesHash; use App\Utils\Traits\MakesHash;
@ -639,7 +641,12 @@ class InvoiceController extends BaseController
break; break;
case 'clone_to_quote': case 'clone_to_quote':
$quote = CloneInvoiceToQuoteFactory::create($invoice, auth()->user()->id); $quote = CloneInvoiceToQuoteFactory::create($invoice, auth()->user()->id);
// todo build the quote transformer and return response here
$this->entity_transformer = QuoteTransformer::class;
$this->entity_type = Quote::class;
return $this->itemResponse($quote);
break; break;
case 'history': case 'history':
// code... // code...

View File

@ -23,9 +23,11 @@ use App\Http\Requests\RecurringQuote\ShowRecurringQuoteRequest;
use App\Http\Requests\RecurringQuote\StoreRecurringQuoteRequest; use App\Http\Requests\RecurringQuote\StoreRecurringQuoteRequest;
use App\Http\Requests\RecurringQuote\UpdateRecurringQuoteRequest; use App\Http\Requests\RecurringQuote\UpdateRecurringQuoteRequest;
use App\Jobs\Entity\ActionEntity; use App\Jobs\Entity\ActionEntity;
use App\Models\Quote;
use App\Models\RecurringQuote; use App\Models\RecurringQuote;
use App\Repositories\BaseRepository; use App\Repositories\BaseRepository;
use App\Repositories\RecurringQuoteRepository; use App\Repositories\RecurringQuoteRepository;
use App\Transformers\QuoteTransformer;
use App\Transformers\RecurringQuoteTransformer; use App\Transformers\RecurringQuoteTransformer;
use App\Utils\Traits\MakesHash; use App\Utils\Traits\MakesHash;
use Illuminate\Http\Request; use Illuminate\Http\Request;
@ -583,8 +585,11 @@ class RecurringQuoteController extends BaseController
// return $this->itemResponse($recurring_invoice); // return $this->itemResponse($recurring_invoice);
break; break;
case 'clone_to_quote': case 'clone_to_quote':
// $quote = CloneRecurringQuoteToQuoteFactory::create($recurring_invoice, auth()->user()->id); $quote = CloneRecurringQuoteToQuoteFactory::create($recurring_invoice, auth()->user()->id);
// todo build the quote transformer and return response here $this->entity_transformer = QuoteTransformer::class;
$this->entity_type = Quote::class;
return $this->itemResponse($quote);
break; break;
case 'history': case 'history':
// code... // code...

View File

@ -84,4 +84,9 @@ class SelfUpdateController extends BaseController
return response()->json(['message' => ''], 200); return response()->json(['message' => ''], 200);
} }
public function checkVersion()
{
return trim(file_get_contents(config('ninja.version_url')));
}
} }

View File

@ -55,7 +55,7 @@ class ContactTokenAuth
//stateless, don't remember the contact. //stateless, don't remember the contact.
auth()->guard('contact')->login($client_contact, false); auth()->guard('contact')->login($client_contact, false);
event(new ContactLoggedIn($client_contact, $client_contact->company, Ninja::eventVars())); //todo event(new ContactLoggedIn($client_contact, $client_contact->company, Ninja::eventVars()));
} else { } else {
$error = [ $error = [
'message' => 'Invalid token', 'message' => 'Invalid token',

View File

@ -75,7 +75,7 @@ class StoreClientRequest extends Request
$input = $this->all(); $input = $this->all();
//@todo implement feature permissions for > 100 clients //@todo implement feature permissions for > 100 clients
//
$settings = ClientSettings::defaults(); $settings = ClientSettings::defaults();
if (array_key_exists('settings', $input) && ! empty($input['settings'])) { if (array_key_exists('settings', $input) && ! empty($input['settings'])) {

View File

@ -77,7 +77,7 @@ class StorePaymentRequest extends Request
} }
if (! isset($input['amount']) || $input['amount'] == 0) { if (! isset($input['amount']) || $input['amount'] == 0) {
$input['amount'] = $invoices_total - $credits_total; //todo the payment amount is always less the credit amount applied $input['amount'] = $invoices_total - $credits_total;
} }
$input['is_manual'] = true; $input['is_manual'] = true;

View File

@ -109,22 +109,20 @@ class StoreRecurringInvoiceRequest extends Request
} }
$this->replace($input); $this->replace($input);
}
private function setAutoBillFlag($auto_bill)
{
if($auto_bill == 'always')
return true;
if($auto_bill == 'off')
return false;
//todo do we need to handle optin / optout here?
}
public function messages()
{
return [];
} }
private function setAutoBillFlag($auto_bill)
{
if($auto_bill == 'always')
return true;
if($auto_bill == 'off')
return false;
}
public function messages()
{
return [];
}
} }

View File

@ -46,16 +46,28 @@ class ValidCreditsPresentRule implements Rule
{ {
//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'))) { // if (request()->input('credits') && is_array(request()->input('credits'))) {
foreach (request()->input('credits') as $credit) { // foreach (request()->input('credits') as $credit) {
$cred = Credit::find($this->decodePrimaryKey($credit['credit_id'])); // $cred = Credit::find($this->decodePrimaryKey($credit['credit_id']));
if (! $cred || $cred->balance == 0) { // if (! $cred || $cred->balance == 0) {
return false; // return false;
} // }
} // }
// }
// return true;
if (request()->input('credits') && is_array(request()->input('credits'))) {
$credit_collection = Credit::whereIn('id', $this->transformKeys(array_column(request()->input('credits'), 'credit_id')))
->where('balance', '>', 0)
->get();
return $credit_collection->count() == count(request()->input('credits'));
} }
return true; return true;
} }
} }

View File

@ -285,10 +285,13 @@ class SendReminders implements ShouldQueue
* @param Invoice $invoice * @param Invoice $invoice
* @param float $amount The fee amount * @param float $amount The fee amount
* @param float $percent The fee percentage amount * @param float $percent The fee percentage amount
*
* @return Invoice * @return Invoice
*/ */
private function setLateFee($invoice, $amount, $percent) :Invoice private function setLateFee($invoice, $amount, $percent) :Invoice
{ {
$temp_invoice_balance = $invoice->balance;
if ($amount <= 0 && $percent <= 0) if ($amount <= 0 && $percent <= 0)
return $invoice; return $invoice;
@ -314,7 +317,9 @@ class SendReminders implements ShouldQueue
/**Refresh Invoice values*/ /**Refresh Invoice values*/
$invoice = $invoice->calc()->getInvoice(); $invoice = $invoice->calc()->getInvoice();
//@todo update the ledger!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! $this->invoice->client->service()->updateBalance($this->invoice->balance - $temp_invoice_balance)->save();
$this->invoice->ledger()->updateInvoiceBalance($this->invoice->balance - $temp_invoice_balance);
return $invoice; return $invoice;
} }

View File

@ -1,5 +1,13 @@
<?php <?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\Jobs\Util; namespace App\Jobs\Util;
use App\Models\Webhook; use App\Models\Webhook;
@ -40,7 +48,7 @@ class WebhookHandler implements ShouldQueue
* @return bool * @return bool
*/ */
public function handle() :bool public function handle() :bool
{ {//todo set multidb here
if (! $this->entity->company || $this->entity->company->is_disabled) { if (! $this->entity->company || $this->entity->company->is_disabled) {
return true; return true;
} }

View File

@ -384,7 +384,7 @@ class Client extends BaseModel implements HasLocalePreference
return array_search($model->id, $transformed_ids); return array_search($model->id, $transformed_ids);
}); });
} else { } else {
$gateways = $this->company->company_gateways; //todo perhaps we can remove this or keep as a catch all. $gateways = $this->company->company_gateways;
} }
foreach ($gateways as $gateway) { foreach ($gateways as $gateway) {

View File

@ -224,8 +224,6 @@ class Credit extends BaseModel
if ($this->balance == 0) { if ($this->balance == 0) {
$this->status_id = self::STATUS_APPLIED; $this->status_id = self::STATUS_APPLIED;
$this->save(); $this->save();
//event(new InvoiceWasPaid($this, $this->company));//todo
return; return;
} }
@ -248,10 +246,10 @@ class Credit extends BaseModel
if (! $invitation) { if (! $invitation) {
event(new CreditWasUpdated($this, $this->company, Ninja::eventVars())); event(new CreditWasUpdated($this, $this->company, Ninja::eventVars()));
CreateEntityPdf::dispatchNow($this, $this->company, $this->client->primary_contact()->first()); CreateEntityPdf::dispatchNow($this->invitations->first());
} else { } else {
event(new CreditWasUpdated($this, $this->company, Ninja::eventVars())); event(new CreditWasUpdated($this, $this->company, Ninja::eventVars()));
CreateEntityPdf::dispatchNow($invitation->credit, $invitation->company, $invitation->contact); CreateEntityPdf::dispatchNow($invitation);
} }
return $storage_path; return $storage_path;

View File

@ -40,7 +40,10 @@ class Webhook extends BaseModel
const EVENT_UPDATE_TASK = 19; const EVENT_UPDATE_TASK = 19;
const EVENT_DELETE_TASK = 20; const EVENT_DELETE_TASK = 20;
const EVENT_APPROVE_QUOTE = 21; const EVENT_APPROVE_QUOTE = 21;
const EVENT_LATE_INVOICE = 22;
const EVENT_EXPIRED_QUOTE = 23;
const EVENT_REMIND_INVOICE = 24;
public static $valid_events = [ public static $valid_events = [
self::EVENT_CREATE_CLIENT, self::EVENT_CREATE_CLIENT,
self::EVENT_CREATE_PAYMENT, self::EVENT_CREATE_PAYMENT,

View File

@ -29,9 +29,9 @@ class CreditService
$this->credit = $credit; $this->credit = $credit;
} }
public function getCreditPdf($contact) public function getCreditPdf($invitation)
{ {
return (new GetCreditPdf($this->credit, $contact))->run(); return (new GetCreditPdf($invitation))->run();
} }
/** /**

View File

@ -19,14 +19,17 @@ use Illuminate\Support\Facades\Storage;
class GetCreditPdf extends AbstractService class GetCreditPdf extends AbstractService
{ {
private $credit; public $credit;
private $contact; public $contact;
public function __construct(Credit $credit, ClientContact $contact = null) public $invitation;
public function __construct($invitation)
{ {
$this->credit = $credit; $this->invitation = $invitation;
$this->contact = $contact; $this->credit = $invitation->credit;
$this->contact = $invitation->contact;
} }
public function run() public function run()
@ -44,7 +47,7 @@ class GetCreditPdf extends AbstractService
$file = Storage::disk($disk)->exists($file_path); $file = Storage::disk($disk)->exists($file_path);
if (! $file) { if (! $file) {
$file_path = CreateEntityPdf::dispatchNow($this->credit, $this->credit->company, $this->contact); $file_path = CreateEntityPdf::dispatchNow($this->invitation);
} }
return Storage::disk($disk)->path($file_path); return Storage::disk($disk)->path($file_path);

View File

@ -74,7 +74,7 @@ class AccountTransformer extends EntityTransformer
'utm_content' => (string) $account->utm_content, 'utm_content' => (string) $account->utm_content,
'utm_term' => (string) $account->utm_term, 'utm_term' => (string) $account->utm_term,
'referral_code' => (string) $account->referral_code, 'referral_code' => (string) $account->referral_code,
'latest_version' => (string) $account->latest_version, 'latest_version' => (string) trim($account->latest_version),
'current_version' => (string) config('ninja.app_version'), 'current_version' => (string) config('ninja.app_version'),
'updated_at' => (int) $account->updated_at, 'updated_at' => (int) $account->updated_at,
'archived_at' => (int) $account->deleted_at, 'archived_at' => (int) $account->deleted_at,

View File

@ -9,7 +9,8 @@
"expenses", "expenses",
"CRM", "CRM",
"Credit card billing", "Credit card billing",
"projects" "projects",
"tasks"
], ],
"license": "Attribution Assurance License", "license": "Attribution Assurance License",
"authors": [ "authors": [