Merge pull request #5346 from turbo124/v5-stable

v5.1.35
This commit is contained in:
David Bomba 2021-04-06 19:07:59 +10:00 committed by GitHub
commit 7ed78f2f2a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
49 changed files with 240454 additions and 239696 deletions

View File

@ -13,7 +13,7 @@ jobs:
strategy: strategy:
matrix: matrix:
operating-system: ['ubuntu-18.04', 'ubuntu-20.04'] operating-system: ['ubuntu-18.04', 'ubuntu-20.04']
php-versions: ['7.4'] php-versions: ['7.3','7.4','8.0']
phpunit-versions: ['latest'] phpunit-versions: ['latest']
env: env:

View File

@ -1 +1 @@
5.1.34 5.1.35

View File

@ -600,6 +600,7 @@ class CompanySettings extends BaseSettings
'$client.city_state_postal', '$client.city_state_postal',
'$client.country', '$client.country',
'$contact.email', '$contact.email',
'$client.phone',
], ],
'company_details' => [ 'company_details' => [
'$company.name', '$company.name',

View File

@ -23,7 +23,9 @@ class WebhookFactory
$webhook->target_url = ''; $webhook->target_url = '';
$webhook->event_id = 1; $webhook->event_id = 1;
$webhook->format = 'JSON'; $webhook->format = 'JSON';
$webhook->rest_method = 'post';
$webhook->headers = [];
return $webhook; return $webhook;
} }
} }

View File

@ -795,7 +795,7 @@ class InvoiceController extends BaseController
$file_path = $invoice->service()->getInvoicePdf($contact); $file_path = $invoice->service()->getInvoicePdf($contact);
nlog($file_path); nlog($file_path);
return response()->download($file_path, basename($file_path)); return response()->download($file_path, basename($file_path));
} }

View File

@ -15,6 +15,7 @@ use App\Factory\ClientFactory;
use App\Jobs\Mail\NinjaMailerJob; use App\Jobs\Mail\NinjaMailerJob;
use App\Jobs\Mail\NinjaMailerObject; use App\Jobs\Mail\NinjaMailerObject;
use App\Mail\ContactPasswordlessLogin; use App\Mail\ContactPasswordlessLogin;
use App\Models\Client;
use App\Models\Subscription; use App\Models\Subscription;
use App\Models\ClientContact; use App\Models\ClientContact;
use App\Models\Invoice; use App\Models\Invoice;
@ -220,6 +221,16 @@ class BillingPortalPurchase extends Component
'settings' => [], 'settings' => [],
]; ];
foreach ($this->request_data as $field => $value) {
if (in_array($field, Client::$subscriptions_fillable)) {
$data[$field] = $value;
}
if (in_array($field, ClientContact::$subscription_fillable)) {
$data['contacts'][0][$field] = $value;
}
}
if (array_key_exists('locale', $this->request_data)) { if (array_key_exists('locale', $this->request_data)) {
$request = $this->request_data; $request = $this->request_data;
@ -391,6 +402,10 @@ class BillingPortalPurchase extends Component
public function render() public function render()
{ {
if (array_key_exists('email', $this->request_data)) {
$this->email = $this->request_data['email'];
}
if ($this->contact instanceof ClientContact) { if ($this->contact instanceof ClientContact) {
$this->getPaymentMethods($this->contact); $this->getPaymentMethods($this->contact);
} }

View File

@ -34,7 +34,7 @@ class StoreSubscriptionRequest extends Request
*/ */
public function rules() public function rules()
{ {
return [ $rules = [
'product_id' => ['sometimes'], 'product_id' => ['sometimes'],
'assigned_user_id' => ['sometimes'], 'assigned_user_id' => ['sometimes'],
'is_recurring' => ['sometimes'], 'is_recurring' => ['sometimes'],
@ -55,12 +55,17 @@ class StoreSubscriptionRequest extends Request
'webhook_configuration' => ['array'], 'webhook_configuration' => ['array'],
'name' => ['required', Rule::unique('subscriptions')->where('company_id', auth()->user()->company()->id)] 'name' => ['required', Rule::unique('subscriptions')->where('company_id', auth()->user()->company()->id)]
]; ];
return $this->globalRules($rules);
} }
protected function prepareForValidation() protected function prepareForValidation()
{ {
$input = $this->all(); $input = $this->all();
$input = $this->decodePrimaryKeys($input);
$this->replace($input); $this->replace($input);
} }
} }

View File

@ -35,8 +35,20 @@ class UpdateSubscriptionRequest extends Request
*/ */
public function rules() public function rules()
{ {
return [ $rules = [
// //
]; ];
return $this->globalRules($rules);
}
protected function prepareForValidation()
{
$input = $this->all();
$input = $this->decodePrimaryKeys($input);
$this->replace($input);
} }
} }

View File

@ -34,6 +34,7 @@ use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Mail; use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use Illuminate\Support\Facades\App;
/*Multi Mailer implemented*/ /*Multi Mailer implemented*/
@ -104,6 +105,9 @@ class EmailEntity implements ShouldQueue
/* Set DB */ /* Set DB */
MultiDB::setDB($this->company->db); MultiDB::setDB($this->company->db);
App::setLocale($this->invitation->contact->preferredLocale());
$nmo = new NinjaMailerObject; $nmo = new NinjaMailerObject;
$nmo->mailable = new TemplateEmail($this->email_entity_builder,$this->invitation->contact, $this->invitation); $nmo->mailable = new TemplateEmail($this->email_entity_builder,$this->invitation->contact, $this->invitation);
$nmo->company = $this->company; $nmo->company = $this->company;

View File

