1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-09-20 08:21:34 +02:00

Merge pull request #4101 from turbo124/v2

Recurring invoice scheduling
This commit is contained in:
David Bomba 2020-09-24 21:37:23 +10:00 committed by GitHub
commit dedf43e562
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 452 additions and 133 deletions

View File

@ -80,4 +80,5 @@ class ContactLoginController extends Controller
return redirect('/client/login');
}
}

View File

@ -0,0 +1,35 @@
<?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\Http\Controllers\ClientPortal;
use App\Http\Controllers\Controller;
use Auth;
use Illuminate\Http\Request;
class ContactHashLoginController extends Controller
{
/**
* Logs a user into the client portal using their contact_key
* @param string $contact_key The contact key
* @return Auth|Redirect
*/
public function login(string $contact_key)
{
return redirect('/client/login');
}
}

View File

@ -496,7 +496,7 @@ class RecurringInvoiceController extends BaseController
$recurring_invoices->each(function ($recurring_invoice, $key) use ($action) {
if (auth()->user()->can('edit', $recurring_invoice)) {
$this->recurring_invoice_repo->{$action}($recurring_invoice);
$this->performAction($recurring_invoice, $action, true);
}
});
@ -572,38 +572,56 @@ class RecurringInvoiceController extends BaseController
* )
*/
public function action(ActionRecurringInvoiceRequest $request, RecurringInvoice $recurring_invoice, $action)
{
return $this->performAction($recurring_invoice, $action);
}
private function performAction(RecurringInvoice $recurring_invoice, string $action, $bulk = false)
{
switch ($action) {
case 'clone_to_RecurringInvoice':
// $recurring_invoice = CloneRecurringInvoiceFactory::create($recurring_invoice, auth()->user()->id);
// return $this->itemResponse($recurring_invoice);
break;
case 'clone_to_quote':
// $recurring_invoice = CloneRecurringInvoiceToQuoteFactory::create($recurring_invoice, auth()->user()->id);
// todo build the quote transformer and return response here
break;
case 'history':
// code...
break;
case 'delivery_note':
// code...
break;
case 'mark_paid':
// code...
break;
case 'archive':
// code...
$this->recurring_invoice_repo->archive($recurring_invoice);
if (! $bulk) {
return $this->listResponse($recurring_invoice);
}
break;
case 'restore':
$this->recurring_invoice_repo->restore($recurring_invoice);
if (! $bulk) {
return $this->listResponse($recurring_invoice);
}
break;
case 'delete':
// code...
$this->recurring_invoice_repo->delete($recurring_invoice);
if (! $bulk) {
return $this->listResponse($recurring_invoice);
}
break;
case 'email':
//dispatch email to queue
break;
case 'start':
$recurring_invoice = $recurring_invoice->service()->start()->save();
if (! $bulk) {
$this->itemResponse($recurring_invoice);
}
break;
case 'stop':
$recurring_invoice = $recurring_invoice->service()->stop()->save();
if (! $bulk) {
$this->itemResponse($recurring_invoice);
}
break;
default:
// code...
break;
}
}
}

View File

@ -82,6 +82,6 @@ class SelfUpdateController extends BaseController
Artisan::call('ninja:post-update');
return response()->json(['message'=>$res], 200);
return response()->json(['message' => ''], 200);
}
}

View File

@ -117,5 +117,6 @@ class Kernel extends HttpKernel
'contact.register' => \App\Http\Middleware\ContactRegister::class,
'shop_token_auth' => \App\Http\Middleware\Shop\ShopTokenAuth::class,
'phantom_secret' => \App\Http\Middleware\PhantomSecret::class,
'contact_key_login' => \App\Http\Middleware\ContactKeyLogin::class,
];
}

View File

@ -0,0 +1,54 @@
<?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\Http\Middleware;
use App\Libraries\MultiDB;
use App\Models\ClientContact;
use App\Models\CompanyToken;
use Closure;
use Auth;
class ContactKeyLogin
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
if ($request->segment(3) && config('ninja.db.multi_db_enabled')) {
if (MultiDB::findAndSetDbByContactKey($request->segment(3))) {
$client_contact = ClientContact::where('contact_key', $request->segment(3))->first();
Auth::guard('contact')->login($client_contact, true);
return redirect()->to('client/dashboard');
}
}
else if ($request->has('contact_key')) {
if($client_contact = ClientContact::where('contact_key', $request->segment(3))->first()){
Auth::guard('contact')->login($client_contact, true);
return redirect()->to('client/dashboard');
}
}
return $next($request);
}
}

