Merge pull request #6982 from turbo124/v5-develop

Hide deleted tasks
This commit is contained in:
David Bomba 2021-11-19 15:36:57 +11:00 committed by GitHub
commit 829722d102
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
46 changed files with 548 additions and 60 deletions

View File

@ -1 +1 @@
5.3.31
5.3.32

View File

@ -0,0 +1,106 @@
<?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://www.elastic.co/licensing/elastic-license
*/
namespace App\Console\Commands;
use App\Libraries\MultiDB;
use App\Models\Backup;
use App\Models\Design;
use Illuminate\Console\Command;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Lang;
use Illuminate\Support\Facades\Storage;
use stdClass;
class TranslationsExport extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'ninja:translations';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Transform translations to json';
private array $langs = [
'ar',
'ca',
'cs',
'da',
'de',
'el',
'en',
'en_GB',
'es',
'es_ES',
'fa',
'fi',
'fr',
'fr_CA',
'hr',
'it',
'ja',
'lt',
'lv_LV',
'mk_MK',
'nb_NO',
'nl',
'pl',
'pt_BR',
'pt_PT',
'ro',
'ru_RU',
'sl',
'sq',
'sv',
'th',
'tr_TR',
'zh_TW'
];
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
Storage::makeDirectory(storage_path('lang'));
foreach($this->langs as $lang)
{
Storage::makeDirectory(storage_path("lang/{$lang}"));
$translations = Lang::getLoader()->load($lang,'texts');
Storage::put(storage_path("lang/{$lang}/{$lang}.json"), json_encode(Arr::dot($translations), JSON_UNESCAPED_UNICODE));
}
}
}

View File

@ -28,7 +28,7 @@ class ClientFactory
$client->public_notes = '';
$client->balance = 0;
$client->paid_to_date = 0;
$client->country_id = 4;
$client->country_id = 840;
$client->is_deleted = 0;
$client->client_hash = Str::random(40);
$client->settings = ClientSettings::defaults();

View File

@ -70,7 +70,7 @@ class GmailTransport extends Transport
if($child->getContentType() != 'text/plain')
{
$this->gmail->attach(TempFile::filePath($child->getBody(), $child->getHeaders()->get('Content-Type')->getParameter('name') ));
$this->gmail->attach(TempFile::filePath($child->getBody(), $child->getHeaders()->get('Content-Type')->getParameter('name') ));
}

View File