@ -11,12 +11,12 @@
namespace App\Jobs\Mail; namespace App\Jobs\Mail;
use App\Jobs\Mail\NinjaMailer;
use App\Jobs\Mail\NinjaMailerJob; use App\Jobs\Mail\NinjaMailerJob;
use App\Jobs\Mail\NinjaMailerObject; use App\Jobs\Mail\NinjaMailerObject;
use App\Libraries\MultiDB; use App\Libraries\MultiDB;
use App\Mail\Admin\EntityNotificationMailer; use App\Mail\Admin\EntityNotificationMailer;
use App\Mail\Admin\PaymentFailureObject; use App\Mail\Admin\PaymentFailureObject;
use App\Mail\NinjaMailer;
use App\Models\User; use App\Models\User;
use App\Utils\Traits\Notifications\UserNotifies; use App\Utils\Traits\Notifications\UserNotifies;
use Illuminate\Bus\Queueable; use Illuminate\Bus\Queueable;
@ -38,7 +38,7 @@ class PaymentFailureMailer implements ShouldQueue
public $company; public $company;
public $payment_hash; public $amount;
public $settings; public $settings;
@ -50,7 +50,7 @@ class PaymentFailureMailer implements ShouldQueue
* @param $company * @param $company
* @param $amount * @param $amount
*/ */
public function __construct($client, $error, $company, $payment_hash) public function __construct($client, $error, $company, $amount)
{ {
$this->company = $company; $this->company = $company;
@ -58,7 +58,7 @@ class PaymentFailureMailer implements ShouldQueue
$this->client = $client; $this->client = $client;
$this->payment_hash = $payment_hash; $this->amount = $amount;
$this->company = $company; $this->company = $company;
@ -86,7 +86,7 @@ class PaymentFailureMailer implements ShouldQueue
if (($key = array_search('mail', $methods)) !== false) { if (($key = array_search('mail', $methods)) !== false) {
unset($methods[$key]); unset($methods[$key]);
$mail_obj = (new PaymentFailureObject($this->client, $this->error, $this->company, $this->payment_hash))->build(); $mail_obj = (new PaymentFailureObject($this->client, $this->error, $this->company, $this->amount))->build();
$nmo = new NinjaMailerObject; $nmo = new NinjaMailerObject;
$nmo->mailable = new NinjaMailer($mail_obj); $nmo->mailable = new NinjaMailer($mail_obj);

View File

@ -55,6 +55,9 @@ class UpdateOrCreateProduct implements ShouldQueue
{ {
MultiDB::setDB($this->company->db); MultiDB::setDB($this->company->db);
if(strval($this->invoice->client->getSetting('currency_id')) != strval($this->company->settings->currency_id))
return;
/* /*
* If the invoice was generated from a Task or Expense then * If the invoice was generated from a Task or Expense then
* we do NOT update the product details this short block we * we do NOT update the product details this short block we

View File

@ -227,6 +227,8 @@ class Import implements ShouldQueue
CompanySizeCheck::dispatch(); CompanySizeCheck::dispatch();
info('Completed🚀🚀🚀🚀🚀 at '.now()); info('Completed🚀🚀🚀🚀🚀 at '.now());
unlink($this->file_path);
} }
private function setInitialCompanyLedgerBalances() private function setInitialCompanyLedgerBalances()

View File

@ -41,6 +41,8 @@ class InvoiceArchivedActivity implements ShouldQueue
{ {
MultiDB::setDb($event->company->db); MultiDB::setDb($event->company->db);
$event->invoice->service()->deletePdf();
$fields = new stdClass; $fields = new stdClass;
$fields->invoice_id = $event->invoice->id; $fields->invoice_id = $event->invoice->id;

View File

@ -26,9 +26,9 @@ class PaymentFailureObject
public $company; public $company;
public $payment_hash; public $amount;
private $invoices; // private $invoices;
/** /**
* Create a new job instance. * Create a new job instance.
@ -38,7 +38,7 @@ class PaymentFailureObject
* @param $company * @param $company
* @param $amount * @param $amount
*/ */
public function __construct($client, $error, $company, $payment_hash) public function __construct($client, $error, $company, $amount)
{ {
$this->client = $client; $this->client = $client;
@ -46,7 +46,7 @@ class PaymentFailureObject
$this->company = $company; $this->company = $company;
$this->payment_hash = $payment_hash; $this->amount = $amount;
$this->company = $company; $this->company = $company;
@ -55,7 +55,7 @@ class PaymentFailureObject
public function build() public function build()
{ {
$this->invoices = Invoice::whereIn('id', $this->transformKeys(array_column($this->payment_hash->invoices(), 'invoice_id')))->get(); // $this->invoices = Invoice::whereIn('id', $this->transformKeys(array_column($this->payment_hash->invoices(), 'invoice_id')))->get();
$mail_obj = new stdClass; $mail_obj = new stdClass;
$mail_obj->amount = $this->getAmount(); $mail_obj->amount = $this->getAmount();
@ -70,7 +70,7 @@ class PaymentFailureObject
private function getAmount() private function getAmount()
{ {
return array_sum(array_column($this->payment_hash->invoices(), 'amount')) + $this->payment_hash->fee_total; return $this->amount;
} }

View File

@ -53,6 +53,7 @@ class Account extends BaseModel
'deleted_at', 'deleted_at',
'promo_expires', 'promo_expires',
'discount_expires', 'discount_expires',
'trial_started',
]; ];
const PLAN_FREE = 'free'; const PLAN_FREE = 'free';
@ -238,14 +239,19 @@ class Account extends BaseModel
} }
$trial_active = false; $trial_active = false;
if ($trial_plan && $include_trial) {
$trial_started = DateTime::createFromFormat('Y-m-d', $this->trial_started);
$trial_expires = clone $trial_started;
$trial_expires->modify('+2 weeks');
if ($trial_expires >= date_create()) { if ($trial_plan && $include_trial) {
$trial_started = $this->trial_started;
$trial_expires = $this->trial_started->addSeconds($this->trial_duration);
// $trial_expires->modify('+2 weeks');
if($trial_expires->greaterThan(now())){
$trial_active = true; $trial_active = true;
} }
// if ($trial_expires >= date_create()) {
// $trial_active = true;
// }
} }
$plan_active = false; $plan_active = false;
@ -265,6 +271,7 @@ class Account extends BaseModel
return null; return null;
} }
// Should we show plan details or trial details? // Should we show plan details or trial details?
if (($plan && ! $trial_plan) || ! $include_trial) { if (($plan && ! $trial_plan) || ! $include_trial) {
$use_plan = true; $use_plan = true;

View File

@ -99,6 +99,36 @@ class Client extends BaseModel implements HasLocalePreference
protected $touches = []; protected $touches = [];
/**
* Whitelisted fields for using from query parameters on subscriptions request.
*
* @var string[]
*/
public static $subscriptions_fillable = [
'assigned_user_id',
'address1',
'address2',
'city',
'state',
'postal_code',
'country_id',
'custom_value1',
'custom_value2',
'custom_value3',
'custom_value4',
'shipping_address1',
'shipping_address2',
'shipping_city',
'shipping_state',
'shipping_postal_code',
'shipping_country_id',
'payment_terms',
'vat_number',
'id_number',
'public_notes',
'phone',
];
public function getEntityType() public function getEntityType()
{ {
return self::class; return self::class;
@ -614,7 +644,7 @@ class Client extends BaseModel implements HasLocalePreference
public function recurring_invoice_filepath() public function recurring_invoice_filepath()
{ {
return $this->company->company_key.'/'.$this->client_hash.'/recurring_invoices/'; return $this->company->company_key.'/'.$this->client_hash.'/recurring_invoices/';
} }
public function company_filepath() public function company_filepath()

View File

@ -95,6 +95,21 @@ class ClientContact extends Authenticatable implements HasLocalePreference
'client_id', 'client_id',
]; ];
/**
* Whitelisted fields for using from query parameters on subscriptions request.
*
* @var string[]
*/
public static $subscription_fillable = [
'first_name',
'last_name',
'phone',
'custom_value1',
'custom_value2',
'custom_value3',
'custom_value4',
'email',
];
/* /*
V2 type of scope V2 type of scope

View File

@ -20,6 +20,16 @@ class Subscription extends BaseModel
{ {
use HasFactory, SoftDeletes; use HasFactory, SoftDeletes;
protected $hidden = [
'id',
'user_id',
'assigned_user_id',
'company_id',
'product_ids',
'recurring_product_ids',
'group_id',
];
protected $fillable = [ protected $fillable = [
'assigned_user_id', 'assigned_user_id',
'product_ids', 'product_ids',

View File

@ -57,6 +57,15 @@ class Webhook extends BaseModel
'target_url', 'target_url',
'format', 'format',
'event_id', 'event_id',
'rest_method',
'headers',
];
protected $casts = [
'headers' => 'array',
'updated_at' => 'timestamp',
'created_at' => 'timestamp',
'deleted_at' => 'timestamp',
]; ];
public function user() public function user()

View File

@ -17,7 +17,9 @@ use App\Models\Design;
use App\Models\Invoice; use App\Models\Invoice;
use App\Services\PdfMaker\Design as PdfMakerDesign; use App\Services\PdfMaker\Design as PdfMakerDesign;
use App\Services\PdfMaker\PdfMaker as PdfMakerService; use App\Services\PdfMaker\PdfMaker as PdfMakerService;
use App\Utils\HostedPDF\NinjaPdf;
use App\Utils\HtmlEngine; use App\Utils\HtmlEngine;
use App\Utils\PhantomJS\Phantom;
use App\Utils\Traits\MakesHash; use App\Utils\Traits\MakesHash;
use App\Utils\Traits\Pdf\PdfMaker; use App\Utils\Traits\Pdf\PdfMaker;
use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Storage;
@ -58,6 +60,10 @@ class GenerateDeliveryNote
$file_path = sprintf('%s%s_delivery_note.pdf', $this->invoice->client->invoice_filepath(), $this->invoice->number); $file_path = sprintf('%s%s_delivery_note.pdf', $this->invoice->client->invoice_filepath(), $this->invoice->number);
if (config('ninja.phantomjs_pdf_generation')) {
return (new Phantom)->generate($this->invoice->invitations->first());
}
$design = Design::find($design_id); $design = Design::find($design_id);
$html = new HtmlEngine($this->invoice->invitations->first()); $html = new HtmlEngine($this->invoice->invitations->first());
@ -86,7 +92,12 @@ class GenerateDeliveryNote
// Storage::makeDirectory($this->invoice->client->invoice_filepath(), 0775); // Storage::makeDirectory($this->invoice->client->invoice_filepath(), 0775);
$pdf = $this->makePdf(null, null, $maker->getCompiledHTML()); if(config('ninja.invoiceninja_hosted_pdf_generation')){
$pdf = (new NinjaPdf())->build($maker->getCompiledHTML(true));
}
else {
$pdf = $this->makePdf(null, null, $maker->getCompiledHTML());
}
if (config('ninja.log_pdf_html')) { if (config('ninja.log_pdf_html')) {
info($maker->getCompiledHTML()); info($maker->getCompiledHTML());

View File

@ -263,7 +263,7 @@ class InvoiceService
{ {
if ((int)$this->invoice->balance == 0) { if ((int)$this->invoice->balance == 0) {
InvoiceWorkflowSettings::dispatch($this->invoice); InvoiceWorkflowSettings::dispatchNow($this->invoice);
$this->setStatus(Invoice::STATUS_PAID); $this->setStatus(Invoice::STATUS_PAID);
} }

View File

@ -12,6 +12,7 @@
namespace App\Services\Payment; namespace App\Services\Payment;
use App\Events\Invoice\InvoiceWasUpdated; use App\Events\Invoice\InvoiceWasUpdated;
use App\Jobs\Invoice\InvoiceWorkflowSettings;
use App\Models\Invoice; use App\Models\Invoice;
use App\Models\Payment; use App\Models\Payment;
use App\Models\PaymentHash; use App\Models\PaymentHash;
@ -81,8 +82,11 @@ class UpdateInvoicePayment
->updateBalance($paid_amount * -1) ->updateBalance($paid_amount * -1)
->updatePaidToDate($paid_amount) ->updatePaidToDate($paid_amount)
->updateStatus() ->updateStatus()
->deletePdf()
->save(); ->save();
InvoiceWorkflowSettings::dispatchNow($invoice);
event(new InvoiceWasUpdated($invoice, $invoice->company, Ninja::eventVars())); event(new InvoiceWasUpdated($invoice, $invoice->company, Ninja::eventVars()));
}); });

View File

@ -251,7 +251,7 @@ class Design extends BaseDesign
public function productTable(): array public function productTable(): array
{ {
$product_items = collect($this->entity->line_items)->filter(function ($item) { $product_items = collect($this->entity->line_items)->filter(function ($item) {
return $item->type_id == 1; return $item->type_id == 1 || $item->type_id == 6;
}); });
if (count($product_items) == 0) { if (count($product_items) == 0) {
@ -412,11 +412,9 @@ class Design extends BaseDesign
$element['elements'][] = ['element' => 'td', 'content' => $row[$cell], 'properties' => ['data-ref' => 'product_table-product.tax2-td']]; $element['elements'][] = ['element' => 'td', 'content' => $row[$cell], 'properties' => ['data-ref' => 'product_table-product.tax2-td']];
} elseif ($cell == '$product.tax_rate3') { } elseif ($cell == '$product.tax_rate3') {
$element['elements'][] = ['element' => 'td', 'content' => $row[$cell], 'properties' => ['data-ref' => 'product_table-product.tax3-td']]; $element['elements'][] = ['element' => 'td', 'content' => $row[$cell], 'properties' => ['data-ref' => 'product_table-product.tax3-td']];
} } else if ($cell == '$product.unit_cost' || $cell == '$task.rate') {
else if($cell == '$product.unit_cost' || $cell == '$task.rate') {
$element['elements'][] = ['element' => 'td', 'content' => $row[$cell], 'properties' => ['style' => 'white-space: nowrap;', 'data-ref' => "{$_type}_table-" . substr($cell, 1) . '-td']]; $element['elements'][] = ['element' => 'td', 'content' => $row[$cell], 'properties' => ['style' => 'white-space: nowrap;', 'data-ref' => "{$_type}_table-" . substr($cell, 1) . '-td']];
} } else {
else {
$element['elements'][] = ['element' => 'td', 'content' => $row[$cell], 'properties' => ['data-ref' => "{$_type}_table-" . substr($cell, 1) . '-td']]; $element['elements'][] = ['element' => 'td', 'content' => $row[$cell], 'properties' => ['data-ref' => "{$_type}_table-" . substr($cell, 1) . '-td']];
} }
} }
@ -456,7 +454,7 @@ class Design extends BaseDesign
['element' => 'div', 'properties' => ['class' => 'totals-table-right-side'], 'elements' => []], ['element' => 'div', 'properties' => ['class' => 'totals-table-right-side'], 'elements' => []],
]; ];
foreach (['discount', 'custom_surcharge1', 'custom_surcharge2', 'custom_surcharge3', 'custom_surcharge4'] as $property) { foreach (['discount'] as $property) {
$variable = sprintf('%s%s', '$', $property); $variable = sprintf('%s%s', '$', $property);
if ( if (
@ -499,6 +497,14 @@ class Design extends BaseDesign
['element' => 'span', 'content', 'content' => Number::formatMoney($tax['total'], $this->context['client']), 'properties' => ['data-ref' => 'totals-table-line_tax_' . $i]], ['element' => 'span', 'content', 'content' => Number::formatMoney($tax['total'], $this->context['client']), 'properties' => ['data-ref' => 'totals-table-line_tax_' . $i]],
]]; ]];
} }
} elseif (Str::startsWith($variable, '$custom')) {
$field = explode('_', $variable);
$visible = property_exists($this->client->company->custom_fields, $field[1]) && !empty($this->client->company->custom_fields->{$field[1]});
$elements[1]['elements'][] = ['element' => 'div', 'elements' => [
['element' => 'span', 'content' => $variable . '_label', 'properties' => ['hidden' => !$visible, 'data-ref' => 'totals_table-' . substr($variable, 1) . '-label']],
['element' => 'span', 'content' => $variable, 'properties' => ['hidden' => !$visible, 'data-ref' => 'totals_table-' . substr($variable, 1)]],
]];
} else { } else {
$elements[1]['elements'][] = ['element' => 'div', 'elements' => [ $elements[1]['elements'][] = ['element' => 'div', 'elements' => [
['element' => 'span', 'content' => $variable . '_label', 'properties' => ['data-ref' => 'totals_table-' . substr($variable, 1) . '-label']], ['element' => 'span', 'content' => $variable . '_label', 'properties' => ['data-ref' => 'totals_table-' . substr($variable, 1) . '-label']],
@ -507,13 +513,6 @@ class Design extends BaseDesign
} }
} }
if (!is_null($this->entity->partial) && $this->entity->partial > 0) {
$elements[1]['elements'][] = ['element' => 'div', 'elements' => [
['element' => 'span', 'content' => '$partial_due_label', 'properties' => ['data-ref' => 'totals_table-partial_due-label']],
['element' => 'span', 'content' => '$partial_due'],
]];
}
$elements[1]['elements'][] = ['element' => 'div', 'elements' => [ $elements[1]['elements'][] = ['element' => 'div', 'elements' => [
['element' => 'span', 'content' => '',], ['element' => 'span', 'content' => '',],
['element' => 'span', 'content' => ''], ['element' => 'span', 'content' => ''],

View File

@ -15,7 +15,9 @@ use App\DataMapper\InvoiceItem;
use App\Factory\InvoiceFactory; use App\Factory\InvoiceFactory;
use App\Factory\InvoiceToRecurringInvoiceFactory; use App\Factory\InvoiceToRecurringInvoiceFactory;
use App\Factory\RecurringInvoiceFactory; use App\Factory\RecurringInvoiceFactory;
use App\Jobs\Util\SubscriptionWebhookHandler;
use App\Jobs\Util\SystemLogger; use App\Jobs\Util\SystemLogger;
use App\Models\Client;
use App\Models\ClientContact; use App\Models\ClientContact;
use App\Models\ClientSubscription; use App\Models\ClientSubscription;
use App\Models\Invoice; use App\Models\Invoice;
@ -27,6 +29,7 @@ use App\Models\SystemLog;
use App\Repositories\InvoiceRepository; use App\Repositories\InvoiceRepository;
use App\Repositories\RecurringInvoiceRepository; use App\Repositories\RecurringInvoiceRepository;
use App\Repositories\SubscriptionRepository; use App\Repositories\SubscriptionRepository;
use App\Utils\Ninja;
use App\Utils\Traits\CleanLineItems; use App\Utils\Traits\CleanLineItems;
use App\Utils\Traits\MakesHash; use App\Utils\Traits\MakesHash;
use GuzzleHttp\RequestOptions; use GuzzleHttp\RequestOptions;
@ -70,7 +73,16 @@ class SubscriptionService
->save(); ->save();
//execute any webhooks //execute any webhooks
$this->triggerWebhook();
$context = [
'context' => 'recurring_purchase',
'recurring_invoice' => $recurring_invoice->hashed_id,
'invoice' => $this->encodePrimaryKey($payment_hash->fee_invoice_id),
'client' => $recurring_invoice->client->hashed_id,
'subscription' => $this->subscription->hashed_id,
];
$this->triggerWebhook($context);
if(array_key_exists('post_purchase_url', $this->subscription->webhook_configuration) && strlen($this->subscription->webhook_configuration['post_purchase_url']) >=1) if(array_key_exists('post_purchase_url', $this->subscription->webhook_configuration) && strlen($this->subscription->webhook_configuration['post_purchase_url']) >=1)
return redirect($this->subscription->webhook_configuration['post_purchase_url']); return redirect($this->subscription->webhook_configuration['post_purchase_url']);
@ -79,9 +91,17 @@ class SubscriptionService
} }
else else
{ {
$invoice = Invoice::find($payment_hash->fee_invoice_id);
$context = [
'context' => 'single_purchase',
'invoice' => $this->encodePrimaryKey($payment_hash->fee_invoice_id),
'client' => $invoice->client->hashed_id,
'subscription' => $this->subscription->hashed_id,
];
//execute any webhooks //execute any webhooks
$this->triggerWebhook(); $this->triggerWebhook($context);
if(array_key_exists('post_purchase_url', $this->subscription->webhook_configuration) && strlen($this->subscription->webhook_configuration['post_purchase_url']) >=1) if(array_key_exists('post_purchase_url', $this->subscription->webhook_configuration) && strlen($this->subscription->webhook_configuration['post_purchase_url']) >=1)
return redirect($this->subscription->webhook_configuration['post_purchase_url']); return redirect($this->subscription->webhook_configuration['post_purchase_url']);
@ -91,11 +111,27 @@ class SubscriptionService
} }
} }
/** /* Hits the client endpoint to determine whether the user is able to access this subscription */
'email' => $this->email ?? $this->contact->email, public function isEligible($contact)
'quantity' => $this->quantity, {
'contact_id' => $this->contact->id,
*/ $context = [
'context' => 'is_eligible',
'subscription' => $this->subscription->hashed_id,
'contact' => $contact->hashed_id,
'contact_email' => $contact->email
];
$response = $this->triggerWebhook($context);
}
/* Starts the process to create a trial
- we create a recurring invoice, which is has its next_send_date as now() + trial_duration
- we then hit the client API end point to advise the trial payload
- we then return the user to either a predefined user endpoint, OR we return the user to the recurring invoice page.
*/
public function startTrial(array $data) public function startTrial(array $data)
{ {
// Redirects from here work just fine. Livewire will respect it. // Redirects from here work just fine. Livewire will respect it.
@ -124,11 +160,19 @@ class SubscriptionService
->start() ->start()
->save(); ->save();
//execute any webhooks $context = [
$this->triggerWebhook(); 'context' => 'trial',
'recurring_invoice' => $recurring_invoice->hashed_id,
'client' => $recurring_invoice->client->hashed_id,
'subscription' => $this->subscription->hashed_id,
];
if(array_key_exists('post_purchase_url', $this->subscription->webhook_configuration) && strlen($this->subscription->webhook_configuration['post_purchase_url']) >=1) //execute any webhooks
return redirect($this->subscription->webhook_configuration['post_purchase_url']); $response = $this->triggerWebhook($context);
if(array_key_exists('return_url', $this->subscription->webhook_configuration) && strlen($this->subscription->webhook_configuration['return_url']) >=1){
return redirect($this->subscription->webhook_configuration['return_url']);
}
return redirect('/client/recurring_invoices/'.$recurring_invoice->hashed_id); return redirect('/client/recurring_invoices/'.$recurring_invoice->hashed_id);
} }
@ -171,67 +215,62 @@ class SubscriptionService
return $recurring_invoice; return $recurring_invoice;
} }
// @deprecated due to change in architecture public function triggerWebhook($context)
// public function createClientSubscription($payment_hash)
// {
// //is this a recurring or one off subscription.
// $cs = new ClientSubscription();
// $cs->subscription_id = $this->subscription->id;
// $cs->company_id = $this->subscription->company_id;
// $cs->invoice_id = $payment_hash->billing_context->invoice_id;
// $cs->client_id = $payment_hash->billing_context->client_id;
// $cs->quantity = $payment_hash->billing_context->quantity;
// //if is_recurring
// //create recurring invoice from invoice
// if($this->subscription->is_recurring)
// {
// $recurring_invoice = $this->convertInvoiceToRecurring($payment_hash);
// $recurring_invoice->frequency_id = $this->subscription->frequency_id;
// $recurring_invoice->next_send_date = $recurring_invoice->nextDateByFrequency(now()->format('Y-m-d'));
// $recurring_invoice->save();
// $cs->recurring_invoice_id = $recurring_invoice->id;
// //?set the recurring invoice as active - set the date here also based on the frequency?
// $recurring_invoice->service()->start();
// }
// $cs->save();
// $this->client_subscription = $cs;
// }
//@todo - need refactor
public function triggerWebhook()
{ {
//hit the webhook to after a successful onboarding /* If no webhooks have been set, then just return gracefully */
if(!array_key_exists('post_purchase_url', $this->subscription->webhook_configuration) || !array_key_exists('post_purchase_rest_method', $this->subscription->webhook_configuration)) {
return true;
}
// $body = [ $response = false;
// 'subscription' => $this->subscription,
// 'client_subscription' => $this->client_subscription, $body = array_merge($context, [
// 'client' => $this->client_subscription->client->toArray(), 'company_key' => $this->subscription->company->company_key,
// ]; 'account_key' => $this->subscription->company->account->key,
'db' => $this->subscription->company->db,
]);
$headers = [
'Content-Type' => 'application/json',
'X-Requested-With' => 'XMLHttpRequest',
];
// $client = new \GuzzleHttp\Client(['headers' => $this->subscription->webhook_configuration->post_purchase_headers]); $client = new \GuzzleHttp\Client(
[
'headers' => $headers,
]);
// $response = $client->{$this->subscription->webhook_configuration->post_purchase_rest_method}($this->subscription->post_purchase_url,[ try {
// RequestOptions::JSON => ['body' => $body] $response = $client->{$this->subscription->webhook_configuration['post_purchase_rest_method']}($this->subscription->webhook_configuration['post_purchase_url'],[
// ]); RequestOptions::JSON => ['body' => $body], RequestOptions::ALLOW_REDIRECTS => false
]);
}
catch(\Exception $e)
{
$body = array_merge($body, ['exception' => $e->getMessage()]);
}
// SystemLogger::dispatch( /* Append the response to the system logger body */
// $body, if($response) {
// SystemLog::CATEGORY_WEBHOOK,
// SystemLog::EVENT_WEBHOOK_RESPONSE, $status = $response->getStatusCode();
// SystemLog::TYPE_WEBHOOK_RESPONSE, $response_body = $response->getBody();
// $this->client_subscription->client, $body = array_merge($body, ['status' => $status, 'response_body' => $response_body]);
// );
}
$client = \App\Models\Client::find($this->decodePrimaryKey($body['client']));
SystemLogger::dispatch(
$body,
SystemLog::CATEGORY_WEBHOOK,
SystemLog::EVENT_WEBHOOK_RESPONSE,
SystemLog::TYPE_WEBHOOK_RESPONSE,
$client,
);
return $response;
} }