View File

@ -95,9 +95,24 @@ class StoreRecurringInvoiceRequest extends Request
}
$input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : [];
//$input['line_items'] = json_encode($input['line_items']);
$this->replace($input);
}
if(isset($input['auto_bill']))
$input['auto_bill_enabled'] = $this->setAutoBillFlag($input['auto_bill']);
$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()
{

View File

@ -85,6 +85,22 @@ class UpdateRecurringInvoiceRequest extends Request
$input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : [];
if(isset($input['auto_bill']))
$input['auto_bill_enabled'] = $this->setAutoBillFlag($input['auto_bill']);
$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?
}
}

View File

@ -17,6 +17,7 @@ use App\Helpers\Email\InvoiceEmail;
use App\Jobs\Invoice\EmailInvoice;
use App\Models\Invoice;
use App\Models\RecurringInvoice;
use App\Utils\Ninja;
use App\Utils\Traits\GeneratesCounter;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;

View File

@ -195,6 +195,20 @@ class MultiDB
return false;
}
public static function findAndSetDbByContactKey($contact_key) :bool
{
foreach (self::$dbs as $db) {
if ($client_contact = ClientContact::on($db)->where('contact_key', $contact_key)->first()) {
self::setDb($client_contact->company->db);
return true;
}
}
return false;
}
public static function findAndSetDbByDomain($subdomain) :bool
{
foreach (self::$dbs as $db) {

View File

@ -15,6 +15,7 @@ use App\Helpers\Invoice\InvoiceSum;
use App\Helpers\Invoice\InvoiceSumInclusive;
use App\Models\Filterable;
use App\Models\RecurringInvoiceInvitation;
use App\Services\Recurring\RecurringService;
use App\Utils\Traits\MakesDates;
use App\Utils\Traits\MakesHash;
use App\Utils\Traits\Recurring\HasRecurrence;
@ -103,6 +104,7 @@ class RecurringInvoice extends BaseModel
'next_send_date',
'remaining_cycles',
'auto_bill',
'auto_bill_enabled',
];
protected $casts = [
@ -190,7 +192,7 @@ class RecurringInvoice extends BaseModel
public function getStatusAttribute()
{
if ($this->status_id == self::STATUS_ACTIVE && $this->next_send_date > Carbon::now())
if ($this->status_id == self::STATUS_ACTIVE && Carbon::parse($this->next_send_date)->isFuture())
return self::STATUS_PENDING;
else
return $this->status_id;
@ -200,27 +202,27 @@ class RecurringInvoice extends BaseModel
{
switch ($this->frequency_id) {
case self::FREQUENCY_WEEKLY:
return Carbon::parse($this->next_send_date->addWeek());
return Carbon::parse($this->next_send_date)->addWeek();
case self::FREQUENCY_TWO_WEEKS:
return Carbon::parse($this->next_send_date->addWeeks(2));
return Carbon::parse($this->next_send_date)->addWeeks(2);
case self::FREQUENCY_FOUR_WEEKS:
return Carbon::parse($this->next_send_date->addWeeks(4));
return Carbon::parse($this->next_send_date)->addWeeks(4);
case self::FREQUENCY_MONTHLY:
return Carbon::parse($this->next_send_date->addMonthNoOverflow());
return Carbon::parse($this->next_send_date)->addMonthNoOverflow();
case self::FREQUENCY_TWO_MONTHS:
return Carbon::parse($this->next_send_date->addMonthsNoOverflow(2));
return Carbon::parse($this->next_send_date)->addMonthsNoOverflow(2);
case self::FREQUENCY_THREE_MONTHS:
return Carbon::parse($this->next_send_date->addMonthsNoOverflow(3));
return Carbon::parse($this->next_send_date)->addMonthsNoOverflow(3);
case self::FREQUENCY_FOUR_MONTHS:
return Carbon::parse($this->next_send_date->addMonthsNoOverflow(4));
return Carbon::parse($this->next_send_date)->addMonthsNoOverflow(4);
case self::FREQUENCY_SIX_MONTHS:
return Carbon::parse($this->next_send_date->addMonthsNoOverflow(6));
return Carbon::parse($this->next_send_date)->addMonthsNoOverflow(6);
case self::FREQUENCY_ANNUALLY:
return Carbon::parse($this->next_send_date->addYear());
return Carbon::parse($this->next_send_date)->addYear();
case self::FREQUENCY_TWO_YEARS:
return Carbon::parse($this->next_send_date->addYears(2));
return Carbon::parse($this->next_send_date)->addYears(2);
case self::FREQUENCY_THREE_YEARS:
return Carbon::parse($this->next_send_date->addYears(3));
return Carbon::parse($this->next_send_date)->addYears(3);
default:
return null;
}
@ -231,27 +233,27 @@ class RecurringInvoice extends BaseModel
switch ($this->frequency_id) {
case self::FREQUENCY_WEEKLY:
return Carbon::parse($date->addWeek());
return Carbon::parse($date)->addWeek();
case self::FREQUENCY_TWO_WEEKS:
return Carbon::parse($date->addWeeks(2));
return Carbon::parse($date)->addWeeks(2);
case self::FREQUENCY_FOUR_WEEKS:
return Carbon::parse($date->addWeeks(4));
return Carbon::parse($date)->addWeeks(4);
case self::FREQUENCY_MONTHLY:
return Carbon::parse($date->addMonthNoOverflow());
return Carbon::parse($date)->addMonthNoOverflow();
case self::FREQUENCY_TWO_MONTHS:
return Carbon::parse($date->addMonthsNoOverflow(2));
return Carbon::parse($date)->addMonthsNoOverflow(2);
case self::FREQUENCY_THREE_MONTHS:
return Carbon::parse($date->addMonthsNoOverflow(3));
return Carbon::parse($date)->addMonthsNoOverflow(3);
case self::FREQUENCY_FOUR_MONTHS:
return Carbon::parse($date->addMonthsNoOverflow(4));
return Carbon::parse($date)->addMonthsNoOverflow(4);
case self::FREQUENCY_SIX_MONTHS:
return Carbon::parse($date->addMonthsNoOverflow(6));
return Carbon::parse($date)->addMonthsNoOverflow(6);
case self::FREQUENCY_ANNUALLY:
return Carbon::parse($date->addYear());
return Carbon::parse($date)->addYear();
case self::FREQUENCY_TWO_YEARS:
return Carbon::parse($date->addYears(2));
return Carbon::parse($date)->addYears(2);
case self::FREQUENCY_THREE_YEARS:
return Carbon::parse($date->addYears(3));
return Carbon::parse($date)->addYears(3);
default:
return null;
}
@ -379,24 +381,24 @@ class RecurringInvoice extends BaseModel
if($this->remaining_cycles == -1)
$iterations = 10;
$data = [];
$next_send_date = Carbon::parse($this->next_send_date)->copy();
$data = [];
for($x=0; $x<$iterations; $x++)
{
$next_due_date = $next_send_date->copy()->addDays($this->due_date_days);
// we don't add the days... we calc the day of the month!!
$next_due_date = $this->calculateDueDate($next_send_date->copy()->format('Y-m-d'));
$next_send_date = Carbon::parse($next_send_date);
$next_due_date = Carbon::parse($next_due_date);
$data[] = [
'next_send_date' => $next_send_date->format('Y-m-d'),
'send_date' => $next_send_date->format('Y-m-d'),
'due_date' => $next_due_date->format('Y-m-d'),
];
$next_send_date = $this->calculateDueDate($next_send_date);
$next_send_date = $this->nextDateByFrequency($next_send_date->format('Y-m-d'));
}
@ -434,14 +436,22 @@ class RecurringInvoice extends BaseModel
*/
public function calculateDateFromTerms($date)
{
$new_date = Carbon::parse($date);
$client_payment_terms = $this->client->getSetting('payment_terms');
if($client_payment_terms == '')//no due date! return null;
return null;
return $date->copy()->addDays($client_payment_terms); //add the number of days in the payment terms to the date
return $new_date->addDays($client_payment_terms); //add the number of days in the payment terms to the date
}
/**
* Service entry points.
*/
public function service() :RecurringService
{
return new RecurringService($this);
}
}

View File

@ -83,7 +83,9 @@ class InvoiceRepository extends BaseRepository
return;
}
$invoice->service()->handleCancellation()->save();
$invoice->service()->markDeleted()->handleCancellation()->save();
$invoice = parent::delete($invoice);

View File

@ -24,6 +24,7 @@ use App\Services\Invoice\CreateInvitations;
use App\Services\Invoice\GetInvoicePdf;
use App\Services\Invoice\HandleCancellation;
use App\Services\Invoice\HandleReversal;
use App\Services\Invoice\MarkInvoiceDeleted;
use App\Services\Invoice\MarkInvoicePaid;
use App\Services\Invoice\MarkSent;
use App\Services\Invoice\TriggeredActions;
@ -154,6 +155,13 @@ class InvoiceService
return $this;
}
public function markDeleted()
{
$this->invoice = (new MarkInvoiceDeleted($this->invoice))->run();
return $this;
}
public function reverseCancellation()
{
$this->invoice = (new HandleCancellation($this->invoice))->reverse();

View File

@ -0,0 +1,71 @@
<?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\Services\Invoice;
use App\Events\Invoice\InvoiceWasCancelled;
use App\Events\Payment\PaymentWasCreated;
use App\Factory\CreditFactory;
use App\Factory\InvoiceItemFactory;
use App\Factory\PaymentFactory;
use App\Helpers\Invoice\InvoiceSum;
use App\Models\Client;
use App\Models\Invoice;
use App\Models\Payment;
use App\Models\Paymentable;
use App\Services\AbstractService;
use App\Services\Client\ClientService;
use App\Services\Payment\PaymentService;
use App\Utils\Ninja;
use App\Utils\Traits\GeneratesCounter;
class MarkInvoiceDeleted extends AbstractService
{
use GeneratesCounter;
private $invoice;
public function __construct(Invoice $invoice)
{
$this->invoice = $invoice;
}
public function run()
{
$check = false;
$x=0;
do {
$number = $this->calcNumber($x);
$check = $this->checkNumberAvailable(Invoice::class, $this->invoice, $number);
$x++;
} while (!$check);
$this->invoice->number = $number;
return $this->invoice;
}
private function calcNumber($x)
{
if($x==0)
$number = $this->invoice->number . '_' . ctrans('texts.deleted');
else
$number = $this->invoice->number . '_' . ctrans('texts.deleted') . '_'. $x;
return $number;
}
}

View File

@ -72,8 +72,10 @@ class RefundPayment
{
if ($this->refund_data['gateway_refund'] !== false && $this->total_refund > 0) {
if ($this->payment->company_gateway) {
$response = $this->payment->company_gateway->driver($this->payment->client)->refund($this->payment, $this->total_refund);
if ($response['success'] == false) {
throw new PaymentRefundFailed();
}

View File

@ -12,6 +12,7 @@
namespace App\Services\Recurring;
use App\Models\RecurringInvoice;
use Illuminate\Support\Carbon;
class RecurringService
{
@ -23,4 +24,35 @@ class RecurringService
}
//set schedules - update next_send_dates
/**
* Stops a recurring invoice
*
* @return $this RecurringService object
*/
public function stop()
{
$this->status_id = RecurringInvoice::STATUS_PAUSED;
return $this;
}
public function start()
{
//make sure next_send_date is either now or in the future else return.
if(Carbon::parse($this->recurring_entity->next_send_date)->lt(now()))
return $this;
$this->recurring_entity->status_id = RecurringInvoice::STATUS_ACTIVE;
return $this;
}
public function save()
{
$this->recurring_entity->save();
return $this->recurring_entity;
}
}

View File

@ -66,6 +66,7 @@ class SystemHealth
'php_version' => [
'minimum_php_version' => (string) self::$php_version,
'current_php_version' => phpversion(),
'current_php_cli_version' => (string) self::checkPhpCli(),
'is_okay' => version_compare(phpversion(), self::$php_version, '>='),
],
'env_writable' => self::checkEnvWritable(),
@ -116,6 +117,21 @@ class SystemHealth
return $result;
}
private static function checkPhpCli()
{
try {
exec('php -v', $foo, $exitCode);
if ($exitCode === 0) {
return empty($foo[0]) ? 'Found php cli, but no version information' : $foo[0];
}
} catch (\Exception $e) {
return false;
}
}
private static function extensions() :array
{
$loaded_extensions = [];

View File

@ -306,6 +306,16 @@ trait GeneratesCounter
return $number;
}
/*Check if a number is available for use. */
public function checkNumberAvailable($class, $entity, $number) :bool
{
if($entity = $class::whereCompanyId($entity->company_id)->whereNumber($number)->withTrashed()->first())
return false;
return true;
}
/**
* Saves counters at both the company and client level.
*

View File

@ -53,12 +53,13 @@ trait HasRecurrence
*/
public function setDayOfMonth($date, $day_of_month)
{
$set_date = $date->copy()->setUnitNoOverflow('day', $day_of_month, 'month');
$carbon_date = Carbon::parse($date);
$set_date = $carbon_date->copy()->setUnitNoOverflow('day', $day_of_month, 'month');
//If the set date is less than the original date we need to add a month.
//If we are overflowing dates, then we need to diff the dates and ensure it doesn't equal 0
if($set_date->lte($date) || $set_date->diffInDays($date) == 0)
if($set_date->lte($date) || $set_date->diffInDays($carbon_date) == 0)
$set_date->addMonthNoOverflow();
if($day_of_month == '31')

149
composer.lock generated
View File

@ -108,16 +108,16 @@
},
{
"name": "aws/aws-sdk-php",
"version": "3.154.6",
"version": "3.155.1",
"source": {
"type": "git",
"url": "https://github.com/aws/aws-sdk-php.git",
"reference": "83a1382930359e4d4f4c9187239f059d5b282520"
"reference": "575430a46c329a2a3723cdcb2d30df390f434ba1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/83a1382930359e4d4f4c9187239f059d5b282520",
"reference": "83a1382930359e4d4f4c9187239f059d5b282520",
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/575430a46c329a2a3723cdcb2d30df390f434ba1",
"reference": "575430a46c329a2a3723cdcb2d30df390f434ba1",
"shasum": ""
},
"require": {
@ -189,7 +189,7 @@
"s3",
"sdk"
],
"time": "2020-09-18T18:16:42+00:00"
"time": "2020-09-23T18:11:33+00:00"
},
{
"name": "brick/math",
@ -1084,30 +1084,30 @@
},
{
"name": "doctrine/dbal",
"version": "2.10.4",
"version": "2.11.0",
"source": {
"type": "git",
"url": "https://github.com/doctrine/dbal.git",
"reference": "47433196b6390d14409a33885ee42b6208160643"
"reference": "0d4e1a8b29dd987704842f0465aded378f441dca"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/dbal/zipball/47433196b6390d14409a33885ee42b6208160643",
"reference": "47433196b6390d14409a33885ee42b6208160643",
"url": "https://api.github.com/repos/doctrine/dbal/zipball/0d4e1a8b29dd987704842f0465aded378f441dca",
"reference": "0d4e1a8b29dd987704842f0465aded378f441dca",
"shasum": ""
},
"require": {
"doctrine/cache": "^1.0",
"doctrine/event-manager": "^1.0",
"ext-pdo": "*",
"php": "^7.2"
"php": "^7.3"
},
"require-dev": {
"doctrine/coding-standard": "^8.1",
"jetbrains/phpstorm-stubs": "^2019.1",
"nikic/php-parser": "^4.4",
"phpstan/phpstan": "^0.12.40",
"phpunit/phpunit": "^8.5.5",
"phpunit/phpunit": "^9.3",
"psalm/plugin-phpunit": "^0.10.0",
"symfony/console": "^2.0.5|^3.0|^4.0|^5.0",
"vimeo/psalm": "^3.14.2"
@ -1121,8 +1121,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.10.x-dev",
"dev-develop": "3.0.x-dev"
"dev-master": "4.0.x-dev"
}
},
"autoload": {
@ -1189,7 +1188,7 @@
"type": "tidelift"
}
],
"time": "2020-09-12T21:20:41+00:00"
"time": "2020-09-20T23:24:53+00:00"
},
{
"name": "doctrine/event-manager",
@ -1504,16 +1503,16 @@
},
{
"name": "egulias/email-validator",
"version": "2.1.20",
"version": "2.1.21",
"source": {
"type": "git",
"url": "https://github.com/egulias/EmailValidator.git",
"reference": "f46887bc48db66c7f38f668eb7d6ae54583617ff"
"reference": "563d0cdde5d862235ffe24a158497f4d490191b5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/egulias/EmailValidator/zipball/f46887bc48db66c7f38f668eb7d6ae54583617ff",
"reference": "f46887bc48db66c7f38f668eb7d6ae54583617ff",
"url": "https://api.github.com/repos/egulias/EmailValidator/zipball/563d0cdde5d862235ffe24a158497f4d490191b5",
"reference": "563d0cdde5d862235ffe24a158497f4d490191b5",
"shasum": ""
},
"require": {
@ -1558,7 +1557,7 @@
"validation",
"validator"
],
"time": "2020-09-06T13:44:32+00:00"
"time": "2020-09-19T14:37:56+00:00"
},
{
"name": "fedeisas/laravel-mail-css-inliner",
@ -1834,16 +1833,16 @@
},
{
"name": "google/apiclient-services",
"version": "v0.146",
"version": "v0.147",
"source": {
"type": "git",
"url": "https://github.com/googleapis/google-api-php-client-services.git",
"reference": "029e20e81508cee6dc652529514eeeb1cf56ce54"
"reference": "8624bd004cfccb33b760ae7650d0b750168cd7f7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/googleapis/google-api-php-client-services/zipball/029e20e81508cee6dc652529514eeeb1cf56ce54",
"reference": "029e20e81508cee6dc652529514eeeb1cf56ce54",
"url": "https://api.github.com/repos/googleapis/google-api-php-client-services/zipball/8624bd004cfccb33b760ae7650d0b750168cd7f7",
"reference": "8624bd004cfccb33b760ae7650d0b750168cd7f7",
"shasum": ""
},
"require": {
@ -1867,7 +1866,7 @@
"keywords": [
"google"
],
"time": "2020-09-12T00:24:59+00:00"
"time": "2020-09-20T00:24:43+00:00"
},
{
"name": "google/auth",
@ -2805,16 +2804,16 @@
},
{
"name": "laravel/ui",
"version": "v2.4.0",
"version": "v2.4.1",
"source": {
"type": "git",
"url": "https://github.com/laravel/ui.git",
"reference": "f5398544a9cd4804a42d09ce51735e37cd51ea2d"
"reference": "1c69ae3e8b52fe6c9eaf83b43c6dd8ef5c3f9e2c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/ui/zipball/f5398544a9cd4804a42d09ce51735e37cd51ea2d",
"reference": "f5398544a9cd4804a42d09ce51735e37cd51ea2d",
"url": "https://api.github.com/repos/laravel/ui/zipball/1c69ae3e8b52fe6c9eaf83b43c6dd8ef5c3f9e2c",
"reference": "1c69ae3e8b52fe6c9eaf83b43c6dd8ef5c3f9e2c",
"shasum": ""
},
"require": {
@ -2856,7 +2855,7 @@
"laravel",
"ui"
],
"time": "2020-09-11T15:31:52+00:00"
"time": "2020-09-22T16:51:51+00:00"
},
{
"name": "league/commonmark",
@ -3265,16 +3264,16 @@
},
{
"name": "league/mime-type-detection",
"version": "1.4.0",
"version": "1.5.0",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/mime-type-detection.git",
"reference": "fda190b62b962d96a069fcc414d781db66d65b69"
"reference": "ea2fbfc988bade315acd5967e6d02274086d0f28"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/fda190b62b962d96a069fcc414d781db66d65b69",
"reference": "fda190b62b962d96a069fcc414d781db66d65b69",
"url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/ea2fbfc988bade315acd5967e6d02274086d0f28",
"reference": "ea2fbfc988bade315acd5967e6d02274086d0f28",
"shasum": ""
},
"require": {
@ -3312,7 +3311,7 @@
"type": "tidelift"
}
],
"time": "2020-08-09T10:34:01+00:00"
"time": "2020-09-21T18:10:53+00:00"
},
{
"name": "league/oauth1-client",
@ -3839,16 +3838,16 @@
},
{
"name": "nesbot/carbon",
"version": "2.40.0",
"version": "2.40.1",
"source": {
"type": "git",
"url": "https://github.com/briannesbitt/Carbon.git",
"reference": "6c7646154181013ecd55e80c201b9fd873c6ee5d"
"reference": "d9a76d8b7eb0f97cf3a82529393245212f40ba3b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/6c7646154181013ecd55e80c201b9fd873c6ee5d",
"reference": "6c7646154181013ecd55e80c201b9fd873c6ee5d",
"url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/d9a76d8b7eb0f97cf3a82529393245212f40ba3b",
"reference": "d9a76d8b7eb0f97cf3a82529393245212f40ba3b",
"shasum": ""
},
"require": {
@ -3924,20 +3923,20 @@
"type": "tidelift"
}
],
"time": "2020-09-11T19:00:58+00:00"
"time": "2020-09-23T08:17:37+00:00"
},
{
"name": "nikic/php-parser",
"version": "v4.9.1",
"version": "v4.10.1",
"source": {
"type": "git",
"url": "https://github.com/nikic/PHP-Parser.git",
"reference": "88e519766fc58bd46b8265561fb79b54e2e00b28"
"reference": "1b479e7592812411c20c34d9ed33db3957bde66e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/88e519766fc58bd46b8265561fb79b54e2e00b28",
"reference": "88e519766fc58bd46b8265561fb79b54e2e00b28",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/1b479e7592812411c20c34d9ed33db3957bde66e",
"reference": "1b479e7592812411c20c34d9ed33db3957bde66e",
"shasum": ""
},
"require": {
@ -3976,7 +3975,7 @@
"parser",
"php"
],
"time": "2020-08-30T16:15:20+00:00"
"time": "2020-09-23T18:23:49+00:00"
},
{
"name": "nwidart/laravel-modules",
@ -4429,16 +4428,16 @@
},
{
"name": "php-http/discovery",
"version": "1.10.0",
"version": "1.12.0",
"source": {
"type": "git",
"url": "https://github.com/php-http/discovery.git",
"reference": "88ff14cad4a0db68b343260fa7ac3f1599703660"
"reference": "4366bf1bc39b663aa87459bd725501d2f1988b6c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-http/discovery/zipball/88ff14cad4a0db68b343260fa7ac3f1599703660",
"reference": "88ff14cad4a0db68b343260fa7ac3f1599703660",
"url": "https://api.github.com/repos/php-http/discovery/zipball/4366bf1bc39b663aa87459bd725501d2f1988b6c",
"reference": "4366bf1bc39b663aa87459bd725501d2f1988b6c",
"shasum": ""
},
"require": {
@ -4490,7 +4489,7 @@
"message",
"psr7"
],
"time": "2020-09-04T08:41:23+00:00"
"time": "2020-09-22T13:31:04+00:00"
},
{
"name": "php-http/guzzle6-adapter",
@ -6137,16 +6136,16 @@
},
{
"name": "spatie/browsershot",
"version": "3.37.2",
"version": "3.38.0",
"source": {
"type": "git",
"url": "https://github.com/spatie/browsershot.git",
"reference": "32d2984079ed8fe690f4dc5b7b6c205ae0a7b0fd"
"reference": "97ebb1dc0750f9c543162cb1e44d5b4916b17a7c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/spatie/browsershot/zipball/32d2984079ed8fe690f4dc5b7b6c205ae0a7b0fd",
"reference": "32d2984079ed8fe690f4dc5b7b6c205ae0a7b0fd",
"url": "https://api.github.com/repos/spatie/browsershot/zipball/97ebb1dc0750f9c543162cb1e44d5b4916b17a7c",
"reference": "97ebb1dc0750f9c543162cb1e44d5b4916b17a7c",
"shasum": ""
},
"require": {
@ -6195,7 +6194,7 @@
"type": "github"
}
],
"time": "2020-07-21T22:40:58+00:00"
"time": "2020-09-22T06:26:15+00:00"
},
{
"name": "spatie/image",
@ -6392,16 +6391,16 @@
},
{
"name": "stripe/stripe-php",
"version": "v7.52.0",
"version": "v7.54.0",
"source": {
"type": "git",
"url": "https://github.com/stripe/stripe-php.git",
"reference": "51e95c514aff45616dff09791ca5b2f10cf5c4e8"
"reference": "3a5cff6ce6f5f2f0fc75164d3677bd5dc3e96fe3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/stripe/stripe-php/zipball/51e95c514aff45616dff09791ca5b2f10cf5c4e8",
"reference": "51e95c514aff45616dff09791ca5b2f10cf5c4e8",
"url": "https://api.github.com/repos/stripe/stripe-php/zipball/3a5cff6ce6f5f2f0fc75164d3677bd5dc3e96fe3",
"reference": "3a5cff6ce6f5f2f0fc75164d3677bd5dc3e96fe3",
"shasum": ""
},
"require": {
@ -6445,7 +6444,7 @@
"payment processing",
"stripe"
],
"time": "2020-09-08T19:29:20+00:00"
"time": "2020-09-23T21:48:00+00:00"
},
{
"name": "swiftmailer/swiftmailer",
@ -9339,33 +9338,33 @@
"packages-dev": [
{
"name": "anahkiasen/former",
"version": "4.4.0",
"version": "4.5.0",
"source": {
"type": "git",
"url": "https://github.com/formers/former.git",
"reference": "2fcc1f68ca04af39a01e370ed0e2eef132ed5072"
"reference": "625e1dbcf3a7c9a60b21ce96a7736a17b0f2ebf8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/formers/former/zipball/2fcc1f68ca04af39a01e370ed0e2eef132ed5072",
"reference": "2fcc1f68ca04af39a01e370ed0e2eef132ed5072",
"url": "https://api.github.com/repos/formers/former/zipball/625e1dbcf3a7c9a60b21ce96a7736a17b0f2ebf8",
"reference": "625e1dbcf3a7c9a60b21ce96a7736a17b0f2ebf8",
"shasum": ""
},
"require": {
"anahkiasen/html-object": "~1.4",
"illuminate/config": "~5.0|^6.0|^7.0",
"illuminate/container": "~5.0|^6.0|^7.0",
"illuminate/http": "~5.0|^6.0|^7.0",
"illuminate/routing": "~5.0|^6.0|^7.0",
"illuminate/session": "~5.0|^6.0|^7.0",
"illuminate/support": "~5.0|^6.0|^7.0",
"illuminate/translation": "~5.0|^6.0|^7.0",
"php": ">=5.4.0"
"illuminate/config": "~5.0|^6.0|^7.0|^8.0",
"illuminate/container": "~5.0|^6.0|^7.0|^8.0",
"illuminate/http": "~5.0|^6.0|^7.0|^8.0",
"illuminate/routing": "~5.0|^6.0|^7.0|^8.0",
"illuminate/session": "~5.0|^6.0|^7.0|^8.0",
"illuminate/support": "~5.0|^6.0|^7.0|^8.0",
"illuminate/translation": "~5.0|^6.0|^7.0|^8.0",
"php": ">=7.2.0"
},
"require-dev": {
"illuminate/database": "~5.0|^6.0|^7.0",
"mockery/mockery": "~0.9.1",
"phpunit/phpunit": "~4"
"illuminate/database": "~5.0|^6.0|^7.0|^8.0",
"mockery/mockery": "^1.3",
"phpunit/phpunit": "^8.5"
},
"type": "library",
"extra": {
@ -9408,7 +9407,7 @@
"foundation",
"laravel"
],
"time": "2020-03-04T08:38:36+00:00"
"time": "2020-09-23T18:18:13+00:00"
},
{
"name": "anahkiasen/html-object",

View File

@ -19,6 +19,8 @@ Route::get('view/{entity_type}/{invitation_key}', 'ClientPortal\EntityViewContro
Route::get('view/{entity_type}/{invitation_key}/password', 'ClientPortal\EntityViewController@password')->name('client.entity_view.password');
Route::post('view/{entity_type}/{invitation_key}/password', 'ClientPortal\EntityViewController@handlePassword');
Route::get('client/key_login/{contact_key}', 'ClientPortal\ContactHashLoginController@login')->name('client.contact_login')->middleware(['contact_key_login']);
//todo implement domain DB
Route::group(['middleware' => ['auth:contact', 'locale'], 'prefix' => 'client', 'as' => 'client.'], function () {
Route::get('dashboard', 'ClientPortal\DashboardController@index')->name('dashboard'); // name = (dashboard. index / create / show / update / destroy / edit

View File

@ -20,9 +20,11 @@ use App\Models\RecurringInvoice;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Tests\MockAccountData;
use Tests\TestCase;
use Illuminate\Support\Carbon;
/**
* @test
* @covers \App\Models\RecurringInvoice
*/
class RecurringDatesTest extends TestCase
{
@ -92,4 +94,13 @@ class RecurringDatesTest extends TestCase
$this->assertEquals(5, count($recurring_invoice->recurringDates()));
}
public function testCompareDatesLogic()
{
$date = now()->startOfDay()->format('Y-m-d');
$this->assertTrue(Carbon::parse($date)->lte(now()->startOfDay()));
}
}