@ -58,7 +58,7 @@ class ContactForgotPasswordController extends Controller
*/
public function showLinkRequestForm(Request $request)
{
$account_id = $request->get('account_id');
$account_id = $request->has('account_id') ? $request->get('account_id') : 1;
$account = Account::find($account_id);
$company = $account->companies->first();

View File

@ -36,12 +36,9 @@ class ContactLoginController extends Controller
public function showLoginForm(Request $request)
{
//if we are on the root domain invoicing.co do not show any company logos
// if(Ninja::isHosted() && count(explode('.', request()->getHost())) == 2){
// $company = null;
// }else
$company = false;
$account = false;
if($request->has('company_key')){
MultiDB::findAndSetDbByCompanyKey($request->input('company_key'));
@ -65,13 +62,16 @@ class ContactLoginController extends Controller
}
elseif (Ninja::isSelfHost()) {
$company = Account::first()->default_company;
$account = Account::first();
$company = $account->default_company;
} else {
$company = null;
}
$account_id = $request->get('account_id');
$account = Account::find($account_id);
if(!$account){
$account_id = $request->get('account_id');
$account = Account::find($account_id);
}
return $this->render('auth.login', ['account' => $account, 'company' => $company]);

View File

@ -68,7 +68,7 @@ class ContactResetPasswordController extends Controller
*/
public function showResetForm(Request $request, $token = null)
{
$account_id = $request->get('account_id');
$account_id = $request->has('account_id') ? $request->get('account_id') : 1;
$account = Account::find($account_id);
$db = $account->companies->first()->db;
$company = $account->companies->first();

View File

@ -334,7 +334,7 @@ class BaseController extends Controller
},
'company.expense_categories'=> function ($query) use ($updated_at, $user) {
$query->where('updated_at', '>=', $updated_at);
$query->whereNotNull('updated_at');
},
'company.task_statuses'=> function ($query) use ($updated_at, $user) {
$query->whereNotNull('updated_at');
@ -568,7 +568,7 @@ class BaseController extends Controller
},
'company.expense_categories'=> function ($query) use ($created_at, $user) {
$query->where('created_at', '>=', $created_at);
$query->whereNotNull('created_at');
},
'company.task_statuses'=> function ($query) use ($created_at, $user) {

View File

@ -29,7 +29,9 @@ class SubscriptionController extends Controller
->where('client_id', auth('contact')->user()->client->id)
->where('company_id', auth('contact')->user()->client->company_id)
->where('status_id', RecurringInvoice::STATUS_ACTIVE)
->where('is_deleted', 0)
->whereNotNull('subscription_id')
->withTrashed()
->count();
if($count == 0)

View File

@ -200,6 +200,7 @@ class CreditController extends BaseController
$credit = $credit->service()
->fillDefaults()
->triggeredActions($request)
->save();
event(new CreditWasCreated($credit, $credit->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
@ -377,7 +378,9 @@ class CreditController extends BaseController
$credit = $this->credit_repository->save($request->all(), $credit);
$credit->service()->deletePdf();
$credit->service()
->triggeredActions($request)
->deletePdf();
event(new CreditWasUpdated($credit, $credit->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));

View File

@ -174,12 +174,7 @@ class PreviewController extends BaseController
MultiDB::setDb($company->db);
if($request->input('entity') == 'invoice'){
$repo = new InvoiceRepository();
$entity_obj = InvoiceFactory::create($company->id, auth()->user()->id);
$class = Invoice::class;
}
elseif($request->input('entity') == 'quote'){
if($request->input('entity') == 'quote'){
$repo = new QuoteRepository();
$entity_obj = QuoteFactory::create($company->id, auth()->user()->id);
$class = Quote::class;
@ -195,7 +190,11 @@ class PreviewController extends BaseController
$entity_obj = RecurringInvoiceFactory::create($company->id, auth()->user()->id);
$class = RecurringInvoice::class;
}
else { //assume it is either an invoice or a null object
$repo = new InvoiceRepository();
$entity_obj = InvoiceFactory::create($company->id, auth()->user()->id);
$class = Invoice::class;
}
try {

View File

@ -189,6 +189,9 @@ class BillingPortalPurchase extends Component
$this->coupon = request()->query('coupon');
$this->handleCoupon();
}
elseif(strlen($this->subscription->promo_code) == 0 && $this->subscription->promo_discount > 0){
$this->price = $this->subscription->promo_price;
}
}
/**

View File

@ -141,7 +141,7 @@ class RequiredClientInfo extends Component
$_field = $this->mappings[$field['name']];
if (Str::startsWith($field['name'], 'client_')) {
if (empty($this->contact->client->{$_field}) || is_null($this->contact->client->{$_field})) {
if (empty($this->contact->client->{$_field}) || is_null($this->contact->client->{$_field}) || $this->contact->client->{$_field} = 840) {
$this->show_form = true;
} else {
$this->fields[$index]['filled'] = true;
@ -149,7 +149,7 @@ class RequiredClientInfo extends Component
}
if (Str::startsWith($field['name'], 'contact_')) {
if ((empty($this->contact->{$_field}) || is_null($this->contact->{$_field}))) {
if ((empty($this->contact->{$_field}) || is_null($this->contact->{$_field})) || $this->contact->client->{$_field} = 840) {
$this->show_form = true;
} else {
$this->fields[$index]['filled'] = true;

View File

@ -36,6 +36,7 @@ class TasksTable extends Component
{
$query = Task::query()
->where('company_id', $this->company->id)
->where('is_deleted', false)
->where('client_id', auth('contact')->user()->client->id);
if ($this->company->getSetting('show_all_tasks_client_portal') === 'invoiced') {

View File

@ -84,6 +84,8 @@ class WepaySignup extends Component
public function submit()
{
MultiDB::setDb($this->company->db);
$data = $this->validate($this->rules);
//need to create or get a new WePay CompanyGateway

View File

@ -34,12 +34,12 @@ class CreateAccountRequest extends Request
public function rules()
{
return [
//'email' => 'required|string|email|max:100',
'first_name' => 'string|max:100',
'last_name' => 'string:max:100',
'password' => 'required|string|min:6',
'email' => 'bail|required|email:rfc,dns',
'email' => new NewUniqueUserRule(),
// 'email' => 'bail|required|email:rfc,dns',
// 'email' => new NewUniqueUserRule(),
'email' => ['required', 'email:rfc,dns', new NewUniqueUserRule],
'privacy_policy' => 'required|boolean',
'terms_of_service' => 'required|boolean',
];

View File

@ -28,7 +28,8 @@ class ShowDocumentRequest extends FormRequest
public function authorize()
{
return auth()->user('contact')->client->id == $this->document->documentable_id
|| $this->document->documentable->client_id == auth()->user('contact')->client->id;
|| $this->document->documentable->client_id == auth()->user('contact')->client->id
|| $this->document->company_id == auth()->user('contact')->company->id;
}
/**

View File

@ -69,6 +69,7 @@ class ApplyCreditPayment implements ShouldQueue
$this->credit
->service()
->markSent()
->setStatus(Credit::STATUS_APPLIED)
->adjustBalance($this->amount * -1)
->updatePaidToDate($this->amount)
@ -78,6 +79,7 @@ class ApplyCreditPayment implements ShouldQueue
$this->credit
->service()
->markSent()
->setStatus(Credit::STATUS_PARTIAL)
->adjustBalance($this->amount * -1)
->updatePaidToDate($this->amount)

View File

@ -0,0 +1,86 @@
<?php
/**
* Quote Ninja (https://quoteninja.com).
*
* @link https://github.com/quoteninja/quoteninja source repository
*
* @copyright Copyright (c) 2021. Quote Ninja LLC (https://quoteninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Listeners\Quote;
use App\Jobs\Mail\NinjaMailer;
use App\Jobs\Mail\NinjaMailerJob;
use App\Jobs\Mail\NinjaMailerObject;
use App\Libraries\MultiDB;
use App\Mail\Admin\EntityCreatedObject;
use App\Mail\Admin\QuoteApprovedObject;
use App\Notifications\Admin\EntitySentNotification;
use App\Utils\Traits\Notifications\UserNotifies;
use Illuminate\Contracts\Queue\ShouldQueue;
class QuoteApprovedNotification implements ShouldQueue
{
use UserNotifies;
public function __construct()
{
}
/**
* Handle the event.
*
* @param object $event
* @return void
*/
public function handle($event)
{
MultiDB::setDb($event->company->db);
$first_notification_sent = true;
$quote = $event->quote;
$nmo = new NinjaMailerObject;
$nmo->mailable = new NinjaMailer( (new QuoteApprovedObject($quote, $event->company))->build() );
$nmo->company = $quote->company;
$nmo->settings = $quote->company->settings;
/* We loop through each user and determine whether they need to be notified */
foreach ($event->company->company_users as $company_user) {
/* The User */
$user = $company_user->user;
if(!$user)
continue;
/* This is only here to handle the alternate message channels - ie Slack */
// $notification = new EntitySentNotification($event->invitation, 'quote');
/* Returns an array of notification methods */
$methods = $this->findUserNotificationTypes($quote->invitations()->first(), $company_user, 'quote', ['all_notifications', 'quote_approved', 'quote_approved_all']);
/* If one of the methods is email then we fire the EntitySentMailer */
if (($key = array_search('mail', $methods)) !== false) {
unset($methods[$key]);
$nmo->to_user = $user;
NinjaMailerJob::dispatch($nmo);
/* This prevents more than one notification being sent */
$first_notification_sent = false;
}
/* Override the methods in the Notification Class */
// $notification->method = $methods;
// Notify on the alternate channels
// $user->notify($notification);
}
}
}

View File

@ -69,7 +69,7 @@ class ClientPaymentFailureObject
/* Set customized translations _NOW_ */
$t->replace(Ninja::transformTranslations($this->company->settings));
$this->invoices = Invoice::whereIn('id', $this->transformKeys(array_column($this->payment_hash->invoices(), 'invoice_id')))->get();
$this->invoices = Invoice::withTrashed()->whereIn('id', $this->transformKeys(array_column($this->payment_hash->invoices(), 'invoice_id')))->get();
$mail_obj = new stdClass;
$mail_obj->amount = $this->getAmount();
@ -101,8 +101,13 @@ class ClientPaymentFailureObject
private function getData()
{
$invitation = $this->invoices->first()->invitations->first();
if(!$invitation)
throw new \Exception('Unable to find invitation for reference');
$signature = $this->client->getSetting('email_signature');
$html_variables = (new HtmlEngine($this->invoices->first()->invitations->first()))->makeValues();
$html_variables = (new HtmlEngine($invitation))->makeValues();
$signature = str_replace(array_keys($html_variables), array_values($html_variables), $signature);
$data = [

View File

@ -0,0 +1,103 @@
<?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://www.elastic.co/licensing/elastic-license
*/
namespace App\Mail\Admin;
use App\Libraries\MultiDB;
use App\Models\Company;
use App\Models\Quote;
use App\Utils\Ninja;
use App\Utils\Number;
use Illuminate\Support\Facades\App;
use stdClass;
class QuoteApprovedObject
{
public $quote;
public $company;
public $settings;
public function __construct(Quote $quote, Company $company)
{
$this->quote = $quote;
$this->company = $company;
}
public function build()
{
MultiDB::setDb($this->company->db);
if(!$this->quote)
return;
App::forgetInstance('translator');
/* Init a new copy of the translator*/
$t = app('translator');
/* Set the locale*/
App::setLocale($this->company->getLocale());
/* Set customized translations _NOW_ */
$t->replace(Ninja::transformTranslations($this->company->settings));
$mail_obj = new stdClass;
$mail_obj->amount = $this->getAmount();
$mail_obj->subject = $this->getSubject();
$mail_obj->data = $this->getData();
$mail_obj->markdown = 'email.admin.generic';
$mail_obj->tag = $this->company->company_key;
return $mail_obj;
}
private function getAmount()
{
return Number::formatMoney($this->quote->amount, $this->quote->client);
}
private function getSubject()
{
return
ctrans(
"texts.notification_quote_approved_subject",
[
'client' => $this->quote->client->present()->name(),
'invoice' => $this->quote->number,
]
);
}
private function getData()
{
$settings = $this->quote->client->getMergedSettings();
$data = [
'title' => $this->getSubject(),
'message' => ctrans(
"texts.notification_quote_approved",
[
'amount' => $this->getAmount(),
'client' => $this->quote->client->present()->name(),
'invoice' => $this->quote->number,
]
),
'url' => $this->quote->invitations->first()->getAdminLink(),
'button' => ctrans("texts.view_quote"),
'signature' => $settings->email_signature,
'logo' => $this->company->present()->logo(),
'settings' => $settings,
'whitelabel' => $this->company->account->isPaid() ? true : false,
];
return $data;
}
}

View File

@ -136,4 +136,4 @@ class InvoiceEmailEngine extends BaseEmailEngine
return $this;
}
}
}

View File

@ -18,6 +18,7 @@ use App\Models\ClientContact;
use App\Models\User;
use App\Services\PdfMaker\Designs\Utilities\DesignHelpers;
use App\Utils\HtmlEngine;
use App\Utils\Ninja;
use App\Utils\TemplateEngine;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
@ -114,6 +115,10 @@ class TemplateEmail extends Mailable
$message->invitation = $this->invitation;
});
/*In the hosted platform we need to slow things down a little for Storage to catch up.*/
if(Ninja::isHosted())
sleep(1);
foreach ($this->build_email->getAttachments() as $file) {
if(is_string($file))

View File

@ -195,6 +195,8 @@ class BaseModel extends Model
// Remove any runs of periods (thanks falstro!)
$formatted_number = mb_ereg_replace("([\.]{2,})", '', $formatted_number);
$formatted_number = str_replace(" ", "_", $formatted_number);
return $formatted_number;
}

View File

@ -222,6 +222,9 @@ class RecurringInvoice extends BaseModel
return null;
}
nlog("frequency = $this->frequency_id");
nlog("frequency = $this->next_send_date");
$offset = $this->client->timezone_offset();
/*

View File

@ -404,7 +404,7 @@ class BaseDriver extends AbstractPaymentDriver
throw new PaymentFailed($error, $e->getCode());
}
public function sendFailureMail(string $error)
public function sendFailureMail($error = '')
{
if (!is_null($this->payment_hash)) {

View File

@ -86,6 +86,7 @@ class Bancontact implements MethodInterface
'webhookUrl' => $this->mollie->company_gateway->webhookUrl(),
'metadata' => [
'client_id' => $this->mollie->client->hashed_id,
'hash' => $this->mollie->payment_hash->hash
],
]);

View File

@ -89,6 +89,7 @@ class BankTransfer implements MethodInterface
'webhookUrl' => $this->mollie->company_gateway->webhookUrl(),
'metadata' => [
'client_id' => $this->mollie->client->hashed_id,
'hash' => $this->mollie->payment_hash->hash
],
]);

View File

@ -86,6 +86,7 @@ class IDEAL implements MethodInterface
'webhookUrl' => $this->mollie->company_gateway->webhookUrl(),
'metadata' => [
'client_id' => $this->mollie->client->hashed_id,
'hash' => $this->mollie->payment_hash->hash
],
]);

View File

@ -86,6 +86,7 @@ class KBC implements MethodInterface
'webhookUrl' => $this->mollie->company_gateway->webhookUrl(),
'metadata' => [
'client_id' => $this->mollie->client->hashed_id,
'hash' => $this->mollie->payment_hash->hash
],
]);

View File

@ -93,7 +93,7 @@ class PayPalExpressPaymentDriver extends BaseDriver
return $response->redirect();
}
$this->sendFailureMail($response->getMessage());
$this->sendFailureMail($response->getMessage() ?: '');
$message = [
'server_response' => $response->getMessage(),
@ -151,7 +151,7 @@ class PayPalExpressPaymentDriver extends BaseDriver
$data = $response->getData();
$this->sendFailureMail($response->getMessage());
$this->sendFailureMail($response->getMessage() ?: '');
$message = [
'server_response' => $data['L_LONGMESSAGE0'],

View File

@ -524,14 +524,25 @@ class StripePaymentDriver extends BaseDriver
if ($request->type === 'charge.succeeded' || $request->type === 'payment_intent.succeeded') {
foreach ($request->data as $transaction) {
$payment = Payment::query()
->where('transaction_reference', $transaction['payment_intent'])
if(array_key_exists('payment_intent', $transaction))
{
$payment = Payment::query()
->where('company_id', $request->getCompany()->id)
->where(function ($query) use ($transaction) {
$query->where('transaction_reference', $transaction['payment_intent'])
->orWhere('transaction_reference', $transaction['id']);
})
->first();
}
else
{
$payment = Payment::query()
->where('company_id', $request->getCompany()->id)
->where('transaction_reference', $transaction['id'])
->first();
}
if ($payment) {
$payment->status_id = Payment::STATUS_COMPLETED;
$payment->save();

View File

@ -208,8 +208,8 @@ class WePayPaymentDriver extends BaseDriver
return 'Processed successfully';
} elseif ($objectType == 'account') {
if ($accountId != $objectId) {
throw new \Exception('Unknown account');
if ($accountId !== $objectId) {
throw new \Exception('Unknown account ' . $accountId . ' does not equal '.$objectId);
}
$wepayAccount = $this->wepay->request('account', array(

View File

@ -171,6 +171,7 @@ use App\Listeners\Payment\PaymentEmailedActivity;
use App\Listeners\Payment\PaymentNotification;
use App\Listeners\Payment\PaymentRestoredActivity;
use App\Listeners\Quote\QuoteApprovedActivity;
use App\Listeners\Quote\QuoteApprovedNotification;
use App\Listeners\Quote\QuoteApprovedWebhook;
use App\Listeners\Quote\QuoteArchivedActivity;
use App\Listeners\Quote\QuoteCreatedNotification;
@ -437,6 +438,7 @@ class EventServiceProvider extends ServiceProvider
ReachWorkflowSettings::class,
QuoteApprovedActivity::class,
QuoteApprovedWebhook::class,
QuoteApprovedNotification::class,
],
QuoteWasCreated::class => [
CreatedQuoteActivity::class,

View File

@ -22,7 +22,9 @@ use Illuminate\Support\Str;
*/
class ClientContactRepository extends BaseRepository
{
public $is_primary;
private bool $is_primary = true;
private bool $set_send_email_on_contact = false;
public function save(array $data, Client $client) : void
{
@ -36,13 +38,20 @@ class ClientContactRepository extends BaseRepository
ClientContact::destroy($contact);
});
$this->is_primary = true;
/* Ensure send_email always exists in at least one contact */
if(!$contacts->contains('send_email', true))
$this->set_send_email_on_contact = true;
/* Set first record to primary - always */
$contacts = $contacts->sortByDesc('is_primary')->map(function ($contact) {
$contact['is_primary'] = $this->is_primary;
$this->is_primary = false;
if($this->set_send_email_on_contact){
$contact['send_email'] = true;
$this->set_send_email_on_contact = false;
}
return $contact;
});

