git branchMerge branch 'v5-develop' of https://github.com/turbo124/invoiceninja into v5-develop

This commit is contained in:
David Bomba 2022-03-02 13:26:38 +11:00
commit 5fd8aab4af
40 changed files with 181401 additions and 180674 deletions

View File

@ -1 +1 @@
5.3.62
5.3.64

View File

@ -480,11 +480,11 @@ class CheckData extends Command
LEFT JOIN paymentables
ON
payments.id = paymentables.payment_id
WHERE paymentable_type = 'App\\Models\\Credit'
WHERE paymentable_type = ?
AND paymentables.deleted_at is NULL
AND payments.is_deleted = 0
AND payments.client_id = ?;
"), [$client->id] );
"), [App\Models\Credit::class, $client->id] );
return $results;
}
@ -507,7 +507,7 @@ class CheckData extends Command
$this->wrong_paid_to_dates++;
$this->logMessage($client->present()->name.' id = # '.$client->id." - Paid to date does not match Client Paid To Date = {$client->paid_to_date} - Invoice Payments = {$total_paid_to_date}");
$this->logMessage($client->present()->name.' id = # '.$client->id." - Client Paid To Date = {$client->paid_to_date} != Invoice Payments = {$total_paid_to_date} - {$_client->payments_applied} + {$credits_used_for_payments[0]->credit_payment}");
$this->isValid = false;

View File

@ -46,7 +46,8 @@ class BaseSettings
return is_null($value) ? '' : (string) $value;
case 'bool':
case 'boolean':
return (bool) ($value);
nlog($value);
return boolval($value);
case 'object':
return json_decode($value);
case 'array':

View File