View File

@ -65,6 +65,7 @@ class SubscriptionTransformer extends EntityTransformer
'created_at' => (int)$subscription->created_at, 'created_at' => (int)$subscription->created_at,
'updated_at' => (int)$subscription->updated_at, 'updated_at' => (int)$subscription->updated_at,
'archived_at' => (int)$subscription->deleted_at, 'archived_at' => (int)$subscription->deleted_at,
'plan_map' => '', //@deprecated 03/04/2021
]; ];
} }

View File

@ -34,6 +34,8 @@ class WebhookTransformer extends EntityTransformer
'target_url' => $webhook->target_url ? (string) $webhook->target_url : '', 'target_url' => $webhook->target_url ? (string) $webhook->target_url : '',
'event_id' => (string) $webhook->event_id, 'event_id' => (string) $webhook->event_id,
'format' => (string) $webhook->format, 'format' => (string) $webhook->format,
'rest_method' => (string) $webhook->rest_method ?: '',
'headers' => $webhook->headers ?: [],
]; ];
} }
} }

View File

@ -220,14 +220,14 @@ trait CompanySettingsSaver
switch ($key) { switch ($key) {
case 'int': case 'int':
case 'integer': case 'integer':
return ctype_digit(strval(abs($value))); return ctype_digit(strval(abs((int)$value)));
// return is_int($value) || ctype_digit(strval(abs($value))); // return is_int($value) || ctype_digit(strval(abs($value)));
case 'real': case 'real':
case 'float': case 'float':
case 'double': case 'double':
return is_float($value) || is_numeric(strval($value)); return is_float($value) || is_numeric(strval($value));
case 'string': case 'string':
return method_exists($value, '__toString') || is_null($value) || is_string($value); return (is_string($value) && method_exists($value, '__toString')) || is_null($value) || is_string($value);
//return is_null($value) || is_string($value); //return is_null($value) || is_string($value);
case 'bool': case 'bool':
case 'boolean': case 'boolean':

View File

@ -273,7 +273,7 @@ trait MakesInvoiceValues
foreach ($items as $key => $item) { foreach ($items as $key => $item) {
if ($table_type == '$product' && $item->type_id != 1) { if ($table_type == '$product' && $item->type_id != 1) {
if ($item->type_id != 4) { if ($item->type_id != 4 && $item->type_id != 6) {
continue; continue;
} }
} }

View File

@ -26,7 +26,7 @@
], ],
"type": "project", "type": "project",
"require": { "require": {
"php": "^7.3|^7.4", "php": "^7.3|^7.4|^8.0",
"ext-dom": "*", "ext-dom": "*",
"ext-json": "*", "ext-json": "*",
"ext-libxml": "*", "ext-libxml": "*",
@ -38,7 +38,7 @@
"coconutcraig/laravel-postmark": "^2.10", "coconutcraig/laravel-postmark": "^2.10",
"composer/composer": "^2", "composer/composer": "^2",
"czproject/git-php": "^3.17", "czproject/git-php": "^3.17",
"dacastro4/laravel-gmail": "^5.1", "turbo124/laravel-gmail": "^5",
"doctrine/dbal": "^2.10", "doctrine/dbal": "^2.10",
"fideloper/proxy": "^4.2", "fideloper/proxy": "^4.2",
"fzaninotto/faker": "^1.4", "fzaninotto/faker": "^1.4",
@ -71,7 +71,7 @@
"wildbit/swiftmailer-postmark": "^3.3" "wildbit/swiftmailer-postmark": "^3.3"
}, },
"require-dev": { "require-dev": {
"php": "^7.3|^7.4", "php": "^7.3|^7.4|^8.0",
"anahkiasen/former": "^4.2", "anahkiasen/former": "^4.2",
"barryvdh/laravel-debugbar": "^3.4", "barryvdh/laravel-debugbar": "^3.4",
"brianium/paratest": "^6.1", "brianium/paratest": "^6.1",

255
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "2307a2f3214da0d1cc772cc1a1405682", "content-hash": "feb70f976b798dde6550a7b5439bb191",
"packages": [ "packages": [
{ {
"name": "authorizenet/authorizenet", "name": "authorizenet/authorizenet",
@ -51,16 +51,16 @@
}, },
{ {
"name": "aws/aws-sdk-php", "name": "aws/aws-sdk-php",
"version": "3.176.5", "version": "3.176.8",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/aws/aws-sdk-php.git", "url": "https://github.com/aws/aws-sdk-php.git",
"reference": "45d69a8da4f55879f8a24b0b659a5bc7ad077725" "reference": "776b944988167fa3d563d2cbc5fcb6763424a9f0"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/45d69a8da4f55879f8a24b0b659a5bc7ad077725", "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/776b944988167fa3d563d2cbc5fcb6763424a9f0",
"reference": "45d69a8da4f55879f8a24b0b659a5bc7ad077725", "reference": "776b944988167fa3d563d2cbc5fcb6763424a9f0",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -135,9 +135,9 @@
"support": { "support": {
"forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80", "forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80",
"issues": "https://github.com/aws/aws-sdk-php/issues", "issues": "https://github.com/aws/aws-sdk-php/issues",
"source": "https://github.com/aws/aws-sdk-php/tree/3.176.5" "source": "https://github.com/aws/aws-sdk-php/tree/3.176.8"
}, },
"time": "2021-03-31T18:15:22+00:00" "time": "2021-04-05T18:16:29+00:00"
}, },
{ {
"name": "bacon/bacon-qr-code", "name": "bacon/bacon-qr-code",
@ -641,16 +641,16 @@
}, },
{ {
"name": "composer/composer", "name": "composer/composer",
"version": "2.0.11", "version": "2.0.12",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/composer/composer.git", "url": "https://github.com/composer/composer.git",
"reference": "a5a5632da0b1c2d6fa9a3b65f1f4e90d1f04abb9" "reference": "6c12ce263da71641903e399c3ce8ecb08fd375fb"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/composer/composer/zipball/a5a5632da0b1c2d6fa9a3b65f1f4e90d1f04abb9", "url": "https://api.github.com/repos/composer/composer/zipball/6c12ce263da71641903e399c3ce8ecb08fd375fb",
"reference": "a5a5632da0b1c2d6fa9a3b65f1f4e90d1f04abb9", "reference": "6c12ce263da71641903e399c3ce8ecb08fd375fb",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -718,7 +718,7 @@
"support": { "support": {
"irc": "irc://irc.freenode.org/composer", "irc": "irc://irc.freenode.org/composer",
"issues": "https://github.com/composer/composer/issues", "issues": "https://github.com/composer/composer/issues",
"source": "https://github.com/composer/composer/tree/2.0.11" "source": "https://github.com/composer/composer/tree/2.0.12"
}, },
"funding": [ "funding": [
{ {
@ -734,7 +734,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2021-02-24T13:57:23+00:00" "time": "2021-04-01T08:14:59+00:00"
}, },
{ {
"name": "composer/semver", "name": "composer/semver",
@ -1006,76 +1006,6 @@
}, },
"time": "2021-02-15T11:41:33+00:00" "time": "2021-02-15T11:41:33+00:00"
}, },
{
"name": "dacastro4/laravel-gmail",
"version": "v5.1",
"source": {
"type": "git",
"url": "https://github.com/dacastro4/laravel-gmail.git",
"reference": "6d4cabe04f8cdd02b25ef73a1a489099b5e790bd"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/dacastro4/laravel-gmail/zipball/6d4cabe04f8cdd02b25ef73a1a489099b5e790bd",
"reference": "6d4cabe04f8cdd02b25ef73a1a489099b5e790bd",
"shasum": ""
},
"require": {
"google/apiclient": "^2.5",
"illuminate/auth": "~5.8|^6.0|^7.0|^8.0",
"illuminate/config": "~5.8|^6.0|^7.0|^8.0",
"illuminate/database": "~5.8|^6.0|^7.0|^8.0",
"illuminate/routing": "~5.8|^6.0|^7.0|^8.0",
"illuminate/session": "~5.8|^6.0|^7.0|^8.0",
"illuminate/support": "~5.8|^6.0|^7.0|^8.0",
"php": "^7.2",
"swiftmailer/swiftmailer": "~5.8|^6.0"
},
"require-dev": {
"mockery/mockery": "^1.0",
"orchestra/testbench": "^4.0",
"phpunit/phpunit": "^8.5",
"squizlabs/php_codesniffer": "~3.4"
},
"type": "library",
"extra": {
"laravel": {
"providers": [
"Dacastro4\\LaravelGmail\\LaravelGmailServiceProvider"
],
"aliases": {
"LaravelGmail": "Dacastro4\\LaravelGmail\\Facade\\LaravelGmail"
}
}
},
"autoload": {
"psr-4": {
"Dacastro4\\LaravelGmail\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Daniel Castro",
"email": "danielcastro04@gmail.com",
"homepage": "https://danielcastro.me"
}
],
"description": "Gmail API package for Laravel",
"keywords": [
"api",
"gmail",
"laravel"
],
"support": {
"issues": "https://github.com/dacastro4/laravel-gmail/issues",
"source": "https://github.com/dacastro4/laravel-gmail/tree/v5.1"
},
"time": "2020-12-13T19:17:07+00:00"
},
{ {
"name": "dasprid/enum", "name": "dasprid/enum",
"version": "1.0.3", "version": "1.0.3",
@ -1921,26 +1851,27 @@
}, },
{ {
"name": "fzaninotto/faker", "name": "fzaninotto/faker",
"version": "v1.9.2", "version": "dev-master",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/fzaninotto/Faker.git", "url": "https://github.com/fzaninotto/Faker.git",
"reference": "848d8125239d7dbf8ab25cb7f054f1a630e68c2e" "reference": "5ffe7db6c80f441f150fc88008d64e64af66634b"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/fzaninotto/Faker/zipball/848d8125239d7dbf8ab25cb7f054f1a630e68c2e", "url": "https://api.github.com/repos/fzaninotto/Faker/zipball/5ffe7db6c80f441f150fc88008d64e64af66634b",
"reference": "848d8125239d7dbf8ab25cb7f054f1a630e68c2e", "reference": "5ffe7db6c80f441f150fc88008d64e64af66634b",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": "^5.3.3 || ^7.0" "php": "^5.3.3 || ^7.0 || ^8.0"
}, },
"require-dev": { "require-dev": {
"ext-intl": "*", "ext-intl": "*",
"phpunit/phpunit": "^4.8.35 || ^5.7", "phpunit/phpunit": "^4.8.35 || ^5.7",
"squizlabs/php_codesniffer": "^2.9.2" "squizlabs/php_codesniffer": "^2.9.2"
}, },
"default-branch": true,
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
@ -1969,10 +1900,10 @@
], ],
"support": { "support": {
"issues": "https://github.com/fzaninotto/Faker/issues", "issues": "https://github.com/fzaninotto/Faker/issues",
"source": "https://github.com/fzaninotto/Faker/tree/v1.9.2" "source": "https://github.com/fzaninotto/Faker/tree/master"
}, },
"abandoned": true, "abandoned": true,
"time": "2020-12-11T09:56:16+00:00" "time": "2020-12-11T09:59:14+00:00"
}, },
{ {
"name": "google/apiclient", "name": "google/apiclient",
@ -2045,7 +1976,7 @@
}, },
{ {
"name": "google/apiclient-services", "name": "google/apiclient-services",
"version": "v0.166.0", "version": "v0.167.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/googleapis/google-api-php-client-services.git", "url": "https://github.com/googleapis/google-api-php-client-services.git",
@ -2080,7 +2011,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/googleapis/google-api-php-client-services/issues", "issues": "https://github.com/googleapis/google-api-php-client-services/issues",
"source": "https://github.com/googleapis/google-api-php-client-services/tree/v0.166.0" "source": "https://github.com/googleapis/google-api-php-client-services/tree/v0.167.0"
}, },
"time": "2021-03-22T11:26:04+00:00" "time": "2021-03-22T11:26:04+00:00"
}, },
@ -3824,27 +3755,28 @@
}, },
{ {
"name": "league/omnipay", "name": "league/omnipay",
"version": "v3.1.0", "version": "dev-master",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/thephpleague/omnipay.git", "url": "https://github.com/thephpleague/omnipay.git",
"reference": "1ba7c8a3312cf2342458b99c9e5b86eaae44aed2" "reference": "e9439db0633ba988e6f6cdd029fad38aad73f9f6"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/thephpleague/omnipay/zipball/1ba7c8a3312cf2342458b99c9e5b86eaae44aed2", "url": "https://api.github.com/repos/thephpleague/omnipay/zipball/e9439db0633ba988e6f6cdd029fad38aad73f9f6",
"reference": "1ba7c8a3312cf2342458b99c9e5b86eaae44aed2", "reference": "e9439db0633ba988e6f6cdd029fad38aad73f9f6",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"omnipay/common": "^3", "omnipay/common": "^3",
"php": "^7.2", "php": "^7.2|^8.0",
"php-http/discovery": "^1.12", "php-http/discovery": "^1.12",
"php-http/guzzle7-adapter": "^0.1" "php-http/guzzle7-adapter": "^0.1"
}, },
"require-dev": { "require-dev": {
"omnipay/tests": "^3" "omnipay/tests": "^3"
}, },
"default-branch": true,
"type": "metapackage", "type": "metapackage",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
@ -3875,22 +3807,22 @@
], ],
"support": { "support": {
"issues": "https://github.com/thephpleague/omnipay/issues", "issues": "https://github.com/thephpleague/omnipay/issues",
"source": "https://github.com/thephpleague/omnipay/tree/v3.1.0" "source": "https://github.com/thephpleague/omnipay/tree/master"
}, },
"time": "2020-09-22T14:02:17+00:00" "time": "2021-03-12T09:17:59+00:00"
}, },
{ {
"name": "livewire/livewire", "name": "livewire/livewire",
"version": "v2.4.1", "version": "v2.4.2",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/livewire/livewire.git", "url": "https://github.com/livewire/livewire.git",
"reference": "b0cb782674673a67ddfd5910d2fcb5308bb32857" "reference": "2495387841a3eb03ac62b2c984ccd2574303285b"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/livewire/livewire/zipball/b0cb782674673a67ddfd5910d2fcb5308bb32857", "url": "https://api.github.com/repos/livewire/livewire/zipball/2495387841a3eb03ac62b2c984ccd2574303285b",
"reference": "b0cb782674673a67ddfd5910d2fcb5308bb32857", "reference": "2495387841a3eb03ac62b2c984ccd2574303285b",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -3941,7 +3873,7 @@
"description": "A front-end framework for Laravel.", "description": "A front-end framework for Laravel.",
"support": { "support": {
"issues": "https://github.com/livewire/livewire/issues", "issues": "https://github.com/livewire/livewire/issues",
"source": "https://github.com/livewire/livewire/tree/v2.4.1" "source": "https://github.com/livewire/livewire/tree/v2.4.2"
}, },
"funding": [ "funding": [
{ {
@ -3949,7 +3881,7 @@
"type": "github" "type": "github"
} }
], ],
"time": "2021-03-22T14:03:36+00:00" "time": "2021-04-04T15:46:50+00:00"
}, },
{ {
"name": "maennchen/zipstream-php", "name": "maennchen/zipstream-php",
@ -4628,21 +4560,21 @@
}, },
{ {
"name": "omnipay/common", "name": "omnipay/common",
"version": "v3.0.5", "version": "dev-master",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/thephpleague/omnipay-common.git", "url": "https://github.com/thephpleague/omnipay-common.git",
"reference": "0d1f4486c1c873537ac030d37c7ce2986c4de1d2" "reference": "e1ebc22615f14219d31cefdf62d7036feb228b1c"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/thephpleague/omnipay-common/zipball/0d1f4486c1c873537ac030d37c7ce2986c4de1d2", "url": "https://api.github.com/repos/thephpleague/omnipay-common/zipball/e1ebc22615f14219d31cefdf62d7036feb228b1c",
"reference": "0d1f4486c1c873537ac030d37c7ce2986c4de1d2", "reference": "e1ebc22615f14219d31cefdf62d7036feb228b1c",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"moneyphp/money": "^3.1", "moneyphp/money": "^3.1",
"php": "^5.6|^7", "php": "^5.6|^7|^8",
"php-http/client-implementation": "^1", "php-http/client-implementation": "^1",
"php-http/discovery": "^1.2.1", "php-http/discovery": "^1.2.1",
"php-http/message": "^1.5", "php-http/message": "^1.5",
@ -4657,6 +4589,7 @@
"suggest": { "suggest": {
"league/omnipay": "The default Omnipay package provides a default HTTP Adapter." "league/omnipay": "The default Omnipay package provides a default HTTP Adapter."
}, },
"default-branch": true,
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
@ -4708,9 +4641,9 @@
], ],
"support": { "support": {
"issues": "https://github.com/thephpleague/omnipay-common/issues", "issues": "https://github.com/thephpleague/omnipay-common/issues",
"source": "https://github.com/thephpleague/omnipay-common/tree/v3.0.5" "source": "https://github.com/thephpleague/omnipay-common/tree/master"
}, },
"time": "2020-08-20T18:22:12+00:00" "time": "2020-12-13T12:53:48+00:00"
}, },
{ {
"name": "omnipay/paypal", "name": "omnipay/paypal",
@ -5645,23 +5578,22 @@
}, },
{ {
"name": "predis/predis", "name": "predis/predis",
"version": "v1.1.6", "version": "v1.1.7",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/predis/predis.git", "url": "https://github.com/predis/predis.git",
"reference": "9930e933c67446962997b05201c69c2319bf26de" "reference": "b240daa106d4e02f0c5b7079b41e31ddf66fddf8"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/predis/predis/zipball/9930e933c67446962997b05201c69c2319bf26de", "url": "https://api.github.com/repos/predis/predis/zipball/b240daa106d4e02f0c5b7079b41e31ddf66fddf8",
"reference": "9930e933c67446962997b05201c69c2319bf26de", "reference": "b240daa106d4e02f0c5b7079b41e31ddf66fddf8",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": ">=5.3.9" "php": ">=5.3.9"
}, },
"require-dev": { "require-dev": {
"cweagans/composer-patches": "^1.6",
"phpunit/phpunit": "~4.8" "phpunit/phpunit": "~4.8"
}, },
"suggest": { "suggest": {
@ -5669,18 +5601,6 @@
"ext-phpiredis": "Allows faster serialization and deserialization of the Redis protocol" "ext-phpiredis": "Allows faster serialization and deserialization of the Redis protocol"
}, },
"type": "library", "type": "library",
"extra": {
"composer-exit-on-patch-failure": true,
"patches": {
"phpunit/phpunit-mock-objects": {
"Fix PHP 7 and 8 compatibility": "./tests/phpunit_mock_objects.patch"
},
"phpunit/phpunit": {
"Fix PHP 7 compatibility": "./tests/phpunit_php7.patch",
"Fix PHP 8 compatibility": "./tests/phpunit_php8.patch"
}
}
},
"autoload": { "autoload": {
"psr-4": { "psr-4": {
"Predis\\": "src/" "Predis\\": "src/"
@ -5712,7 +5632,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/predis/predis/issues", "issues": "https://github.com/predis/predis/issues",
"source": "https://github.com/predis/predis/tree/v1.1.6" "source": "https://github.com/predis/predis/tree/v1.1.7"
}, },
"funding": [ "funding": [
{ {
@ -5720,7 +5640,7 @@
"type": "github" "type": "github"
} }
], ],
"time": "2020-09-11T19:18:05+00:00" "time": "2021-04-04T19:34:46+00:00"
}, },
{ {
"name": "psr/cache", "name": "psr/cache",
@ -9761,6 +9681,75 @@
}, },
"time": "2021-03-23T09:54:29+00:00" "time": "2021-03-23T09:54:29+00:00"
}, },
{
"name": "turbo124/laravel-gmail",
"version": "v5.0.1",
"source": {
"type": "git",
"url": "https://github.com/turbo124/laravel-gmail.git",
"reference": "55ca0271a54a568ebaa26febbe0790b2ce5ac966"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/turbo124/laravel-gmail/zipball/55ca0271a54a568ebaa26febbe0790b2ce5ac966",
"reference": "55ca0271a54a568ebaa26febbe0790b2ce5ac966",
"shasum": ""
},
"require": {
"google/apiclient": "^2.5",
"illuminate/auth": "~5.8|^6.0|^7.0|^8.0",
"illuminate/config": "~5.8|^6.0|^7.0|^8.0",
"illuminate/database": "~5.8|^6.0|^7.0|^8.0",
"illuminate/routing": "~5.8|^6.0|^7.0|^8.0",
"illuminate/session": "~5.8|^6.0|^7.0|^8.0",
"illuminate/support": "~5.8|^6.0|^7.0|^8.0",
"php": "^7.3|^7.4|^8.0",
"swiftmailer/swiftmailer": "~5.8|^6.0"
},
"require-dev": {
"mockery/mockery": "^1.0",
"orchestra/testbench": "^4.0",
"phpunit/phpunit": "^8.5",
"squizlabs/php_codesniffer": "~3.4"
},
"type": "library",
"extra": {
"laravel": {
"providers": [
"Dacastro4\\LaravelGmail\\LaravelGmailServiceProvider"
],
"aliases": {
"LaravelGmail": "Dacastro4\\LaravelGmail\\Facade\\LaravelGmail"
}
}
},
"autoload": {
"psr-4": {
"Dacastro4\\LaravelGmail\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Daniel Castro",
"email": "danielcastro04@gmail.com",
"homepage": "https://danielcastro.me"
}
],
"description": "Gmail API package for Laravel",
"keywords": [
"api",
"gmail",
"laravel"
],
"support": {
"source": "https://github.com/turbo124/laravel-gmail/tree/v5.0.1"
},
"time": "2021-04-06T00:53:48+00:00"
},
{ {
"name": "vlucas/phpdotenv", "name": "vlucas/phpdotenv",
"version": "v5.3.0", "version": "v5.3.0",
@ -14133,13 +14122,13 @@
"prefer-stable": true, "prefer-stable": true,
"prefer-lowest": false, "prefer-lowest": false,
"platform": { "platform": {
"php": "^7.3|^7.4", "php": "^7.3|^7.4|^8.0",
"ext-dom": "*", "ext-dom": "*",
"ext-json": "*", "ext-json": "*",
"ext-libxml": "*" "ext-libxml": "*"
}, },
"platform-dev": { "platform-dev": {
"php": "^7.3|^7.4" "php": "^7.3|^7.4|^8.0"
}, },
"plugin-api-version": "2.0.0" "plugin-api-version": "2.0.0"
} }

