fix conflicts

This commit is contained in:
David Bomba 2021-03-22 07:22:20 +11:00
commit f80179814d
51 changed files with 95264 additions and 94749 deletions

View File

@ -7,15 +7,7 @@
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/d39acb4bf0f74a0698dc77f382769ba5)](https://www.codacy.com/app/turbo124/invoiceninja?utm_source=github.com&utm_medium=referral&utm_content=invoiceninja/invoiceninja&utm_campaign=Badge_Grade)
# Invoice Ninja version 5.1 RC2!
Invoice Ninja version 5.1 has now reached Release Candidate 2!
What does this mean exactly? We consider this version _almost_ stable. There may be some remaining small issues which we would love to get feedback on. We would really appreciate the community booting up this version and attempting the migration from their Invoice Ninja V4 application and inspect the migrated data.
We'd also like feedback on any issues that you can see, and help us nail down the few remaining issues before Version 5 graduates to Stable Gold Release.
Please note we do not consider this version ready for production use, please stick with your V4 installation for your production clients!
# Invoice Ninja version 5!
## Quick Start

View File

@ -1 +1 @@
5.1.27
5.1.29

View File

@ -101,6 +101,7 @@ class CreateSingleAccount extends Command
$company = Company::factory()->create([
'account_id' => $account->id,
'slack_webhook_url' => config('ninja.notification.slack'),
'default_password_timeout' => 30*60000,
]);
$account->default_company_id = $company->id;

View File

@ -58,23 +58,16 @@ class Kernel extends ConsoleKernel
$schedule->job(new RecurringInvoicesCron)->hourly()->withoutOverlapping();
$schedule->job(new SchedulerCheck)->everyFiveMinutes();
/* Run hosted specific jobs */
if (Ninja::isHosted()) {
$schedule->job(new AdjustEmailQuota())->daily()->withoutOverlapping();
$schedule->job(new SendFailedEmails())->daily()->withoutOverlapping();
$schedule->job(new AdjustEmailQuota)->daily()->withoutOverlapping();
$schedule->job(new SendFailedEmails)->daily()->withoutOverlapping();
}
/* Run queue's with this*/
if (Ninja::isSelfHost()) {
$schedule->command('queue:work --daemon')->everyMinute()->withoutOverlapping();
//we need to add this as we are seeing cached queues mess up the system on first load.
$schedule->command('queue:restart')->everyFiveMinutes()->withoutOverlapping();
$schedule->job(new SchedulerCheck)->everyFiveMinutes()->withoutOverlapping();
}
}
/**

View File

@ -35,12 +35,18 @@ class WebhookConfiguration
*/
public $post_purchase_body = '';
/**
* @var string
*/
public $post_purchase_rest_method = 'POST';
/**
* @var array
*/
public static $casts = [
'return_url' => 'string',
'post_purchase_url' => 'string',
'post_purchase_rest_method' => 'string',
'post_purchase_headers' => 'array',
'post_purchase_body' => 'object',
];

View File

@ -51,7 +51,7 @@ class InvoiceItem
public $custom_value4 = '';
public $type_id = '1'; //1 = product, 2 = service, 3 unpaid gateway fee, 4 paid gateway fee, 5 late fee
public $type_id = '1'; //1 = product, 2 = service, 3 unpaid gateway fee, 4 paid gateway fee, 5 late fee, 6 promo code
public static $casts = [
'type_id' => 'string',

View File

@ -37,6 +37,7 @@ class CompanyFactory
$company->enabled_modules = config('ninja.enabled_modules'); //32767;//8191; //4095
$company->default_password_timeout = 1800000;
return $company;
}
}

View File