@ -51,7 +51,7 @@ class ContactHashLoginController extends Controller
return render('generic.error', ['title' => session()->get('title'), 'notification' => session()->get('notification')]);
}
private function setRedirectPath()
private function setRedirectPath()
{
if(auth()->guard('contact')->user()->company->enabled_modules & PortalComposer::MODULE_INVOICES)

View File

@ -125,7 +125,7 @@ class InvitationController extends Controller
}
if (auth()->guard('contact') && ! request()->has('silent') && ! $invitation->viewed_date) {
if (auth()->guard('contact')->user() && ! request()->has('silent') && ! $invitation->viewed_date) {
$invitation->markViewed();
event(new InvitationWasViewed($invitation->{$entity}, $invitation, $invitation->{$entity}->company, Ninja::eventVars()));
@ -264,15 +264,18 @@ class InvitationController extends Controller
abort(404, "Invoice not found");
}
public function unsubscribe(Request $request, string $invitation_key)
public function unsubscribe(Request $request, string $entity_type, string $invitation_key)
{
if($invite = InvoiceInvitation::withTrashed()->where('key', $invitation_key)->first()){
if($entity_type == 'invoice'){
$invite = InvoiceInvitation::withTrashed()->where('key', $invitation_key)->first();
$invite->contact->send_email = false;
$invite->contact->save();
}elseif($invite = QuoteInvitation::withTrashed()->where('key', $invitation_key)->first()){
}elseif($entity_type == 'quote'){
$invite = QuoteInvitation::withTrashed()->where('key', $invitation_key)->first();
$invite->contact->send_email = false;
$invite->contact->save();
}elseif($invite = CreditInvitation::withTrashed()->where('key', $invitation_key)->first()){
}elseif($entity_type == 'credit'){
$invite = CreditInvitation::withTrashed()->where('key', $invitation_key)->first();
$invite->contact->send_email = false;
$invite->contact->save();
}

View File

@ -75,7 +75,7 @@ class NinjaPlanController extends Controller
public function trial()
{
$gateway = CompanyGateway::where('gateway_key', 'd14dd26a37cecc30fdd65700bfb55b23')->first();
$gateway = CompanyGateway::on('db-ninja-01')->find(config('ninja.ninja_default_company_gateway_id'));
$data['gateway'] = $gateway;
@ -106,8 +106,7 @@ class NinjaPlanController extends Controller
$client->save();
//store payment method
$gateway = CompanyGateway::where('gateway_key', 'd14dd26a37cecc30fdd65700bfb55b23')->first();
$gateway = CompanyGateway::on('db-ninja-01')->find(config('ninja.ninja_default_company_gateway_id'));
$gateway_driver = $gateway->driver(auth()->guard('contact')->user()->client)->init();
$stripe_response = json_decode($request->input('gateway_response'));
@ -132,10 +131,16 @@ class NinjaPlanController extends Controller
$gateway_driver->storeGatewayToken($data, ['gateway_customer_reference' => $customer->id]);
//set free trial
$account = auth()->guard('contact')->user()->company->account;
$account->trial_started = now();
$account->trial_plan = 'pro';
$account->save();
// $account = auth()->guard('contact')->user()->company->account;
if(auth()->guard('contact')->user()->client->custom_value2){
MultiDB::findAndSetDbByAccountKey(auth()->guard('contact')->user()->client->custom_value2);
$account = Account::where('key', auth()->guard('contact')->user()->client->custom_value2);
$account->trial_started = now();
$account->trial_plan = 'pro';
$account->save();
}
MultiDB::setDB('db-ninja-01');
//create recurring invoice
$subscription_repo = new SubscriptionRepository();
@ -154,6 +159,8 @@ class NinjaPlanController extends Controller
$recurring_invoice->next_send_date = now()->addDays(14)->format('Y-m-d');
$recurring_invoice->save();
$recurring_invoice = $recurring_invoice->calc()->getRecurringInvoice();
$recurring_invoice->service()->start();
return redirect('/');

View File

@ -123,6 +123,8 @@ class ContactKeyLogin
return redirect($this->setRedirectPath());
}
}
//28-02-2022 middleware should not allow this to progress as we should have redirected by this stage.
abort(404, "Unable to authenticate.");
return $next($request);
}

View File

@ -46,6 +46,9 @@ class SetInviteDb
if($entity == "pay")
$entity = "invoice";
if(!in_array($entity, ['invoice','quote','credit','recurring_invoice']))
abort(404,'I could not find this resource.');
/* Try and determine the DB from the invitation key STRING*/
if (config('ninja.db.multi_db_enabled')) {

View File

@ -58,7 +58,9 @@ class StoreCreditRequest extends Request
// $rules['number'] = new UniqueCreditNumberRule($this->all());
$rules['number'] = ['nullable', Rule::unique('credits')->where('company_id', auth()->user()->company()->id)];
$rules['discount'] = 'sometimes|numeric';
$rules['is_amount_discount'] = ['boolean'];
if($this->invoice_id)
$rules['invoice_id'] = new ValidInvoiceCreditRule();

View File

@ -58,7 +58,8 @@ class UpdateCreditRequest extends Request
$rules['line_items'] = 'array';
$rules['discount'] = 'sometimes|numeric';
$rules['is_amount_discount'] = ['boolean'];
return $rules;
}

View File

@ -55,7 +55,8 @@ class StoreInvoiceRequest extends Request
$rules['number'] = ['nullable', Rule::unique('invoices')->where('company_id', auth()->user()->company()->id)];
$rules['project_id'] = ['bail', 'sometimes', new ValidProjectForClient($this->all())];
$rules['is_amount_discount'] = ['boolean'];
$rules['line_items'] = 'array';
$rules['discount'] = 'sometimes|numeric';

View File

@ -55,6 +55,8 @@ class UpdateInvoiceRequest extends Request
if($this->number)
$rules['number'] = Rule::unique('invoices')->where('company_id', auth()->user()->company()->id)->ignore($this->invoice->id);
$rules['is_amount_discount'] = ['boolean'];
$rules['line_items'] = 'array';
$rules['discount'] = 'sometimes|numeric';

View File

@ -52,6 +52,8 @@ class StoreQuoteRequest extends Request
$rules['number'] = ['nullable',Rule::unique('quotes')->where('company_id', auth()->user()->company()->id)];
$rules['discount'] = 'sometimes|numeric';
$rules['is_amount_discount'] = ['boolean'];
// $rules['number'] = new UniqueQuoteNumberRule($this->all());
$rules['line_items'] = 'array';

View File

@ -52,6 +52,8 @@ class UpdateQuoteRequest extends Request
$rules['line_items'] = 'array';
$rules['discount'] = 'sometimes|numeric';
$rules['is_amount_discount'] = ['boolean'];
return $rules;
}

View File

@ -125,7 +125,7 @@ class InvoiceTransformer extends BaseTransformer
$this->getString($invoice_data, 'invoice.status')
))
] ?? Invoice::STATUS_SENT,
'archived' => $status === 'archived',
// 'archived' => $status === 'archived',
];
/* If we can't find the client, then lets try and create a client */

View File

@ -46,6 +46,8 @@ class CSVIngest implements ShouldQueue {
public array $request;
public $tries = 1;
public function __construct( array $request, Company $company ) {
$this->company = $company;
$this->request = $request;

View File

@ -110,13 +110,10 @@ class PaymentFailedMailer implements ShouldQueue
});
//add client payment failures here.
nlog("pre client failure email");
//
if($contact = $this->client->primary_contact()->first())
{
nlog("inside failure");
$mail_obj = (new ClientPaymentFailureObject($this->client, $this->error, $this->company, $this->payment_hash))->build();
$nmo = new NinjaMailerObject;

View File

@ -362,6 +362,7 @@ class Import implements ShouldQueue
) {
$data['settings']['invoice_design_id'] = 1;
}
$data['settings']['email_sending_method'] = 'default';
$data = $this->transformCompanyData($data);
@ -374,9 +375,6 @@ class Import implements ShouldQueue
$data['subdomain'] = MultiDB::randomSubdomainGenerator();
}
else {
$data['email_sending_method'] = 'default';
}
$rules = (new UpdateCompanyRequest())->rules();