View File

@ -43,7 +43,7 @@ return [
'redis' => [ 'redis' => [
'driver' => 'redis', 'driver' => 'redis',
'connection' => 'default', 'connection' => env('REDIS_BROADCAST_CONNECTION', 'default'),
], ],
'log' => [ 'log' => [

View File

@ -74,7 +74,7 @@ return [
'redis' => [ 'redis' => [
'driver' => 'redis', 'driver' => 'redis',
'connection' => 'cache', 'connection' => env('REDIS_CACHE_CONNECTION', 'cache'),
], ],
'dynamodb' => [ 'dynamodb' => [

View File

@ -153,6 +153,42 @@ return [
'database' => env('REDIS_CACHE_DB', 1), 'database' => env('REDIS_CACHE_DB', 1),
], ],
'sentinel-default' => array_merge(
array_map(
function ($a, $b) {
return ["host" => $a,"port" => $b];
},
explode(',', env('REDIS_HOST', 'localhost')),
explode(',', env('REDIS_PORT', 26379))
),
['options' => [
'replication' => 'sentinel',
'service' => env('REDIS_SENTINEL_SERVICE', 'mymaster'),
'parameters' => [
'password' => env('REDIS_PASSWORD', null),
'database' => env('REDIS_DB', 0),
],
]]
),
'sentinel-cache' => array_merge(
array_map(
function ($a, $b) {
return ["host" => $a,"port" => $b];
},
explode(',', env('REDIS_HOST', 'localhost')),
explode(',', env('REDIS_PORT', 26379))
),
['options' => [
'replication' => 'sentinel',
'service' => env('REDIS_SENTINEL_SERVICE', 'mymaster'),
'parameters' => [
'password' => env('REDIS_PASSWORD', null),
'database' => env('REDIS_CACHE_DB', 1),
],
]]
),
], ],
]; ];

View File

@ -14,7 +14,7 @@ return [
'require_https' => env('REQUIRE_HTTPS', true), 'require_https' => env('REQUIRE_HTTPS', true),
'app_url' => rtrim(env('APP_URL', ''), '/'), 'app_url' => rtrim(env('APP_URL', ''), '/'),
'app_domain' => env('APP_DOMAIN', ''), 'app_domain' => env('APP_DOMAIN', ''),
'app_version' => '5.1.34', 'app_version' => '5.1.35',
'minimum_client_version' => '5.0.16', 'minimum_client_version' => '5.0.16',
'terms_version' => '1.0.1', 'terms_version' => '1.0.1',
'api_secret' => env('API_SECRET', false), 'api_secret' => env('API_SECRET', false),
@ -142,7 +142,7 @@ return [
'expanded_logging' => env('EXPANDED_LOGGING', false), 'expanded_logging' => env('EXPANDED_LOGGING', false),
'snappdf_chromium_path' => env('SNAPPDF_CHROMIUM_PATH', false), 'snappdf_chromium_path' => env('SNAPPDF_CHROMIUM_PATH', false),
'v4_migration_version' => '4.5.35', 'v4_migration_version' => '4.5.35',
'flutter_canvas_kit' => env('FLUTTER_CANVAS_KIT', 'selfhosted-html'), 'flutter_renderer' => env('FLUTTER_RENDERER', 'selfhosted-html'),
'webcron_secret' => env('WEBCRON_SECRET', false), 'webcron_secret' => env('WEBCRON_SECRET', false),
'disable_auto_update' => env('DISABLE_AUTO_UPDATE', false), 'disable_auto_update' => env('DISABLE_AUTO_UPDATE', false),
'invoiceninja_hosted_pdf_generation' => env('NINJA_HOSTED_PDF', false), 'invoiceninja_hosted_pdf_generation' => env('NINJA_HOSTED_PDF', false),

View File

@ -61,7 +61,7 @@ return [
'redis' => [ 'redis' => [
'driver' => 'redis', 'driver' => 'redis',
'connection' => 'default', 'connection' => env('REDIS_QUEUE_CONNECTION', 'default'),
'queue' => env('REDIS_QUEUE', 'default'), 'queue' => env('REDIS_QUEUE', 'default'),
'retry_after' => 90000000, 'retry_after' => 90000000,
'block_for' => null, 'block_for' => null,

View File

@ -0,0 +1,30 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddTrialDurationToAccountsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('accounts', function (Blueprint $table) {
$table->unsignedInteger('trial_duration')->nullable();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
}
}

View File

@ -0,0 +1,33 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddRestFieldsToWebhooksTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('webhooks', function (Blueprint $table) {
$table->text('rest_method')->nullable();
$table->text('headers')->nullable();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('webhooks', function (Blueprint $table) {
//
});
}
}

View File

@ -31,7 +31,7 @@ const RESOURCES = {
"assets/assets/images/payment_types/paypal.png": "8e06c094c1871376dfea1da8088c29d1", "assets/assets/images/payment_types/paypal.png": "8e06c094c1871376dfea1da8088c29d1",
"assets/assets/images/payment_types/maestro.png": "e533b92bfb50339fdbfa79e3dfe81f08", "assets/assets/images/payment_types/maestro.png": "e533b92bfb50339fdbfa79e3dfe81f08",
"assets/FontManifest.json": "cf3c681641169319e61b61bd0277378f", "assets/FontManifest.json": "cf3c681641169319e61b61bd0277378f",
"main.dart.js": "54ad5a5e961909b8be9d8dd395bb3628", "main.dart.js": "23e852b45f0ac1c4cf3d45a272ec0c83",
"version.json": "e021a7a1750aa3e7d1d89b51ac9837e9" "version.json": "e021a7a1750aa3e7d1d89b51ac9837e9"
}; };

158643
public/main.dart.js vendored

File diff suppressed because one or more lines are too long

160513
public/main.foss.dart.js vendored

File diff suppressed because one or more lines are too long

160109
public/main.wasm.dart.js vendored

File diff suppressed because one or more lines are too long

View File

@ -2,6 +2,7 @@
if(!isset($design)) { if(!isset($design)) {
$design = 'light'; $design = 'light';
} }
$primary_color = isset($settings) ? $settings->primary_color : '#4caf50';
@endphp @endphp
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
@ -16,11 +17,11 @@
<style type="text/css"> <style type="text/css">
:root { :root {
--primary-color: {{ isset($settings) ? $settings->primary_color : '#4caf50' }}; --primary-color: {{ $primary_color }};
} }
.primary-color-bg { .primary-color-bg {
background-color: {{ isset($settings) ? $settings->primary_color : '#4caf50' }}; background-color: {{ $primary_color }};
} }
#email-content h1, h2, h3, h4 { #email-content h1, h2, h3, h4 {
@ -38,7 +39,7 @@
} }
.button { .button {
background-color: {{ isset($settings) ? $settings->primary_color : '#4caf50' }}; background-color: {{ $primary_color }};
color: white; color: white;
padding: 10px 16px; padding: 10px 16px;
text-decoration: none; text-decoration: none;
@ -68,7 +69,7 @@
<table align="center" cellpadding="0" cellspacing="0" width="600" <table align="center" cellpadding="0" cellspacing="0" width="600"
style="box-shadow: 0 1px 3px 0 rgba(0,0,0,.1), 0 1px 2px 0 rgba(0,0,0,.06)"> style="box-shadow: 0 1px 3px 0 rgba(0,0,0,.1), 0 1px 2px 0 rgba(0,0,0,.06)">
<tr> <tr>
<td align="center" bgcolor="#4caf50" class="primary-color-bg" style="padding: 40px 0 30px 0;"> <td align="center" bgcolor="{{ $primary_color }}" class="primary-color-bg" style="padding: 40px 0 30px 0;">
{{ $header }} {{ $header }}
</td> </td>
</tr> </tr>

View File

@ -146,9 +146,9 @@
</script> </script>
@if(config('ninja.flutter_canvas_kit') == 'hosted') @if(config('ninja.flutter_renderer') == 'hosted')
<script defer src="main.dart.js?v={{ config('ninja.app_version') }}" type="application/javascript"></script> <script defer src="main.dart.js?v={{ config('ninja.app_version') }}" type="application/javascript"></script>
@elseif(config('ninja.flutter_canvas_kit') == 'selfhosted-canvaskit') @elseif(config('ninja.flutter_renderer') == 'selfhosted-canvaskit')
<script defer src="main.wasm.dart.js?v={{ config('ninja.app_version') }}" type="application/javascript"></script> <script defer src="main.wasm.dart.js?v={{ config('ninja.app_version') }}" type="application/javascript"></script>
@else @else
<script defer src="main.foss.dart.js?v={{ config('ninja.app_version') }}" type="application/javascript"></script> <script defer src="main.foss.dart.js?v={{ config('ninja.app_version') }}" type="application/javascript"></script>

View File

@ -19,7 +19,10 @@
@foreach($subscription->service()->products() as $product) @foreach($subscription->service()->products() as $product)
<div class="flex items-center justify-between mb-4 bg-white rounded px-6 py-4 shadow-sm border"> <div class="flex items-center justify-between mb-4 bg-white rounded px-6 py-4 shadow-sm border">
<div class="text-sm">{{ $product->product_key }}</div> <div>
<p class="text-sm text-xl">{{ $product->product_key }}</p>
<p class="text-sm text-gray-800">{{ $product->notes }}</p>
</div>
<div data-ref="price-and-quantity-container"> <div data-ref="price-and-quantity-container">
<span <span
data-ref="price">{{ \App\Utils\Number::formatMoney($product->price, $subscription->company) }}</span> data-ref="price">{{ \App\Utils\Number::formatMoney($product->price, $subscription->company) }}</span>
@ -57,8 +60,7 @@
<div class="relative flex justify-center text-sm leading-5"> <div class="relative flex justify-center text-sm leading-5">
<h1 class="text-2xl font-bold tracking-wide bg-gray-50 px-6 py-0"> <h1 class="text-2xl font-bold tracking-wide bg-gray-50 px-6 py-0">
{{ ctrans('texts.total') }} {{ ctrans('texts.total') }}: {{ \App\Utils\Number::formatMoney($price, $subscription->company) }}
: {{ \App\Utils\Number::formatMoney($price, $subscription->company) }}
@if($steps['discount_applied']) @if($steps['discount_applied'])
<small class="ml-1 line-through text-gray-500">{{ \App\Utils\Number::formatMoney($subscription->price, $subscription->company) }}</small> <small class="ml-1 line-through text-gray-500">{{ \App\Utils\Number::formatMoney($subscription->price, $subscription->company) }}</small>

View File

@ -3,7 +3,7 @@
@push('head') @push('head')
<meta name="show-invoice-terms" content="{{ $settings->show_accept_invoice_terms ? true : false }}"> <meta name="show-invoice-terms" content="{{ $settings->show_accept_invoice_terms ? true : false }}">
<meta name="require-invoice-signature" content="{{ $settings->require_invoice_signature ? true : false }}"> <meta name="require-invoice-signature" content="{{ $client->company->account->hasFeature(\App\Models\Account::FEATURE_INVOICE_SETTINGS) && $settings->require_invoice_signature }}">
<script src="https://cdn.jsdelivr.net/npm/signature_pad@2.3.2/dist/signature_pad.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/signature_pad@2.3.2/dist/signature_pad.min.js"></script>
@endpush @endpush

View File

@ -4,7 +4,7 @@
@push('head') @push('head')
<meta name="pdf-url" content="{{ $invoice->pdf_file_path() }}"> <meta name="pdf-url" content="{{ $invoice->pdf_file_path() }}">
<meta name="show-invoice-terms" content="{{ $settings->show_accept_invoice_terms ? true : false }}"> <meta name="show-invoice-terms" content="{{ $settings->show_accept_invoice_terms ? true : false }}">
<meta name="require-invoice-signature" content="{{ $settings->require_invoice_signature ? true : false }}"> <meta name="require-invoice-signature" content="{{ $client->company->account->hasFeature(\App\Models\Account::FEATURE_INVOICE_SETTINGS) && $settings->require_invoice_signature }}">
<script src="{{ asset('js/vendor/pdf.js/pdf.min.js') }}"></script> <script src="{{ asset('js/vendor/pdf.js/pdf.min.js') }}"></script>
<script src="https://cdn.jsdelivr.net/npm/signature_pad@2.3.2/dist/signature_pad.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/signature_pad@2.3.2/dist/signature_pad.min.js"></script>
@endpush @endpush

View File

@ -3,7 +3,7 @@
@push('head') @push('head')
<meta name="show-quote-terms" content="{{ $settings->show_accept_quote_terms ? true : false }}"> <meta name="show-quote-terms" content="{{ $settings->show_accept_quote_terms ? true : false }}">
<meta name="require-quote-signature" content="{{ $settings->require_quote_signature ? true : false }}"> <meta name="require-quote-signature" content="{{ $client->company->account->hasFeature(\App\Models\Account::FEATURE_INVOICE_SETTINGS) && $settings->require_quote_signature }}">
<script src="https://cdn.jsdelivr.net/npm/signature_pad@2.3.2/dist/signature_pad.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/signature_pad@2.3.2/dist/signature_pad.min.js"></script>
@endpush @endpush

View File

@ -0,0 +1,65 @@
<?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://opensource.org/licenses/AAL
*/
namespace Tests\Feature\Ninja;
use App\Models\Account;
use App\Utils\Traits\MakesHash;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Facades\Session;
use Tests\MockAccountData;
use Tests\TestCase;
/**
* @test
*/
class PlanTest extends TestCase
{
use MakesHash;
use DatabaseTransactions;
use MockAccountData;
public function setUp() :void
{
parent::setUp();
$this->makeTestData();
Session::start();
$this->faker = \Faker\Factory::create();
Model::reguard();
}
public function testTrialFeatures()
{
config(['ninja.production' => true]);
$this->assertFalse($this->account->hasFeature(Account::FEATURE_USERS));
$this->account->trial_plan = 'enterprise';
$this->account->trial_started = now();
$this->account->trial_duration = 60*60*24*31;
$this->account->save();
$this->assertFalse($this->account->hasFeature(Account::FEATURE_USERS));
$this->account->trial_plan = 'pro';
$this->account->save();
$this->assertFalse($this->account->hasFeature(Account::FEATURE_USERS));
$this->assertTrue($this->account->hasFeature(Account::FEATURE_CUSTOM_URL));
}
}

View File

@ -55,4 +55,36 @@ class CompanySettingsTest extends TestCase
$this->assertEquals(1, count($diff)); $this->assertEquals(1, count($diff));
} }
public function testStringEquivalence()
{
$value = (strval(4) != strval(3));
$this->assertTrue($value);
$value = (strval(4) != strval(4));
$this->assertFalse($value);
$value = (strval('4') != strval(4));
$this->assertFalse($value);
$value = (strval('4') != strval('4'));
$this->assertFalse($value);
$value = (strval('4') != strval(3));
$this->assertTrue($value);
$value = (strval(4) != strval('3'));
$this->assertTrue($value);
$value = (strval('4') != strval('3'));
$this->assertTrue($value);
}
} }