View File

@ -129,7 +129,7 @@ class PaymentRepository extends BaseRepository {
//todo optimize this into a single query
foreach ($data['invoices'] as $paid_invoice) {
$invoice = Invoice::whereId($paid_invoice['invoice_id'])->first();
$invoice = Invoice::withTrashed()->whereId($paid_invoice['invoice_id'])->first();
if ($invoice) {
$invoice = $invoice->service()

View File

@ -14,6 +14,7 @@ namespace App\Services\Credit;
use App\Jobs\Util\UnlinkFile;
use App\Models\Credit;
use App\Services\Credit\CreateInvitations;
use App\Services\Credit\TriggeredActions;
use App\Utils\Traits\MakesHash;
class CreditService
@ -150,6 +151,13 @@ class CreditService
return $this;
}
public function triggeredActions($request)
{
$this->invoice = (new TriggeredActions($this->credit, $request))->run();
return $this;
}
/**
* Saves the credit.
* @return Credit object

View File

@ -0,0 +1,76 @@
<?php
/**
* Credit Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Credit Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Services\Credit;
use App\Events\Credit\CreditWasEmailed;
use App\Jobs\Entity\EmailEntity;
use App\Models\Credit;
use App\Services\AbstractService;
use App\Utils\Ninja;
use App\Utils\Traits\GeneratesCounter;
use Illuminate\Http\Request;
class TriggeredActions extends AbstractService
{
use GeneratesCounter;
private $request;
private $credit;
public function __construct(Credit $credit, Request $request)
{
$this->request = $request;
$this->credit = $credit;
}
public function run()
{
// if ($this->request->has('auto_bill') && $this->request->input('auto_bill') == 'true') {
// $this->credit = $this->credit->service()->autoBill()->save();
// }
// if ($this->request->has('paid') && $this->request->input('paid') == 'true') {
// $this->credit = $this->credit->service()->markPaid()->save();
// }
// if ($this->request->has('amount_paid') && is_numeric($this->request->input('amount_paid')) ) {
// $this->credit = $this->credit->service()->applyPaymentAmount($this->request->input('amount_paid'))->save();
// }
if ($this->request->has('send_email') && $this->request->input('send_email') == 'true') {
$this->sendEmail();
}
if ($this->request->has('mark_sent') && $this->request->input('mark_sent') == 'true') {
$this->credit = $this->credit->service()->markSent()->save();
}
return $this->credit;
}
private function sendEmail()
{
$reminder_template = $this->credit->calculateTemplate('credit');
$this->credit->invitations->load('contact.client.country', 'credit.client.country', 'credit.company')->each(function ($invitation) use ($reminder_template) {
EmailEntity::dispatch($invitation, $this->credit->company, $reminder_template);
});
if ($this->credit->invitations->count() > 0) {
event(new CreditWasEmailed($this->credit->invitations->first(), $this->credit->company, Ninja::eventVars(), 'credit'));
}
}
}

View File

@ -47,7 +47,7 @@ class ApplyPayment extends AbstractService
$amount_paid = $this->payment_amount * -1;
$this->invoice->service()->clearPartial()->setDueDate()->setStatus(Invoice::STATUS_PARTIAL)->updateBalance($amount_paid);
$this->invoice->service()->clearPartial()->setDueDate()->setStatus(Invoice::STATUS_PARTIAL)->updateBalance($amount_paid)->save();
}
elseif ($this->invoice->partial > 0 && $this->invoice->partial > $this->payment_amount)
@ -56,7 +56,7 @@ class ApplyPayment extends AbstractService
$amount_paid = $this->payment_amount * -1;
$this->invoice->service()->updatePartial($amount_paid)->updateBalance($amount_paid);
$this->invoice->service()->updatePartial($amount_paid)->updateBalance($amount_paid)->save();
}
elseif ($this->invoice->partial > 0 && $this->invoice->partial < $this->payment_amount)
@ -65,7 +65,7 @@ class ApplyPayment extends AbstractService
$amount_paid = $this->payment_amount * -1;
$this->invoice->service()->clearPartial()->setDueDate()->setStatus(Invoice::STATUS_PARTIAL)->updateBalance($amount_paid);
$this->invoice->service()->clearPartial()->setDueDate()->setStatus(Invoice::STATUS_PARTIAL)->updateBalance($amount_paid)->save();
}
@ -76,7 +76,7 @@ class ApplyPayment extends AbstractService
{
$amount_paid = $this->payment_amount * -1;
$this->invoice->service()->clearPartial()->setStatus(Invoice::STATUS_PAID)->updateBalance($amount_paid);
$this->invoice->service()->clearPartial()->setStatus(Invoice::STATUS_PAID)->updateBalance($amount_paid)->save();
}
elseif ($this->payment_amount < $this->invoice->balance)
@ -85,7 +85,7 @@ class ApplyPayment extends AbstractService
$amount_paid = $this->payment_amount * -1;
$this->invoice->service()->clearPartial()->setStatus(Invoice::STATUS_PARTIAL)->updateBalance($amount_paid);
$this->invoice->service()->clearPartial()->setStatus(Invoice::STATUS_PARTIAL)->updateBalance($amount_paid)->save();
}
@ -95,7 +95,7 @@ class ApplyPayment extends AbstractService
$amount_paid = $this->invoice->balance * -1;
$this->invoice->service()->clearPartial()->setStatus(Invoice::STATUS_PAID)->updateBalance($amount_paid);
$this->invoice->service()->clearPartial()->setStatus(Invoice::STATUS_PAID)->updateBalance($amount_paid)->save();
}
}

View File

@ -82,10 +82,15 @@ class UpdateInvoicePayment
->updateBalance($paid_amount * -1)
->updatePaidToDate($paid_amount)
->updateStatus()
->deletePdf()
->workFlow()
->save();
$invoice->refresh();
$invoice->service()
->deletePdf()
->workFlow()
->save();
event(new InvoiceWasUpdated($invoice, $invoice->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
});

View File

@ -75,9 +75,9 @@ class SubscriptionService
$recurring_invoice = $this->convertInvoiceToRecurring($payment_hash->payment->client_id);
$recurring_invoice_repo = new RecurringInvoiceRepository();
$recurring_invoice->next_send_date = now();
$recurring_invoice = $recurring_invoice_repo->save([], $recurring_invoice);
$recurring_invoice->next_send_date = $recurring_invoice->nextSendDate();
// $recurring_invoice->next_send_date = now()->format('Y-m-d');
// $recurring_invoice->next_send_date = $recurring_invoice->nextSendDate();
$recurring_invoice->auto_bill = $this->subscription->auto_bill;
/* Start the recurring service */
@ -161,6 +161,11 @@ class SubscriptionService
$recurring_invoice->discount = $this->subscription->promo_discount;
$recurring_invoice->is_amount_discount = $this->subscription->is_amount_discount;
}
elseif(strlen($this->subscription->promo_code) == 0 && $this->subscription->promo_discount > 0) {
$recurring_invoice->discount = $this->subscription->promo_discount;
$recurring_invoice->is_amount_discount = $this->subscription->is_amount_discount;
}
$recurring_invoice = $recurring_invoice_repo->save($data, $recurring_invoice);
@ -594,7 +599,7 @@ class SubscriptionService
$recurring_invoice = $this->convertInvoiceToRecurring($old_recurring_invoice->client_id);
$recurring_invoice = $recurring_invoice_repo->save([], $recurring_invoice);
$recurring_invoice->next_send_date = now();
$recurring_invoice->next_send_date = now()->format('Y-m-d');
$recurring_invoice->next_send_date = $recurring_invoice->nextSendDate();
/* Start the recurring service */
@ -693,6 +698,11 @@ class SubscriptionService
$invoice->discount = $this->subscription->promo_discount;
$invoice->is_amount_discount = $this->subscription->is_amount_discount;
}
elseif(strlen($this->subscription->promo_code) == 0 && $this->subscription->promo_discount > 0) {
$invoice->discount = $this->subscription->promo_discount;
$invoice->is_amount_discount = $this->subscription->is_amount_discount;
}
return $invoice_repo->save($data, $invoice);
@ -722,7 +732,9 @@ class SubscriptionService
$recurring_invoice->auto_bill = $client->getSetting('auto_bill');
$recurring_invoice->auto_bill_enabled = $this->setAutoBillFlag($recurring_invoice->auto_bill);
$recurring_invoice->due_date_days = 'terms';
$recurring_invoice->next_send_date = now()->format('Y-m-d');
$recurring_invoice->next_send_date = $recurring_invoice->nextSendDate();
return $recurring_invoice;
}
@ -1027,7 +1039,7 @@ class SubscriptionService
'subscription' => $this->subscription->hashed_id,
'recurring_invoice' => $recurring_invoice_hashed_id,
'client' => $invoice->client->hashed_id,
'contact' => $invoice->client->primary_contact()->first() ? $invoice->client->primary_contact()->first(): $invoice->client->contacts->first(),
'contact' => $invoice->client->primary_contact()->first() ? $invoice->client->primary_contact()->first()->hashed_id: $invoice->client->contacts->first()->hashed_id,
'invoice' => $invoice->hashed_id,
];