View File

@ -57,7 +57,6 @@ class ClientPaymentFailureObject
public function build()
{
if(!$this->payment_hash){
nlog("no payment has for failure notification - ClientPaymentFailureObject");
return;
}
@ -124,7 +123,6 @@ class ClientPaymentFailureObject
'settings' => $this->client->getMergedSettings(),
'whitelabel' => $this->company->account->isPaid() ? true : false,
'url' => $this->invoices->first()->invitations->first()->getPaymentLink(),
// 'button' => 'texts.pay_now',
'button' => ctrans('texts.pay_now'),
'additional_info' => false,
'company' => $this->company,

View File

@ -111,7 +111,7 @@ class TemplateEmail extends Mailable
'company' => $company,
'whitelabel' => $this->client->user->account->isPaid() ? true : false,
'logo' => $this->company->present()->logo($settings),
'unsubscribe_link' => $this->invitation->getUnsubscribeLink(),
'unsubscribe_link' => $this->invitation ? $this->invitation->getUnsubscribeLink() : '',
])
->withSwiftMessage(function ($message) use($company){
$message->getHeaders()->addTextHeader('Tag', $company->company_key);

View File

@ -90,6 +90,8 @@ class Credit extends BaseModel
'updated_at' => 'timestamp',
'created_at' => 'timestamp',
'deleted_at' => 'timestamp',
'is_amount_discount' => 'bool',
];
protected $touches = [];

View File

@ -103,6 +103,7 @@ class Invoice extends BaseModel
'created_at' => 'timestamp',
'deleted_at' => 'timestamp',
'is_deleted' => 'bool',
'is_amount_discount' => 'bool',
];
protected $with = [];
@ -497,6 +498,20 @@ class Invoice extends BaseModel
return $this->calc()->getTotal();
}
public function getPayableAmount()
{
if($this->partial > 0)
return $this->partial;
if($this->balance > 0)
return $this->balance;
if($this->status_id = 1)
return $this->amount;
return 0;
}
public function entityEmailEvent($invitation, $reminder_template, $template)
{
switch ($reminder_template) {

View File

@ -89,6 +89,7 @@ class Quote extends BaseModel
'created_at' => 'timestamp',
'deleted_at' => 'timestamp',
'is_deleted' => 'boolean',
'is_amount_discount' => 'bool',
];
protected $dates = [];

View File

@ -99,6 +99,6 @@ class ClientService
{
$this->client->save();
return $this->client;
return $this->client->fresh();
}
}

View File

@ -145,10 +145,23 @@ class DeletePayment
$multiplier = -1;
$paymentable_credit->service()
->updateBalance($paymentable_credit->pivot->amount*$multiplier)
->updatePaidToDate($paymentable_credit->pivot->amount*-1)
->updateBalance($paymentable_credit->pivot->amount*$multiplier*-1)
->updatePaidToDate($paymentable_credit->pivot->amount*$multiplier)
->setStatus(Credit::STATUS_SENT)
->save();
$this->payment
->client
->service()
->updatePaidToDate(($paymentable_credit->pivot->amount)*-1)
->save();
//01-03-2022
// $paymentable_credit->service()
// ->updateBalance($paymentable_credit->pivot->amount*$multiplier)
// ->updatePaidToDate($paymentable_credit->pivot->amount*-1)
// ->setStatus(Credit::STATUS_SENT)
// ->save();
});
}

View File