@ -175,7 +175,7 @@ class LoginController extends BaseController
{
$google2fa = new Google2FA();
if(!$google2fa->verifyKey(decrypt($user->google_2fa_secret), $request->input('one_time_password')))
if(strlen($request->input('one_time_password')) == 0 || !$google2fa->verifyKey(decrypt($user->google_2fa_secret), $request->input('one_time_password')))
{
return response()
->json(['message' => ctrans('texts.invalid_one_time_password')], 401)
@ -194,6 +194,7 @@ class LoginController extends BaseController
$user->setCompany($user->account->default_company);
$timeout = auth()->user()->company()->default_password_timeout;
Cache::put(auth()->user()->hashed_id.'_logged_in', Str::random(64), $timeout);
$cu = CompanyUser::query()
@ -322,33 +323,35 @@ class LoginController extends BaseController
if ($user) {
$client = new Google_Client();
$client->setClientId(config('ninja.auth.google.client_id'));
$client->setClientSecret(config('ninja.auth.google.client_secret'));
$client->setRedirectUri(config('ninja.app_url'));
// we are no longer accessing the permissions for gmail - email permissions here
// $client = new Google_Client();
// $client->setClientId(config('ninja.auth.google.client_id'));
// $client->setClientSecret(config('ninja.auth.google.client_secret'));
// $client->setRedirectUri(config('ninja.app_url'));
$token = false;
try{
$token = $client->authenticate(request()->input('server_auth_code'));
}
catch(\Exception $e) {
// try{
// $token = $client->authenticate(request()->input('server_auth_code'));
// }
// catch(\Exception $e) {
return response()
->json(['message' => ctrans('texts.invalid_credentials')], 401)
->header('X-App-Version', config('ninja.app_version'))
->header('X-Api-Version', config('ninja.minimum_client_version'));
// return response()
// ->json(['message' => ctrans('texts.invalid_credentials')], 401)
// ->header('X-App-Version', config('ninja.app_version'))
// ->header('X-Api-Version', config('ninja.minimum_client_version'));
}
// }
// $refresh_token = '';
// if (array_key_exists('refresh_token', $token)) {
// $refresh_token = $token['refresh_token'];
// }
$refresh_token = '';
if (array_key_exists('refresh_token', $token)) {
$refresh_token = $token['refresh_token'];
}
//$access_token = $token['access_token'];
$name = OAuth::splitName($google->harvestName($user));
$new_account = [

View File

@ -21,9 +21,9 @@ use Illuminate\Http\Request;
class ConnectedAccountController extends BaseController
{
protected $entity_type = CompanyUser::class;
protected $entity_type = User::class;
protected $entity_transformer = CompanyUserTransformer::class;
protected $entity_transformer = UserTransformer::class;
public function __construct()
{
@ -104,7 +104,6 @@ class ConnectedAccountController extends BaseController
}
$connected_account = [
'password' => '',
'email' => $google->harvestEmail($user),
'oauth_user_id' => $google->harvestSubField($user),
'oauth_user_token' => $token,
@ -117,11 +116,8 @@ class ConnectedAccountController extends BaseController
auth()->user()->email_verified_at = now();
auth()->user()->save();
//$ct = CompanyUser::whereUserId(auth()->user()->id);
//return $this->listResponse($ct);
return $this->itemResponse(auth()->user());
// return $this->listResponse(auth()->user());
}
return response()

View File

@ -42,7 +42,7 @@ class PasswordProtection
if($timeout == 0)
$timeout = null;
else
$timeout = now()->addMinutes($timeout);
$timeout = now()->addMinutes($timeout/60000);
if (Cache::get(auth()->user()->hashed_id.'_logged_in')) {

View File

@ -54,7 +54,7 @@ class QueryLogging
nlog($request->method().' - '.$request->url().": $count queries - ".$time);
// if($count > 50)
// Log::nlog($queries);
//nlog($queries);
}
}

View File

@ -19,6 +19,7 @@ use App\Models\Client;
use App\Models\GroupSetting;
use App\Utils\Traits\MakesHash;
use Illuminate\Support\Facades\Cache;
use Illuminate\Validation\Rule;
class StoreClientRequest extends Request
{
@ -52,7 +53,6 @@ class StoreClientRequest extends Request
/* Ensure we have a client name, and that all emails are unique*/
//$rules['name'] = 'required|min:1';
$rules['id_number'] = 'unique:clients,id_number,'.$this->id.',id,company_id,'.$this->company_id;
$rules['settings'] = new ValidClientGroupSettingsRule();
$rules['contacts.*.email'] = 'bail|nullable|distinct|sometimes|email';
$rules['contacts.*.password'] = [
@ -70,6 +70,10 @@ class StoreClientRequest extends Request
$rules['hosted_clients'] = new CanStoreClientsRule($this->company_id);
}
$rules['number'] = ['nullable',Rule::unique('clients')->where('company_id', auth()->user()->company()->id)];
$rules['id_number'] = ['nullable',Rule::unique('clients')->where('company_id', auth()->user()->company()->id)];
return $rules;
}
@ -126,7 +130,7 @@ class StoreClientRequest extends Request
public function messages()
{
return [
'unique' => ctrans('validation.unique', ['attribute' => 'email']),
// 'unique' => ctrans('validation.unique', ['attribute' => ['email','number']),
//'required' => trans('validation.required', ['attribute' => 'email']),
'contacts.*.email.required' => ctrans('validation.email', ['attribute' => 'email']),
];

View File

@ -16,6 +16,7 @@ use App\Http\Requests\Request;
use App\Http\ValidationRules\ValidClientGroupSettingsRule;
use App\Utils\Traits\ChecksEntityStatus;
use App\Utils\Traits\MakesHash;
use Illuminate\Validation\Rule;
class UpdateClientRequest extends Request
{
@ -52,7 +53,14 @@ class UpdateClientRequest extends Request
$rules['country_id'] = 'integer|nullable';
$rules['shipping_country_id'] = 'integer|nullable';
//$rules['id_number'] = 'unique:clients,id_number,,id,company_id,' . auth()->user()->company()->id;
$rules['id_number'] = 'unique:clients,id_number,'.$this->id.',id,company_id,'.$this->company_id;
//$rules['id_number'] = 'unique:clients,id_number,'.$this->id.',id,company_id,'.$this->company_id;
if($this->id_number)
$rules['id_number'] = Rule::unique('clients')->where('company_id', auth()->user()->company()->id)->ignore($this->client->id);
if($this->number)
$rules['number'] = Rule::unique('clients')->where('company_id', auth()->user()->company()->id)->ignore($this->client->id);
$rules['settings'] = new ValidClientGroupSettingsRule();
$rules['contacts.*.email'] = 'bail|nullable|distinct|sometimes|email';
$rules['contacts.*.password'] = [
@ -72,7 +80,6 @@ class UpdateClientRequest extends Request
public function messages()
{
return [
'unique' => ctrans('validation.unique', ['attribute' => 'email']),
'email' => ctrans('validation.email', ['attribute' => 'email']),
'name.required' => ctrans('validation.required', ['attribute' => 'name']),
'required' => ctrans('validation.required', ['attribute' => 'email']),

View File

@ -16,6 +16,7 @@ use App\Http\Requests\Request;
use App\Utils\Traits\ChecksEntityStatus;
use App\Utils\Traits\CleanLineItems;
use App\Utils\Traits\MakesHash;
use Illuminate\Validation\Rule;
class UpdateCreditRequest extends Request
{
@ -52,9 +53,8 @@ class UpdateCreditRequest extends Request
$rules['documents'] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
}
if ($this->input('number')) {
$rules['number'] = 'unique:credits,number,'.$this->id.',id,company_id,'.$this->credit->company_id;
}
if($this->number)
$rules['number'] = Rule::unique('credits')->where('company_id', auth()->user()->company()->id)->ignore($this->credit->id);
$rules['line_items'] = 'array';

View File

@ -35,9 +35,8 @@ class StoreExpenseRequest extends Request
{
$rules = [];
if (isset($this->number)) {
if ($this->number)
$rules['number'] = Rule::unique('expenses')->where('company_id', auth()->user()->company()->id);
}
if(!empty($this->client_id))
$rules['client_id'] = 'bail|sometimes|exists:clients,id,company_id,'.auth()->user()->company()->id;

View File

@ -16,6 +16,7 @@ use App\Http\ValidationRules\Invoice\LockedInvoiceRule;
use App\Utils\Traits\ChecksEntityStatus;
use App\Utils\Traits\CleanLineItems;
use App\Utils\Traits\MakesHash;
use Illuminate\Validation\Rule;
class UpdateInvoiceRequest extends Request
{
@ -49,10 +50,8 @@ class UpdateInvoiceRequest extends Request
$rules['id'] = new LockedInvoiceRule($this->invoice);
// if ($this->input('number') && strlen($this->input('number')) >= 1) {
if ($this->input('number')) {
$rules['number'] = 'unique:invoices,number,'.$this->id.',id,company_id,'.$this->invoice->company_id;
}
if($this->number)
$rules['number'] = Rule::unique('invoices')->where('company_id', auth()->user()->company()->id)->ignore($this->invoice->id);
$rules['line_items'] = 'array';

View File

@ -16,6 +16,7 @@ use App\Http\ValidationRules\PaymentAppliedValidAmount;
use App\Http\ValidationRules\ValidCreditsPresentRule;
use App\Utils\Traits\ChecksEntityStatus;
use App\Utils\Traits\MakesHash;
use Illuminate\Validation\Rule;
class UpdatePaymentRequest extends Request
{
@ -35,12 +36,14 @@ class UpdatePaymentRequest extends Request
public function rules()
{
$rules = [
'number' => 'nullable|unique:payments,number,'.$this->id.',id,company_id,'.$this->payment->company_id,
'invoices' => ['array', new PaymentAppliedValidAmount, new ValidCreditsPresentRule],
'invoices.*.invoice_id' => 'distinct',
'documents' => 'mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx',
];
if($this->number)
$rules['number'] = Rule::unique('payments')->where('company_id', auth()->user()->company()->id)->ignore($this->payment->id);
if ($this->input('documents') && is_array($this->input('documents'))) {
$documents = count($this->input('documents'));

View File

@ -15,6 +15,7 @@ use App\Http\Requests\Request;
use App\Utils\Traits\ChecksEntityStatus;
use App\Utils\Traits\CleanLineItems;
use App\Utils\Traits\MakesHash;
use Illuminate\Validation\Rule;
class UpdateQuoteRequest extends Request
{
@ -46,9 +47,8 @@ class UpdateQuoteRequest extends Request
$rules['documents'] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
}
if ($this->input('number')) {
$rules['number'] = 'unique:quotes,number,'.$this->id.',id,company_id,'.$this->quote->company_id;
}
if($this->number)
$rules['number'] = Rule::unique('quotes')->where('company_id', auth()->user()->company()->id)->ignore($this->quote->id);
$rules['line_items'] = 'array';

View File

@ -16,6 +16,7 @@ use App\Utils\Traits\ChecksEntityStatus;
use App\Utils\Traits\CleanLineItems;
use App\Utils\Traits\MakesHash;
use Illuminate\Http\UploadedFile;
use Illuminate\Validation\Rule;
class UpdateRecurringInvoiceRequest extends Request
{
@ -47,9 +48,9 @@ class UpdateRecurringInvoiceRequest extends Request
$rules['documents'] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
}
if ($this->input('number')) {
$rules['number'] = 'unique:recurring_invoices,number,'.$this->recurring_invoice->id.',id,company_id,'.$this->recurring_invoice->company_id;
}
if($this->number)
$rules['number'] = Rule::unique('recurring_invoices')->where('company_id', auth()->user()->company()->id)->ignore($this->recurring_invoice->id);
return $rules;
}

View File

@ -14,6 +14,7 @@ namespace App\Http\Requests\RecurringQuote;
use App\Http\Requests\Request;
use App\Utils\Traits\ChecksEntityStatus;
use App\Utils\Traits\CleanLineItems;
use Illuminate\Validation\Rule;
class UpdateRecurringQuoteRequest extends Request
{
@ -47,6 +48,9 @@ class UpdateRecurringQuoteRequest extends Request
$input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : [];
if($this->number)
$rules['number'] = Rule::unique('recurring_quotes')->where('company_id', auth()->user()->company()->id)->ignore($this->recurring_quote->id);
$this->replace($input);
}
}

View File

@ -12,6 +12,7 @@
namespace App\Http\Requests\TaxRate;
use App\Http\Requests\Request;
use Illuminate\Validation\Rule;
class UpdateTaxRateRequest extends Request
{
@ -27,10 +28,14 @@ class UpdateTaxRateRequest extends Request
public function rules()
{
return [
// 'name' => 'unique:tax_rates,name,'.$this->tax_rate->name.',id,company_id,'.auth()->user()->companyId(),
'name' => 'unique:tax_rates,name,'.$this->id.',id,company_id,'.$this->company_id,
'rate' => 'numeric',
];
$rules = [];
$rules['rate'] = 'numeric';
if($this->number)
$rules['number'] = Rule::unique('tax_rates')->where('company_id', auth()->user()->company()->id)->ignore($this->tax_rate->id);
return $rules;
}
}

View File

@ -14,6 +14,7 @@ namespace App\Http\Requests\Vendor;
use App\Http\Requests\Request;
use App\Utils\Traits\ChecksEntityStatus;
use App\Utils\Traits\MakesHash;
use Illuminate\Validation\Rule;
class UpdateVendorRequest extends Request
{
@ -35,18 +36,15 @@ class UpdateVendorRequest extends Request
/* Ensure we have a client name, and that all emails are unique*/
$rules['country_id'] = 'integer|nullable';
//$rules['id_number'] = 'unique:clients,id_number,,id,company_id,' . auth()->user()->company()->id;
$rules['id_number'] = 'unique:clients,id_number,'.$this->id.',id,company_id,'.$this->company_id;
if($this->number)
$rules['number'] = Rule::unique('vendors')->where('company_id', auth()->user()->company()->id)->ignore($this->vendor->id);
if($this->id_number)
$rules['id_number'] = Rule::unique('vendors')->where('company_id', auth()->user()->company()->id)->ignore($this->vendor->id);
$rules['contacts.*.email'] = 'nullable|distinct';
$contacts = request('contacts');
if (is_array($contacts)) {
// for ($i = 0; $i < count($contacts); $i++) {
// // $rules['contacts.' . $i . '.email'] = 'nullable|email|unique:client_contacts,email,' . isset($contacts[$i]['id'].',company_id,'.$this->company_id);
// //$rules['contacts.' . $i . '.email'] = 'nullable|email';
// }
}
return $rules;
}
@ -54,7 +52,6 @@ class UpdateVendorRequest extends Request
public function messages()
{
return [
'unique' => ctrans('validation.unique', ['attribute' => 'email']),
'email' => ctrans('validation.email', ['attribute' => 'email']),
'name.required' => ctrans('validation.required', ['attribute' => 'name']),
'required' => ctrans('validation.required', ['attribute' => 'email']),

View File

@ -37,6 +37,7 @@ class Project extends BaseModel
'custom_value4',
'assigned_user_id',
'color',
'number',
];
public function getEntityType()

View File

@ -241,7 +241,7 @@ class BaseDriver extends AbstractPaymentDriver
event(new PaymentWasCreated($payment, $payment->company, Ninja::eventVars()));
(new BillingSubscriptionService)->completePurchase($this->payment_hash);
//(new BillingSubscriptionService)->completePurchase($this->payment_hash);
return $payment->service()->applyNumber()->save();
}

View File

@ -11,6 +11,6 @@ class CreditPolicy extends EntityPolicy
public function create(User $user) : bool
{
return $user->isAdmin() || $user->hasPermission('create_quote') || $user->hasPermission('create_all');
return $user->isAdmin() || $user->hasPermission('create_credit') || $user->hasPermission('create_all');
}
}

View File

@ -20,6 +20,6 @@ class TaskPolicy extends EntityPolicy
{
public function create(User $user) : bool
{
return $user->isAdmin();
return $user->isAdmin() || $user->hasPermission('create_task') || $user->hasPermission('create_all');
}
}

View File

@ -13,17 +13,25 @@ namespace App\Services\BillingSubscription;
use App\DataMapper\InvoiceItem;
use App\Factory\InvoiceFactory;
use App\Jobs\Util\SystemLogger;
use App\Models\BillingSubscription;
use App\Models\ClientSubscription;
use App\Models\PaymentHash;
use App\Models\Product;
use App\Models\SystemLog;
use App\Repositories\InvoiceRepository;
use App\Utils\Traits\MakesHash;
use GuzzleHttp\RequestOptions;
class BillingSubscriptionService
{
use MakesHash;
/** @var BillingSubscription */
private $billing_subscription;
private $client_subscription;
public function __construct(BillingSubscription $billing_subscription)
{
$this->billing_subscription = $billing_subscription;
@ -56,7 +64,7 @@ class BillingSubscriptionService
{
$invoice_repo = new InvoiceRepository();
$data['line_items'] = $this->createLineItems($data['quantity']);
$data['line_items'] = $this->createLineItems($data);
/*
If trial_enabled -> return early
@ -74,48 +82,154 @@ class BillingSubscriptionService
}
private function createLineItems($quantity): array
/**
* Creates the required line items for the invoice
* for the billing subscription.
*/
private function createLineItems($data): array
{
$line_items = [];
$product = $this->billing_subscription->product;
$item = new InvoiceItem;
$item->quantity = $quantity;
$item->quantity = $data['quantity'];
$item->product_key = $product->product_key;
$item->notes = $product->notes;
$item->cost = $product->price;
$item->tax_rate1 = $product->tax_rate1 ?: 0;
$item->tax_name1 = $product->tax_name1 ?: '';
$item->tax_rate2 = $product->tax_rate2 ?: 0;
$item->tax_name2 = $product->tax_name2 ?: '';
$item->tax_rate3 = $product->tax_rate3 ?: 0;
$item->tax_name3 = $product->tax_name3 ?: '';
$item->custom_value1 = $product->custom_value1 ?: '';
$item->custom_value2 = $product->custom_value2 ?: '';
$item->custom_value3 = $product->custom_value3 ?: '';
$item->custom_value4 = $product->custom_value4 ?: '';
//$item->type_id need to switch whether the subscription is a service or product
$line_items[] = $item;
//do we have a promocode? enter this as a line item.
if(strlen($data['coupon']) >=1 && ($data['coupon'] == $this->billing_subscription->promo_code) && $this->billing_subscription->promo_discount > 0)
$line_items[] = $this->createPromoLine($data);
return $line_items;
}
private function convertInvoiceToRecurring()
/**
* If a coupon is entered (and is valid)
* then we apply the coupon discount with a line item.
*/
private function createPromoLine($data)
{
$product = $this->billing_subscription->product;
$discounted_amount = 0;
$discount = 0;
$amount = $data['quantity'] * $product->cost;
if ($this->billing_subscription->is_amount_discount == true) {
$discount = $this->billing_subscription->promo_discount;
}
else {
$discount = round($amount * ($this->billing_subscription->promo_discount / 100), 2);
}
$discounted_amount = $amount - $discount;
$item = new InvoiceItem;
$item->quantity = 1;
$item->product_key = ctrans('texts.promo_code');
$item->notes = ctrans('texts.promo_code');
$item->cost = $discounted_amount;
$item->tax_rate1 = $product->tax_rate1 ?: 0;
$item->tax_name1 = $product->tax_name1 ?: '';
$item->tax_rate2 = $product->tax_rate2 ?: 0;
$item->tax_name2 = $product->tax_name2 ?: '';
$item->tax_rate3 = $product->tax_rate3 ?: 0;
$item->tax_name3 = $product->tax_name3 ?: '';
return $item;
}
private function convertInvoiceToRecurring($payment_hash)
{
//The first invoice is a plain invoice - the second is fired on the recurring schedule.
}
public function createClientSubscription($payment_hash, $recurring_invoice_id = null)
public function createClientSubscription($payment_hash)
{
//create the client sub record
//create the client subscription record
//are we in a trial phase?
//has money been paid?
//is this a recurring or one off subscription.
//?trial enabled?
$cs = new ClientSubscription();
$cs->subscription_id = $this->billing_subscription->id;
$cs->company_id = $this->billing_subscription->company_id;
//if is_trial
//$cs->trial_started = time();
//$cs->trial_duration = time() + duration period in seconds
//trials will not have any monies paid.
//if a payment has been made
//$cs->invoice_id = xx
//if is_recurring
//create recurring invoice from invoice
$recurring_invoice = $this->convertInvoiceToRecurring($payment_hash);
$recurring_invoice->frequency_id = $this->billing_subscription->frequency_id;
$recurring_invoice->next_send_date = $recurring_invoice->nextDateByFrequency(now()->format('Y-m-d'));
//$cs->recurring_invoice_id = $recurring_invoice->id;
//?set the recurring invoice as active - set the date here also based on the frequency?
//$cs->quantity = xx
// client_id
//$cs->client_id = xx
$cs->save();
$this->client_subscription = $cs;
}
public function triggerWebhook($payment_hash)
{
//hit the webhook to after a successful onboarding
//$client = xxxxxxx
//todo webhook
$body = [
'billing_subscription' => $this->billing_subscription,
'client_subscription' => $this->client_subscription,
// 'client' => $client->toArray(),
];
$client = new \GuzzleHttp\Client(['headers' => $this->billing_subscription->webhook_configuration->post_purchase_headers]);
$response = $client->{$this->billing_subscription->webhook_configuration->post_purchase_rest_method}($this->billing_subscription->post_purchase_url,[
RequestOptions::JSON => ['body' => $body]
]);
SystemLogger::dispatch(
$body,
SystemLog::CATEGORY_WEBHOOK,
SystemLog::EVENT_WEBHOOK_RESPONSE,
SystemLog::TYPE_WEBHOOK_RESPONSE,
//$client,
);
}
public function fireNotifications()

View File

@ -302,7 +302,7 @@ class Design extends BaseDesign
public function buildTableHeader(string $type): array
{
$this->processTaxColumns($type);
$this->processCustomColumns($type);
// $this->processCustomColumns($type);
$elements = [];

View File

@ -213,6 +213,7 @@ trait PdfMakerUtilities
$css = <<<'EOT'
table.page-container {
page-break-after: always;
min-width: 100%;
}
thead.page-header {

View File

@ -78,9 +78,15 @@ class SystemHealth
'phantom_enabled' => (bool)config('ninja.phantomjs_pdf_generation'),
'exec' => (bool)self::checkExecWorks(),
'open_basedir' => (bool)self::checkOpenBaseDir(),
'mail_mailer' => (string)self::checkMailMailer(),
];
}
public static function checkMailMailer()
{
return config('mail.default');
}
public static function checkOpenBaseDir()
{
if (strlen(ini_get('open_basedir') == 0)) {

View File

@ -34,7 +34,7 @@ trait UserNotifies
array_push($required_permissions, 'all_user_notifications');
}
if (count(array_intersect($required_permissions, $notifications->email)) >= 1 || count(array_intersect($required_permissions, ['all_user_notifications'])) >= 1 || count(array_intersect($required_permissions, 'all_notifications')) >= 1) {
if (count(array_intersect($required_permissions, $notifications->email)) >= 1 || count(array_intersect($required_permissions, ['all_user_notifications'])) >= 1 || count(array_intersect($required_permissions, ['all_notifications'])) >= 1) {
array_push($notifiable_methods, 'mail');
}

View File

@ -13,7 +13,7 @@ return [
'require_https' => env('REQUIRE_HTTPS', true),
'app_url' => rtrim(env('APP_URL', ''), '/'),
'app_domain' => env('APP_DOMAIN', ''),
'app_version' => '5.1.27',
'app_version' => '5.1.29',
'minimum_client_version' => '5.0.16',
'terms_version' => '1.0.1',
'api_secret' => env('API_SECRET', false),

View File

@ -37,6 +37,7 @@ class CompanyFactory extends Factory
'db' => config('database.default'),
'settings' => CompanySettings::defaults(),
'is_large' => false,
'default_password_timeout' => 30*60000,
'enabled_modules' => config('ninja.enabled_modules'),
'custom_fields' => (object) [
//'invoice1' => 'Custom Date|date',

View File

@ -0,0 +1,65 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddUniqueConstraintsOnAllEntities extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('expenses', function (Blueprint $table) {
$table->unique(['company_id', 'number']);
});
Schema::table('tasks', function (Blueprint $table) {
$table->unique(['company_id', 'number']);
});
Schema::table('vendors', function (Blueprint $table) {
$table->unique(['company_id', 'number']);
});
Schema::table('payments', function (Blueprint $table) {
$table->unique(['company_id', 'number']);
});
Schema::table('projects', function (Blueprint $table) {
$table->unique(['company_id', 'number']);
});
Schema::table('clients', function (Blueprint $table) {
$table->unique(['company_id', 'number']);
});
Schema::table('payment_hashes', function (Blueprint $table) {
$table->unique(['hash']);
});
Schema::table('recurring_invoices', function (Blueprint $table) {
$table->string('number')->change();
$table->unique(['company_id', 'number']);
});
Schema::table('recurring_invoice_invitations', function (Blueprint $table) {
$table->unique(['client_contact_id', 'recurring_invoice_id'],'recur_invoice_client_unique');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
//
}
}

View File

@ -0,0 +1,31 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddInvoiceIdToClientSubscriptionsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('client_subscriptions', function (Blueprint $table) {
$table->unsignedInteger('invoice_id')->nullable();
$table->unsignedInteger('quantity')->default(1);
$table->foreign('invoice_id')->references('id')->on('invoices')->onDelete('cascade')->onUpdate('cascade');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
}
}

View File

@ -4,7 +4,7 @@ const TEMP = 'flutter-temp-cache';
const CACHE_NAME = 'flutter-app-cache';
const RESOURCES = {
"favicon.ico": "51636d3a390451561744c42188ccd628",
"manifest.json": "77215c1737c7639764e64a192be2f7b8",
"manifest.json": "ce1b79950eb917ea619a0a30da27c6a3",
"icons/Icon-192.png": "bb1cf5f6982006952211c7c8404ffbed",
"icons/Icon-512.png": "0f9aff01367f0a0c69773d25ca16ef35",
"assets/NOTICES": "e80e999afd09f0f14597c78d582d9c7c",
@ -30,7 +30,7 @@ const RESOURCES = {
"assets/packages/material_design_icons_flutter/lib/fonts/materialdesignicons-webfont.ttf": "3e722fd57a6db80ee119f0e2c230ccff",
"assets/FontManifest.json": "cf3c681641169319e61b61bd0277378f",
"/": "23224b5e03519aaa87594403d54412cf",
"main.dart.js": "1edd6ac83b22ed1c401a76cefe7eaa7d",
"main.dart.js": "a15d902ac164ae4f5ec2bcdf3470d7e0",
"version.json": "b7c8971e1ab5b627fd2a4317c52b843e",
"favicon.png": "dca91c54388f52eded692718d5a98b8b"
};

189213
public/main.dart.js vendored

File diff suppressed because one or more lines are too long

View File

@ -11,7 +11,6 @@
"related_applications": [
{
"platform": "play",
"url": "https://play.google.com/store/apps/details?id=com.invoiceninja.app",
"id": "com.invoiceninja.app"
}, {
"platform": "itunes",

View File

@ -1976,7 +1976,7 @@ $LANG = array(
'require_invoice_signature_help' => 'Require client to provide their signature.',
'require_quote_signature' => 'Quote Signature',
'require_quote_signature_help' => 'Require client to provide their signature.',
'i_agree' => 'I Agree To',
'i_agree' => 'I Agree To The Terms',
'sign_here' => 'Please sign here:',
'authorization' => 'Authorization',
'signed' => 'Signed',
@ -3449,10 +3449,10 @@ $LANG = array(
'client_country' => 'Client Country',
'client_is_active' => 'Client is Active',
'client_balance' => 'Client Balance',
'client_address1' => 'Client Address 1',
'client_address2' => 'Client Address 2',
'client_shipping_address1' => 'Client Shipping Address 1',
'client_shipping_address2' => 'Client Shipping Address 2',
'client_address1' => 'Client Street',
'client_address2' => 'Client Apt/Suite',
'client_shipping_address1' => 'Client Shipping Street',
'client_shipping_address2' => 'Client Shipping Apt/Suite',
'tax_rate1' => 'Tax Rate 1',
'tax_rate2' => 'Tax Rate 2',
'tax_rate3' => 'Tax Rate 3',
@ -3532,8 +3532,8 @@ $LANG = array(
'marked_credit_as_sent' => 'Successfully marked credit as sent',
'email_subject_payment_partial' => 'Email Partial Payment Subject',
'is_approved' => 'Is Approved',
'migration_went_wrong' => 'Oops, something went wrong! Make sure you did proper setup with V2 of Invoice Ninja, before starting migration.',
'cross_migration_message' => 'Cross account migration is not allowed. Please read more about it here: <a href="https://invoiceninja.github.io/cross-site-migration.html">https://invoiceninja.github.io/cross-site-migration.html</a>',
'migration_went_wrong' => 'Oops, something went wrong! Please make sure you have setup an Invoice Ninja v5 instance before starting the migration.',
'cross_migration_message' => 'Cross account migration is not allowed. Please read more about it here: <a href="https://invoiceninja.github.io/docs/migration/#troubleshooting">https://invoiceninja.github.io/docs/migration/#troubleshooting</a>',
'email_credit' => 'Email Credit',
'client_email_not_set' => 'Client does not have an email address set',
'ledger' => 'Ledger',
@ -3912,7 +3912,7 @@ $LANG = array(
'show' => 'Show',
'empty_columns' => 'Empty Columns',
'project_name' => 'Project Name',
'counter_pattern_error' => 'To use :client_counter please add either :number or :id_number to prevent conflicts',
'counter_pattern_error' => 'To use :client_counter please add either :client_number or :client_id_number to prevent conflicts',
'this_quarter' => 'This Quarter',
'to_update_run' => 'To update run',
'registration_url' => 'Registration URL',
@ -3968,8 +3968,8 @@ $LANG = array(
'list_of_recurring_invoices' => 'List of recurring invoices',
'details_of_recurring_invoice' => 'Here are some details about recurring invoice',
'cancellation' => 'Cancellation',
'about_cancellation' => 'In case you want to stop the recurring invoice, please click the request the cancellation.',
'cancellation_warning' => 'Warning! You are requesting a cancellation of this service. Your service may be cancelled with no further notification to you.',
'about_cancellation' => 'In case you want to stop the recurring invoice,\n please click the request the cancellation.',
'cancellation_warning' => 'Warning! You are requesting a cancellation of this service.\n Your service may be cancelled with no further notification to you.',
'cancellation_pending' => 'Cancellation pending, we\'ll be in touch!',
'list_of_payments' => 'List of payments',
'payment_details' => 'Details of the payment',
@ -4135,19 +4135,26 @@ $LANG = array(
'payment_message_extended' => 'Thank you for your payment of :amount for :invoice',
'online_payments_minimum_note' => 'Note: Online payments are supported only if amount is bigger than $1 or currency equivalent.',
'payment_token_not_found' => 'Payment token not found, please try again. If an issue still persist, try with another payment method',
/////////////////////////////////////////////////
'vendor_address1' => 'Vendor Street',
'vendor_address2' => 'Vendor Apt/Suite',
'partially_unapplied' => 'Partially Unapplied',
'select_a_gmail_user' => 'Please select a user authenticated with Gmail',
'list_long_press' => 'List Long Press',
'show_actions' => 'Show Actions',
'start_multiselect' => 'Start Multiselect',
'email_sent_to_confirm_email' => 'An email has been sent to confirm the email address',
'converted_paid_to_date' => 'Converted Paid to Date',
'converted_credit_balance' => 'Converted Credit Balance',
'converted_total' => 'Converted Total',
'reply_to_name' => 'Reply-To Name',
'payment_status_-2' => 'Partially Unapplied',
'color_theme' => 'Color Theme',
'start_migration' => 'Start Migration',
'recurring_cancellation_request' => 'Request for recurring invoice cancellation from :contact',
'recurring_cancellation_request_body' => ':contact from Client :client requested to cancel Recurring Invoice :invoice',
'hello' => 'Hello',
'group_documents' => 'Group documents',
'quote_approval_confirmation_label' => 'Are you sure you want to approve this quote?',
'click_agree_to_accept_terms' => 'Click "Agree" to Accept Terms.',
'agree' => 'Agree',
'pending_approval' => 'Pending Approval',
'migration_select_company_label' => 'Select companies to migrate',
'force_migration' => 'Force migration',
'require_password_with_social_login' => 'Require Password with Social Login',
@ -4170,6 +4177,29 @@ $LANG = array(
'migration_auth_label' => 'Let\'s continue by authenticating.',
'api_secret' => 'API secret',
'migration_api_secret_notice' => 'You can find API_SECRET in the .env file or Invoice Ninja v5. If property is missing, leave field blank.',
'use_last_email' => 'Use last email',
'activate_company' => 'Activate Company',
'activate_company_help' => 'Enable emails, recurring invoices and notifications',
'an_error_occurred_try_again' => 'An error occurred, please try again',
'please_first_set_a_password' => 'Please first set a password',
'changing_phone_disables_two_factor' => 'Warning: Changing your phone number will disable 2FA',
'help_translate' => 'Help Translate',
'please_select_a_country' => 'Please select a country',
'disabled_two_factor' => 'Successfully disabled 2FA',
'connected_google' => 'Successfully connected account',
'disconnected_google' => 'Successfully disconnected account',
'delivered' => 'Delivered',
'spam' => 'Spam',
'view_docs' => 'View Docs',
'enter_phone_to_enable_two_factor' => 'Please provide a mobile phone number to enable two factor authentication',
'send_sms' => 'Send SMS',
'sms_code' => 'SMS Code',
'connect_google' => 'Connect Google',
'disconnect_google' => 'Disconnect Google',
'disable_two_factor' => 'Disable Two Factor',
'invoice_task_datelog' => 'Invoice Task Datelog',
'invoice_task_datelog_help' => 'Add date details to the invoice line items',
'promo_code' => 'Promo code',
);
return $LANG;

View File

@ -262,7 +262,7 @@
<div class="contacts-wrapper">
<div class="contact-wrapper-left-side">
<p class="contact-label">$to_label:</p>
<p class="contact-label">$from_label:</p>
<div class="company-info">
<div id="company-details"></div>
<div id="company-address"></div>
@ -270,7 +270,7 @@
</div>
<div class="contact-wrapper-right-side">
<p class="contact-label">$from_label:</p>
<p class="contact-label">$to_label:</p>
<div id="client-details"></div>
</div>
</div>

View File

@ -54,6 +54,28 @@ class ClientApiTest extends TestCase
$response->assertStatus(200);
}
public function testDuplicateNumberCatch()
{
$data = [
'name' => $this->faker->firstName,
'number' => 'iamaduplicate',
];
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->post('/api/v1/clients', $data);
$response->assertStatus(200);
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->post('/api/v1/clients', $data);
$response->assertStatus(302);
}
public function testClientPut()
{
$data = [
@ -67,6 +89,20 @@ class ClientApiTest extends TestCase
])->put('/api/v1/clients/'.$this->encodePrimaryKey($this->client->id), $data);
$response->assertStatus(200);
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->put('/api/v1/clients/'.$this->encodePrimaryKey($this->client->id), $data);
$response->assertStatus(200);
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->post('/api/v1/clients/', $data);
$response->assertStatus(302);
}
public function testClientGet()

View File

@ -131,4 +131,80 @@ class CreditTest extends TestCase
$response->assertStatus(200);
}
public function testDuplicateNumberCatch()
{
$data = [
'status_id' => 1,
'number' => 'dfdfd',
'discount' => 0,
'is_amount_discount' => 1,
'number' => '3434343',
'public_notes' => 'notes',
'is_deleted' => 0,
'custom_value1' => 0,
'custom_value2' => 0,
'custom_value3' => 0,
'custom_value4' => 0,
'status' => 1,
'client_id' => $this->encodePrimaryKey($this->client->id),
];
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->post('/api/v1/credits', $data);
$response->assertStatus(200);
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->post('/api/v1/credits', $data);
$response->assertStatus(302);
}
public function testCreditPut()
{
$data = [
'status_id' => 1,
'number' => 'dfdfd',
'discount' => 0,
'is_amount_discount' => 1,
'number' => '3434343',
'public_notes' => 'notes',
'is_deleted' => 0,
'custom_value1' => 0,
'custom_value2' => 0,
'custom_value3' => 0,
'custom_value4' => 0,
'status' => 1,
'client_id' => $this->encodePrimaryKey($this->client->id),
];
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->put('/api/v1/credits/'.$this->encodePrimaryKey($this->credit->id), $data);
$response->assertStatus(200);
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->put('/api/v1/credits/'.$this->encodePrimaryKey($this->credit->id), $data);
$response->assertStatus(200);
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->post('/api/v1/credits/', $data);
$response->assertStatus(302);
}
}

View File

@ -57,11 +57,34 @@ class ExpenseApiTest extends TestCase
$this->assertNotEmpty($arr['data']['number']);
}
public function testDuplicateNumberCatch()
{
$data = [
'public_notes' => $this->faker->firstName,
'number' => 'iamaduplicate',
];
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->post('/api/v1/expenses', $data);
$response->assertStatus(200);
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->post('/api/v1/expenses', $data);
$response->assertStatus(302);
}
public function testExpensePut()
{
$data = [
'public_notes' => $this->faker->firstName,
'id_number' => 'Coolio',
'number' => 'Coolio',
];
$response = $this->withHeaders([
@ -70,6 +93,22 @@ class ExpenseApiTest extends TestCase
])->put('/api/v1/expenses/'.$this->encodePrimaryKey($this->expense->id), $data);
$response->assertStatus(200);
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->put('/api/v1/expenses/'.$this->encodePrimaryKey($this->expense->id), $data);
$response->assertStatus(200);
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->post('/api/v1/expenses/', $data);
$response->assertStatus(302);
}
public function testExpenseGet()

View File

@ -125,8 +125,15 @@ class InvoiceTest extends TestCase
])->post('/api/v1/invoices/', $invoice)
->assertStatus(200);
//test that the same request should produce a validation error due
//to duplicate number being used.
$arr = $response->json();
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->put('/api/v1/invoices/'.$arr['data']['id'], $invoice)
->assertStatus(200);
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,

View File

@ -104,6 +104,15 @@ class PaymentTest extends TestCase
$response->assertStatus(200);
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->put('/api/v1/payments/'.$this->encodePrimaryKey($Payment->id), $Payment->toArray());
$response->assertStatus(200);
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,

View File

@ -16,6 +16,7 @@ use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Facades\Session;
use Tests\MockAccountData;
use Tests\TestCase;
use Illuminate\Validation\ValidationException;
/**
* @test
@ -56,6 +57,7 @@ class ProjectApiTest extends TestCase
$data = [
'name' => $this->faker->firstName,
'client_id' => $this->client->hashed_id,
'number' => 'duplicate',
];
$response = $this->withHeaders([
@ -64,6 +66,26 @@ class ProjectApiTest extends TestCase
])->post('/api/v1/projects', $data);
$response->assertStatus(200);
$arr = $response->json();
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->put('/api/v1/projects/'.$arr['data']['id'], $data)->assertStatus(200);
try{
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->post('/api/v1/projects', $data);
}
catch(ValidationException $e){
$response->assertStatus(302);
}
}
public function testProjectPut()

View File

@ -86,7 +86,8 @@ class QuoteTest extends TestCase
$quote_update = [
'status_id' => Quote::STATUS_APPROVED,
// 'client_id' => $this->encodePrimaryKey($quote->client_id),
'client_id' => $this->encodePrimaryKey($this->quote->client_id),
'number' => 'Rando',
];
$this->assertNotNull($this->quote);
@ -98,6 +99,22 @@ class QuoteTest extends TestCase
$response->assertStatus(200);
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->put('/api/v1/quotes/'.$this->encodePrimaryKey($this->quote->id), $quote_update);
$response->assertStatus(200);
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->post('/api/v1/quotes/', $quote_update);
$response->assertStatus(302);
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,

View File

@ -117,6 +117,7 @@ class RecurringInvoiceTest extends TestCase
$RecurringInvoice_update = [
'status_id' => RecurringInvoice::STATUS_DRAFT,
'client_id' => $this->encodePrimaryKey($RecurringInvoice->client_id),
'number' => 'customnumber'
];
$this->assertNotNull($RecurringInvoice);
@ -127,6 +128,26 @@ class RecurringInvoiceTest extends TestCase
])->put('/api/v1/recurring_invoices/'.$this->encodePrimaryKey($RecurringInvoice->id), $RecurringInvoice_update)
->assertStatus(200);
$arr = $response->json();
$this->assertEquals('customnumber', $arr['data']['number']);
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->put('/api/v1/recurring_invoices/'.$this->encodePrimaryKey($RecurringInvoice->id), $RecurringInvoice_update)
->assertStatus(200);
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->post('/api/v1/recurring_invoices/', $RecurringInvoice_update)
->assertStatus(302);
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,

View File

@ -14,6 +14,7 @@ use App\Utils\Traits\MakesHash;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Facades\Session;
use Illuminate\Validation\ValidationException;
use Tests\MockAccountData;
use Tests\TestCase;
@ -44,6 +45,7 @@ class TaskApiTest extends TestCase
{
$data = [
'description' => $this->faker->firstName,
'number' => 'taskynumber'
];
$response = $this->withHeaders([
@ -54,6 +56,27 @@ class TaskApiTest extends TestCase
$arr = $response->json();
$response->assertStatus(200);
$this->assertEquals('taskynumber', $arr['data']['number']);
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->put('/api/v1/tasks/'.$arr['data']['id'], $data);
$response->assertStatus(200);
try{
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->post('/api/v1/tasks', $data);
$arr = $response->json();
}catch(ValidationException $e){
$response->assertStatus(302);
}
$this->assertNotEmpty($arr['data']['number']);
}

View File

@ -14,6 +14,7 @@ use App\Utils\Traits\MakesHash;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Facades\Session;
use Illuminate\Validation\ValidationException;
use Tests\MockAccountData;
use Tests\TestCase;
@ -59,6 +60,7 @@ class VendorApiTest extends TestCase
$data = [
'name' => $this->faker->firstName,
'id_number' => 'Coolio',
'number' => 'wiggles'
];
$response = $this->withHeaders([
@ -67,6 +69,33 @@ class VendorApiTest extends TestCase
])->put('/api/v1/vendors/'.$this->encodePrimaryKey($this->vendor->id), $data);
$response->assertStatus(200);
$arr = $response->json();
$this->assertEquals('Coolio', $arr['data']['id_number']);
$this->assertEquals('wiggles', $arr['data']['number']);
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->put('/api/v1/vendors/'.$this->encodePrimaryKey($this->vendor->id), $data);
$response->assertStatus(200);
try{
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->post('/api/v1/vendors/', $data);
}catch(ValidationException $e){
$response->assertStatus(302);
}
}
public function testVendorGet()