View File

@ -150,4 +150,4 @@
},
"minimum-stability": "dev",
"prefer-stable": true
}
}

View File

@ -14,8 +14,8 @@ return [
'require_https' => env('REQUIRE_HTTPS', true),
'app_url' => rtrim(env('APP_URL', ''), '/'),
'app_domain' => env('APP_DOMAIN', 'invoicing.co'),
'app_version' => '5.3.31',
'app_tag' => '5.3.31',
'app_version' => '5.3.32',
'app_tag' => '5.3.32',
'minimum_client_version' => '5.0.16',
'terms_version' => '1.0.1',
'api_secret' => env('API_SECRET', ''),

View File

@ -25,8 +25,8 @@
@foreach($multiple_contacts as $contact)
<a data-turbolinks="false"
href="{{ route('client.switch_company', $contact->hashed_id) }}"
class="block px-4 py-2 text-sm leading-5 text-gray-700 hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus:bg-gray-100 focus:text-gray-900">{{ $contact->company->present()->name() }}
- {{ $contact->client->present()->name()}}</a>
class="block px-4 py-2 text-sm leading-5 text-gray-700 hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus:bg-gray-100 focus:text-gray-900">{{ $contact->client->present()->name()}} - {{ $contact->company->present()->name() }}
</a>
@endforeach
</div>
</div>

View File

@ -23,7 +23,7 @@ Route::get('tmp_pdf/{hash}', 'ClientPortal\TempRouteController@index')->name('tm
Route::get('client/key_login/{contact_key}', 'ClientPortal\ContactHashLoginController@login')->name('client.contact_login')->middleware(['domain_db','contact_key_login']);
Route::get('client/magic_link/{magic_link}', 'ClientPortal\ContactHashLoginController@magicLink')->name('client.contact_magic_link')->middleware(['domain_db','contact_key_login']);
Route::get('documents/{document_hash}', 'ClientPortal\DocumentController@publicDownload')->name('documents.public_download')->middleware(['document_db']);
Route::get('documents/{document_hash}', 'ClientPortal\DocumentController@publicDownload')->name('documents.public_download')->middleware(['domain_db']);
Route::get('error', 'ClientPortal\ContactHashLoginController@errorPage')->name('client.error');
Route::get('client/payment/{contact_key}/{payment_id}', 'ClientPortal\InvitationController@paymentRouter')->middleware(['domain_db','contact_key_login']);
Route::get('client/ninja/{contact_key}/{company_key}', 'ClientPortal\NinjaPlanController@index')->name('client.ninja_contact_login')->middleware(['domain_db']);

View File

@ -10,6 +10,7 @@
*/
namespace Tests\Unit;
use App\Factory\ClientContactFactory;
use App\Factory\InvoiceItemFactory;
use App\Utils\Traits\UserSessionAttributes;
use Illuminate\Support\Facades\Session;
@ -77,4 +78,41 @@ class CollectionMergingTest extends TestCase
$this->assertTrue(collect($items)->contains('type_id', 3));
}
public function testClientContactSendEmailExists()
{
$new_collection = collect();
$cc = ClientContactFactory::create(1,1);
$cc->send_email = true;
$new_collection->push($cc);
$cc_false = ClientContactFactory::create(1,1);
$cc_false->send_email = false;
$new_collection->push($cc_false);
$this->assertTrue($new_collection->contains('send_email', true));
}
public function testClientContactSendEmailDoesNotExists()
{
$new_collection = collect();
$cc = ClientContactFactory::create(1,1);
$cc->send_email = false;
$new_collection->push($cc);
$cc_false = ClientContactFactory::create(1,1);
$cc_false->send_email = false;
$new_collection->push($cc_false);
$this->assertFalse($new_collection->contains('send_email', true));
}
}