@ -228,10 +228,14 @@ class SubscriptionService
->where('is_deleted', 0)
->orderBy('id', 'desc')
->first();
}
if ($outstanding->count() == 0){
//need to ensure at this point that a refund is appropriate!!
//28-02-2022
if($recurring_invoice->invoices()->count() == 0){
return $target->price;
}
elseif ($outstanding->count() == 0){
//nothing outstanding
return $target->price - $this->calculateProRataRefundForSubscription($outstanding_invoice);
}
@ -408,7 +412,10 @@ class SubscriptionService
->orderBy('id', 'desc')
->first();
if(!$last_invoice){
if($recurring_invoice->invoices()->count() == 0){
$pro_rata_refund_amount = 0;
}
elseif(!$last_invoice){
$is_credit = true;

View File

@ -62,7 +62,9 @@ trait Inviteable
else
$domain = config('ninja.app_url');
return $domain.'/client/unsubscribe/'.$this->key;
$entity_type = Str::snake(class_basename($this->entityType()));
return $domain.'/client/unsubscribe/'.$entity_type.'/'.$this->key;
}

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.62',
'app_tag' => '5.3.62',
'app_version' => '5.3.64',
'app_tag' => '5.3.64',
'minimum_client_version' => '5.0.16',
'terms_version' => '1.0.1',
'api_secret' => env('API_SECRET', ''),

View File

@ -28,11 +28,11 @@ const RESOURCES = {
"assets/NOTICES": "9a4bf0423a5e265f38c4df37f7a0a913",
"assets/fonts/MaterialIcons-Regular.otf": "7e7a6cccddf6d7b20012a548461d5d81",
"favicon.ico": "51636d3a390451561744c42188ccd628",
"/": "a10a76fa9beabc958dfdeda4f1be38c8",
"/": "5b6588d4983a2540f5d0da10ef512a78",
"version.json": "a00481850d5c63ba5df4e22636643438",
"icons/Icon-512.png": "0f9aff01367f0a0c69773d25ca16ef35",
"icons/Icon-192.png": "bb1cf5f6982006952211c7c8404ffbed",
"main.dart.js": "2c4d2723a1998a9cfe695f465288e5e8",
"main.dart.js": "8be39c52c4228fcf411da26c2517a432",
"favicon.png": "dca91c54388f52eded692718d5a98b8b",
"manifest.json": "ef43d90e57aa7682d7e2cfba2f484a40",
"canvaskit/profiling/canvaskit.js": "ae2949af4efc61d28a4a80fffa1db900",

88420
public/main.dart.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

90402
public/main.foss.dart.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

85958
public/main.html.dart.js vendored

File diff suppressed because one or more lines are too long

85128
public/main.next.dart.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -4547,7 +4547,25 @@ $LANG = array(
'to_view_entity_set_password' => 'To view the :entity you need to set password.',
'unsubscribe' => 'Unsubscribe',
'unsubscribed' => 'Unsubscribed',
'unsubscribed_text' => 'You have been removed from notifications for this document'
'unsubscribed_text' => 'You have been removed from notifications for this document',
'client_shipping_state' => 'Client Shipping State',
'client_shipping_city' => 'Client Shipping City',
'client_shipping_postal_code' => 'Client Shipping Postal Code',
'client_shipping_country' => 'Client Shipping Country',
'load_pdf' => 'Load PDF',
'start_free_trial' => 'Start Free Trial',
'start_free_trial_message' => 'Start your FREE 14 day trial of the pro plan',
'due_on_receipt' => 'Due on Receipt',
'is_paid' => 'Is Paid',
'age_group_paid' => 'Paid',
'id' => 'Id',
'convert_to' => 'Convert To',
'client_currency' => 'Client Currency',
'company_currency' => 'Company Currency',
'custom_emails_disabled_help' => 'To prevent spam we require upgrading to a paid account to customize the email',
'upgrade_to_add_company' => 'Upgrade your plan to add companies',
'file_saved_in_downloads_folder' => 'The file has been saved in the downloads folder',
'small' => 'Small',
);
return $LANG;

View File

@ -59,7 +59,7 @@
@if($settings->client_portal_allow_under_payment || $settings->client_portal_allow_over_payment)
<button class="button button-primary bg-primary">{{ ctrans('texts.pay_now') }}</button>
@else
@livewire('pay-now-dropdown', ['total' => $invoice->partial > 0 ? $invoice->partial : $invoice->balance, 'company' => $company])
@livewire('pay-now-dropdown', ['total' => $invoice->getPayableAmount(), 'company' => $company])
@endif
</div>
</div>

View File

@ -114,7 +114,7 @@ Route::group(['middleware' => ['invite_db'], 'prefix' => 'client', 'as' => 'clie
Route::get('{entity}/{invitation_key}/download', 'ClientPortal\InvitationController@routerForDownload');
Route::get('pay/{invitation_key}', 'ClientPortal\InvitationController@payInvoice')->name('pay.invoice');
Route::get('unsubscribe/{invitation_key}', 'ClientPortal\InvitationController@unsubscribe')->name('unsubscribe');
Route::get('unsubscribe/{entity_type}/{invitation_key}', 'ClientPortal\InvitationController@unsubscribe')->name('unsubscribe');
// Route::get('{entity}/{client_hash}/{invitation_key}', 'ClientPortal\InvitationController@routerForIframe')->name('invoice.client_hash_and_invitation_key'); //should never need this

View File

@ -79,7 +79,7 @@ class CreditPaymentTest extends TestCase
$invoice = $invoice_calc->getInvoice();
$invoice->setRelation('client', $this->client);
$invoice->setRelation('company', $this->company);
$invoice->service()->markSent()->save();
$invoice->service()->markSent()->createInvitations()->save();
$data = [