mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-08-11 15:34:21 -04:00
commit
dcc4b13524
@ -1 +1 @@
|
||||
5.3.32
|
||||
5.3.33
|
@ -3,6 +3,7 @@
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\Company;
|
||||
use App\Utils\Ninja;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
@ -42,6 +43,10 @@ class S3Cleanup extends Command
|
||||
public function handle()
|
||||
{
|
||||
|
||||
|
||||
if(!Ninja::isHosted())
|
||||
return;
|
||||
|
||||
$c1 = Company::on('db-ninja-01')->pluck('company_key');
|
||||
$c2 = Company::on('db-ninja-02')->pluck('company_key');
|
||||
|
||||
|
@ -52,8 +52,6 @@ class Kernel extends ConsoleKernel
|
||||
|
||||
$schedule->job(new DiskCleanup)->daily()->withoutOverlapping();
|
||||
|
||||
$schedule->command('ninja:check-data --database=db-ninja-01')->daily()->withoutOverlapping();
|
||||
|
||||
$schedule->job(new ReminderJob)->hourly()->withoutOverlapping();
|
||||
|
||||
$schedule->job(new CompanySizeCheck)->daily()->withoutOverlapping();
|
||||
@ -84,7 +82,8 @@ class Kernel extends ConsoleKernel
|
||||
|
||||
$schedule->job(new AdjustEmailQuota)->dailyAt('23:00')->withoutOverlapping();
|
||||
$schedule->job(new SendFailedEmails)->daily()->withoutOverlapping();
|
||||
$schedule->command('ninja:check-data --database=db-ninja-02')->dailyAt('00:15')->withoutOverlapping();
|
||||
$schedule->command('ninja:check-data --database=db-ninja-01')->daily()->withoutOverlapping();
|
||||
$schedule->command('ninja:check-data --database=db-ninja-02')->dailyAt('00:05')->withoutOverlapping();
|
||||
$schedule->command('ninja:s3-cleanup')->dailyAt('23:15')->withoutOverlapping();
|
||||
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ trait WithTypeHelpers
|
||||
*/
|
||||
public function isImage(): bool
|
||||
{
|
||||
if (in_array($this->type, ['png', 'svg', 'jpeg', 'jpg', 'tiff', 'gif'])) {
|
||||
if (in_array($this->type, ['png', 'jpeg', 'jpg', 'tiff', 'gif'])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -346,7 +346,8 @@ class BaseController extends Controller
|
||||
|
||||
},
|
||||
'company.subscriptions'=> function ($query) use($updated_at, $user) {
|
||||
$query->where('updated_at', '>=', $updated_at);
|
||||
// $query->where('updated_at', '>=', $updated_at);
|
||||
$query->whereNotNull('updated_at');
|
||||
|
||||
if(!$user->isAdmin())
|
||||
$query->where('subscriptions.user_id', $user->id);
|
||||
|
@ -18,14 +18,20 @@ use App\Libraries\MultiDB;
|
||||
use App\Models\Account;
|
||||
use App\Models\ClientContact;
|
||||
use App\Models\Company;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\RecurringInvoice;
|
||||
use App\Models\Subscription;
|
||||
use App\Utils\Ninja;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Contracts\Routing\ResponseFactory;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class NinjaPlanController extends Controller
|
||||
{
|
||||
use MakesHash;
|
||||
|
||||
public function index(string $contact_key, string $account_or_company_key)
|
||||
{
|
||||
@ -57,4 +63,82 @@ class NinjaPlanController extends Controller
|
||||
return redirect()->route('client.catchall');
|
||||
|
||||
}
|
||||
|
||||
public function plan()
|
||||
{
|
||||
//harvest the current plan
|
||||
$data = [];
|
||||
|
||||
if(MultiDB::findAndSetDbByAccountKey(Auth::guard('contact')->user()->client->custom_value2))
|
||||
{
|
||||
$account = Account::where('key', Auth::guard('contact')->user()->client->custom_value2)->first();
|
||||
|
||||
if($account)
|
||||
{
|
||||
|
||||
if(Carbon::parse($account->plan_expires)->lt(now())){
|
||||
//expired get the most recent invoice for payment
|
||||
|
||||
$late_invoice = Invoice::on('db-ninja-01')
|
||||
->where('company_id', Auth::guard('contact')->user()->company->id)
|
||||
->where('client_id', Auth::guard('contact')->user()->client->id)
|
||||
->where('status_id', Invoice::STATUS_SENT)
|
||||
->whereNotNull('subscription_id')
|
||||
->orderBy('id', 'DESC')
|
||||
->first();
|
||||
|
||||
//account status means user cannot perform upgrades until they pay their account.
|
||||
$data['late_invoice'] = $late_invoice;
|
||||
|
||||
}
|
||||
|
||||
$recurring_invoice = RecurringInvoice::on('db-ninja-01')
|
||||
->where('client_id', auth('contact')->user()->client->id)
|
||||
->where('company_id', Auth::guard('contact')->user()->company->id)
|
||||
->whereNotNull('subscription_id')
|
||||
->where('status_id', RecurringInvoice::STATUS_ACTIVE)
|
||||
->orderBy('id', 'desc')
|
||||
->first();
|
||||
|
||||
$monthly_plans = Subscription::on('db-ninja-01')
|
||||
->where('company_id', Auth::guard('contact')->user()->company->id)
|
||||
->where('group_id', 6)
|
||||
->orderBy('promo_price', 'ASC')
|
||||
->get();
|
||||
|
||||
$yearly_plans = Subscription::on('db-ninja-01')
|
||||
->where('company_id', Auth::guard('contact')->user()->company->id)
|
||||
->where('group_id', 31)
|
||||
->orderBy('promo_price', 'ASC')
|
||||
->get();
|
||||
|
||||
$monthly_plans = $monthly_plans->merge($yearly_plans);
|
||||
|
||||
$current_subscription_id = $recurring_invoice ? $this->encodePrimaryKey($recurring_invoice->subscription_id) : false;
|
||||
|
||||
//remove existing subscription
|
||||
if($current_subscription_id){
|
||||
|
||||
$monthly_plans = $monthly_plans->filter(function ($plan) use($current_subscription_id){
|
||||
return (string)$plan->hashed_id != (string)$current_subscription_id;
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
$data['account'] = $account;
|
||||
$data['client'] = Auth::guard('contact')->user()->client;
|
||||
$data['plans'] = $monthly_plans;
|
||||
$data['current_subscription_id'] = $current_subscription_id;
|
||||
$data['current_recurring_id'] = $recurring_invoice ? $recurring_invoice->hashed_id : false;
|
||||
|
||||
return $this->render('plan.index', $data);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
return redirect()->route('client.catchall');
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -524,6 +524,11 @@ class CreditController extends BaseController
|
||||
{
|
||||
/*If we are using bulk actions, we don't want to return anything */
|
||||
switch ($action) {
|
||||
case 'mark_paid':
|
||||
$credit->service()->markPaid()->save();
|
||||
return $this->itemResponse($credit);
|
||||
break;
|
||||
|
||||
case 'clone_to_credit':
|
||||
$credit = CloneCreditFactory::create($credit, auth()->user()->id);
|
||||
|
||||
|
67
app/Http/Controllers/FilterController.php
Normal file
67
app/Http/Controllers/FilterController.php
Normal file
@ -0,0 +1,67 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Foundation\Bus\DispatchesJobs;
|
||||
use Illuminate\Http\Response;
|
||||
|
||||
class FilterController extends BaseController
|
||||
{
|
||||
|
||||
private array $base_filters = ['archive', 'restore', 'delete'];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function index(Request $request, string $entity)
|
||||
{
|
||||
|
||||
$entity_filters = [];
|
||||
|
||||
switch ($entity) {
|
||||
|
||||
case 'invoice':
|
||||
$entity_filters = ['bulk_download', 'mark_paid', 'mark_sent', 'download', 'cancel', 'email'];
|
||||
break;
|
||||
|
||||
case 'quote':
|
||||
$entity_filters = ['bulk_download', 'convert', 'convert_to_invoice', 'download', 'approve', 'email', 'mark_sent'];
|
||||
break;
|
||||
|
||||
case 'credit':
|
||||
$entity_filters = ['bulk_download', 'download', 'email', 'mark_sent'];
|
||||
break;
|
||||
|
||||
case 'payment':
|
||||
$entity_filters = ['bulk_download', 'download', 'email', 'email_receipt'];
|
||||
break;
|
||||
|
||||
case 'recurring_invoice':
|
||||
$entity_filters = ['bulk_download', 'start', 'stop', 'email'];
|
||||
break;
|
||||
|
||||
|
||||
}
|
||||
|
||||
return response()->json( array_merge($this->base_filters, $entity_filters), 200);
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -63,29 +63,12 @@ class HostedMigrationController extends Controller
|
||||
MultiDB::findAndSetDbByCompanyKey($input['account_key']);
|
||||
|
||||
$company = Company::with('account')->where('company_key', $input['account_key'])->first();
|
||||
$account = $company->account;
|
||||
$client_id = false;
|
||||
|
||||
if($contact = ClientContact::on('db-ninja-01')->where(['email' => $input['email'], 'company_id' => config('ninja.ninja_default_company_id')])->first()){
|
||||
$client_id = $contact->client_id;
|
||||
}
|
||||
else if($client = Client::on('db-ninja-01')->where(['custom_value2' => $account->key, 'company_id' => config('ninja.ninja_default_company_id')])->first()){
|
||||
$client_id = $client->id;
|
||||
}
|
||||
|
||||
//get ninja client_id;
|
||||
|
||||
if(strlen($input['gateway_reference']) >1 && $client_id){
|
||||
|
||||
Artisan::call('ninja:add-token', [
|
||||
'--customer' => $input['gateway_reference'], '--client_id' => 1
|
||||
]);
|
||||
|
||||
}
|
||||
|
||||
$forward_url = $company->domain();
|
||||
|
||||
return response()->json(['forward_url' => $forward_url], 200);
|
||||
$billing_transferred = \Modules\Admin\Jobs\Account\TransferAccountPlan::dispatchNow($input);
|
||||
|
||||
return response()->json(['forward_url' => $forward_url, 'billing_transferred' => $billing_transferred], 200);
|
||||
}
|
||||
|
||||
}
|
@ -345,14 +345,6 @@ class BillingPortalPurchase extends Component
|
||||
'quantity' => $this->quantity,
|
||||
];
|
||||
|
||||
$this->invoice = $this->subscription
|
||||
->service()
|
||||
->createInvoice($data)
|
||||
->service()
|
||||
->markSent()
|
||||
->fillDefaults()
|
||||
->save();
|
||||
|
||||
$is_eligible = $this->subscription->service()->isEligible($this->contact);
|
||||
|
||||
if (is_array($is_eligible) && $is_eligible['message'] != 'Success') {
|
||||
@ -363,6 +355,14 @@ class BillingPortalPurchase extends Component
|
||||
return;
|
||||
}
|
||||
|
||||
$this->invoice = $this->subscription
|
||||
->service()
|
||||
->createInvoice($data)
|
||||
->service()
|
||||
->markSent()
|
||||
->fillDefaults()
|
||||
->save();
|
||||
|
||||
Cache::put($this->hash, [
|
||||
'subscription_id' => $this->subscription->id,
|
||||
'email' => $this->email ?? $this->contact->email,
|
||||
|
@ -60,8 +60,8 @@ class RequiredClientInfo extends Component
|
||||
|
||||
'contact_first_name' => 'first_name',
|
||||
'contact_last_name' => 'last_name',
|
||||
'contact_email' => 'email',
|
||||
'contact_phone' => 'phone',
|
||||
// 'contact_email' => 'email',
|
||||
// 'contact_phone' => 'phone',
|
||||
];
|
||||
|
||||
public $show_form = false;
|
||||
@ -141,7 +141,7 @@ class RequiredClientInfo extends Component
|
||||
$_field = $this->mappings[$field['name']];
|
||||
|
||||
if (Str::startsWith($field['name'], 'client_')) {
|
||||
if (empty($this->contact->client->{$_field}) || is_null($this->contact->client->{$_field}) || $this->contact->client->{$_field} = 840) {
|
||||
if (empty($this->contact->client->{$_field}) || is_null($this->contact->client->{$_field}) || $this->contact->client->{$_field} == 840) {
|
||||
$this->show_form = true;
|
||||
} else {
|
||||
$this->fields[$index]['filled'] = true;
|
||||
@ -149,7 +149,7 @@ class RequiredClientInfo extends Component
|
||||
}
|
||||
|
||||
if (Str::startsWith($field['name'], 'contact_')) {
|
||||
if ((empty($this->contact->{$_field}) || is_null($this->contact->{$_field})) || $this->contact->client->{$_field} = 840) {
|
||||
if ((empty($this->contact->{$_field}) || is_null($this->contact->{$_field})) || $this->contact->client->{$_field} == 840) {
|
||||
$this->show_form = true;
|
||||
} else {
|
||||
$this->fields[$index]['filled'] = true;
|
||||
|
@ -38,6 +38,7 @@ class SubscriptionRecurringInvoicesTable extends Component
|
||||
->where('client_id', auth('contact')->user()->client->id)
|
||||
->where('company_id', $this->company->id)
|
||||
->whereNotNull('subscription_id')
|
||||
->where('is_deleted', false)
|
||||
->where('status_id', RecurringInvoice::STATUS_ACTIVE)
|
||||
->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc')
|
||||
->withTrashed()
|
||||
|
@ -32,7 +32,7 @@ class ContactAccount
|
||||
if(!Ninja::isHosted()) {
|
||||
|
||||
$account_id = Account::first()->id;
|
||||
$request->attributes->add(['account_id' => $account_id]);
|
||||
$request->request->add(['account_id' => $account_id]);
|
||||
|
||||
}
|
||||
|
||||
|
@ -49,7 +49,7 @@ class SetDomainNameDb
|
||||
];
|
||||
|
||||
if($company = MultiDB::findAndSetDbByDomain($query)){
|
||||
$request->attributes->add(['account_id' => $company->account_id]);
|
||||
$request->request->add(['account_id' => $company->account_id]);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -71,7 +71,7 @@ class SetDomainNameDb
|
||||
];
|
||||
|
||||
if($company = MultiDB::findAndSetDbByDomain($query)){
|
||||
$request->attributes->add(['account_id' => $company->account_id]);
|
||||
$request->request->add(['account_id' => $company->account_id]);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -42,10 +42,10 @@ class StoreClientRequest extends Request
|
||||
$documents = count($this->input('documents'));
|
||||
|
||||
foreach (range(0, $documents) as $index) {
|
||||
$rules['documents.'.$index] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
$rules['documents.'.$index] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
}
|
||||
} elseif ($this->input('documents')) {
|
||||
$rules['documents'] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
$rules['documents'] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
}
|
||||
|
||||
if (isset($this->number)) {
|
||||
|
@ -41,10 +41,10 @@ class UpdateClientRequest extends Request
|
||||
$documents = count($this->input('documents'));
|
||||
|
||||
foreach (range(0, $documents) as $index) {
|
||||
$rules['documents.'.$index] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
$rules['documents.'.$index] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
}
|
||||
} elseif ($this->input('documents')) {
|
||||
$rules['documents'] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
$rules['documents'] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
}
|
||||
|
||||
$rules['company_logo'] = 'mimes:jpeg,jpg,png,gif|max:10000';
|
||||
|
@ -31,7 +31,7 @@ class UploadClientRequest extends Request
|
||||
$rules = [];
|
||||
|
||||
if($this->input('documents'))
|
||||
$rules['documents'] = 'file|mimes:html,csv,png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
|
||||
$rules['documents'] = 'file|mimes:csv,png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
|
||||
|
||||
return $rules;
|
||||
|
||||
|
@ -29,7 +29,7 @@ class StoreDocumentRequest extends Request
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'file' => 'required|max:10000|mimes:png,svg,jpeg,gif,jpg,bmp,txt,doc,docx,xls,xlsx,pdf',
|
||||
'file' => 'required|max:10000|mimes:png,jpeg,gif,jpg,bmp,txt,doc,docx,xls,xlsx,pdf',
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -32,7 +32,7 @@ class UpdateClientRequest extends Request
|
||||
{
|
||||
return [
|
||||
'name' => 'sometimes|required',
|
||||
'file' => 'sometimes|nullable|max:100000|mimes:png,svg,jpeg,gif,jpg,bmp',
|
||||
'file' => 'sometimes|nullable|max:100000|mimes:png,jpeg,gif,jpg,bmp',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ class StoreUploadRequest extends FormRequest
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'file' => ['file', 'mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000'],
|
||||
'file' => ['file', 'mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000'],
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -31,7 +31,7 @@ class UploadCompanyRequest extends Request
|
||||
$rules = [];
|
||||
|
||||
if($this->input('documents'))
|
||||
$rules['documents'] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
$rules['documents'] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
|
||||
return $rules;
|
||||
|
||||
|
@ -46,16 +46,17 @@ class StoreCreditRequest extends Request
|
||||
$documents = count($this->input('documents'));
|
||||
|
||||
foreach (range(0, $documents) as $index) {
|
||||
$rules['documents.'.$index] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
$rules['documents.'.$index] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
}
|
||||
} elseif ($this->input('documents')) {
|
||||
$rules['documents'] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
$rules['documents'] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
}
|
||||
|
||||
$rules['client_id'] = 'required|exists:clients,id,company_id,'.auth()->user()->company()->id;
|
||||
|
||||
// $rules['number'] = new UniqueCreditNumberRule($this->all());
|
||||
$rules['number'] = ['nullable', Rule::unique('credits')->where('company_id', auth()->user()->company()->id)];
|
||||
$rules['discount'] = 'sometimes|numeric';
|
||||
|
||||
|
||||
|
||||
|
@ -47,16 +47,17 @@ class UpdateCreditRequest extends Request
|
||||
$documents = count($this->input('documents'));
|
||||
|
||||
foreach (range(0, $documents) as $index) {
|
||||
$rules['documents.'.$index] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
$rules['documents.'.$index] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
}
|
||||
} elseif ($this->input('documents')) {
|
||||
$rules['documents'] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
$rules['documents'] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
}
|
||||
|
||||
if($this->number)
|
||||
$rules['number'] = Rule::unique('credits')->where('company_id', auth()->user()->company()->id)->ignore($this->credit->id);
|
||||
|
||||
$rules['line_items'] = 'array';
|
||||
$rules['discount'] = 'sometimes|numeric';
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ class UploadCreditRequest extends Request
|
||||
$rules = [];
|
||||
|
||||
if($this->input('documents'))
|
||||
$rules['documents'] = 'file|mimes:html,csv,png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
|
||||
$rules['documents'] = 'file|mimes:csv,png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
|
||||
|
||||
return $rules;
|
||||
|
||||
|
@ -31,7 +31,7 @@ class UploadExpenseRequest extends Request
|
||||
$rules = [];
|
||||
|
||||
if($this->input('documents'))
|
||||
$rules['documents'] = 'file|mimes:html,csv,png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
|
||||
$rules['documents'] = 'file|mimes:csv,png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
|
||||
|
||||
return $rules;
|
||||
|
||||
|
@ -31,7 +31,7 @@ class UploadGroupSettingRequest extends Request
|
||||
$rules = [];
|
||||
|
||||
if($this->input('documents'))
|
||||
$rules['documents'] = 'file|mimes:html,csv,png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
|
||||
$rules['documents'] = 'file|mimes:csv,png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
|
||||
|
||||
return $rules;
|
||||
|
||||
|
@ -41,10 +41,10 @@ class StoreInvoiceRequest extends Request
|
||||
$documents = count($this->input('documents'));
|
||||
|
||||
foreach (range(0, $documents) as $index) {
|
||||
$rules['documents.'.$index] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
$rules['documents.'.$index] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
}
|
||||
} elseif ($this->input('documents')) {
|
||||
$rules['documents'] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
$rules['documents'] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
}
|
||||
|
||||
$rules['client_id'] = 'bail|required|exists:clients,id,company_id,'.auth()->user()->company()->id;
|
||||
@ -56,6 +56,7 @@ class StoreInvoiceRequest extends Request
|
||||
$rules['project_id'] = ['bail', 'sometimes', new ValidProjectForClient($this->all())];
|
||||
|
||||
$rules['line_items'] = 'array';
|
||||
$rules['discount'] = 'sometimes|numeric';
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
@ -44,10 +44,10 @@ class UpdateInvoiceRequest extends Request
|
||||
$documents = count($this->input('documents'));
|
||||
|
||||
foreach (range(0, $documents) as $index) {
|
||||
$rules['documents.'.$index] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
$rules['documents.'.$index] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
}
|
||||
} elseif ($this->input('documents')) {
|
||||
$rules['documents'] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
$rules['documents'] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
}
|
||||
|
||||
$rules['id'] = new LockedInvoiceRule($this->invoice);
|
||||
@ -56,6 +56,7 @@ class UpdateInvoiceRequest extends Request
|
||||
$rules['number'] = Rule::unique('invoices')->where('company_id', auth()->user()->company()->id)->ignore($this->invoice->id);
|
||||
|
||||
$rules['line_items'] = 'array';
|
||||
$rules['discount'] = 'sometimes|numeric';
|
||||
|
||||
if($this->input('status_id') != Invoice::STATUS_DRAFT)
|
||||
$rules['balance'] = new InvoiceBalanceSanity($this->invoice, $this->all());
|
||||
|
@ -31,7 +31,7 @@ class UploadInvoiceRequest extends Request
|
||||
$rules = [];
|
||||
|
||||
if($this->input('documents'))
|
||||
$rules['documents'] = 'file|mimes:html,csv,png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
|
||||
$rules['documents'] = 'file|mimes:csv,png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
|
||||
|
||||
return $rules;
|
||||
|
||||
|
@ -107,10 +107,10 @@ class StorePaymentRequest extends Request
|
||||
$documents = count($this->input('documents'));
|
||||
|
||||
foreach (range(0, $documents) as $index) {
|
||||
$rules['documents.'.$index] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
$rules['documents.'.$index] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
}
|
||||
} elseif ($this->input('documents')) {
|
||||
$rules['documents'] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
$rules['documents'] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
}
|
||||
|
||||
return $rules;
|
||||
|
@ -38,7 +38,7 @@ class UpdatePaymentRequest extends Request
|
||||
$rules = [
|
||||
'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',
|
||||
'documents' => 'mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx',
|
||||
];
|
||||
|
||||
if($this->number)
|
||||
@ -48,10 +48,10 @@ class UpdatePaymentRequest extends Request
|
||||
$documents = count($this->input('documents'));
|
||||
|
||||
foreach (range(0, $documents) as $index) {
|
||||
$rules['documents.'.$index] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
$rules['documents.'.$index] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
}
|
||||
} elseif ($this->input('documents')) {
|
||||
$rules['documents'] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
$rules['documents'] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
}
|
||||
|
||||
return $rules;
|
||||
|
@ -31,7 +31,7 @@ class UploadPaymentRequest extends Request
|
||||
$rules = [];
|
||||
|
||||
if($this->input('documents'))
|
||||
$rules['documents'] = 'file|mimes:html,csv,png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
|
||||
$rules['documents'] = 'file|mimes:csv,png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
|
||||
|
||||
return $rules;
|
||||
|
||||
|
@ -32,10 +32,10 @@ class StoreProductRequest extends Request
|
||||
$documents = count($this->input('documents'));
|
||||
|
||||
foreach (range(0, $documents) as $index) {
|
||||
$rules['documents.'.$index] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
$rules['documents.'.$index] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
}
|
||||
} elseif ($this->input('documents')) {
|
||||
$rules['documents'] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
$rules['documents'] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
}
|
||||
|
||||
$rules['cost'] = 'numeric';
|
||||
|
@ -35,10 +35,10 @@ class UpdateProductRequest extends Request
|
||||
$documents = count($this->input('documents'));
|
||||
|
||||
foreach (range(0, $documents) as $index) {
|
||||
$rules['documents.'.$index] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
$rules['documents.'.$index] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
}
|
||||
} elseif ($this->input('documents')) {
|
||||
$rules['documents'] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
$rules['documents'] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
}
|
||||
|
||||
$rules['cost'] = 'numeric';
|
||||
|
@ -31,7 +31,7 @@ class UploadProductRequest extends Request
|
||||
$rules = [];
|
||||
|
||||
if($this->input('documents'))
|
||||
$rules['documents'] = 'file|mimes:html,csv,png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
|
||||
$rules['documents'] = 'file|mimes:csv,png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
|
||||
|
||||
return $rules;
|
||||
|
||||
|
@ -31,7 +31,7 @@ class UploadProjectRequest extends Request
|
||||
$rules = [];
|
||||
|
||||
if($this->input('documents'))
|
||||
$rules['documents'] = 'file|mimes:html,csv,png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
|
||||
$rules['documents'] = 'file|mimes:csv,png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
|
||||
|
||||
return $rules;
|
||||
|
||||
|
@ -43,13 +43,14 @@ class StoreQuoteRequest extends Request
|
||||
$documents = count($this->input('documents'));
|
||||
|
||||
foreach (range(0, $documents) as $index) {
|
||||
$rules['documents.'.$index] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
$rules['documents.'.$index] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
}
|
||||
} elseif ($this->input('documents')) {
|
||||
$rules['documents'] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
$rules['documents'] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
}
|
||||
|
||||
$rules['number'] = ['nullable',Rule::unique('quotes')->where('company_id', auth()->user()->company()->id)];
|
||||
$rules['discount'] = 'sometimes|numeric';
|
||||
|
||||
// $rules['number'] = new UniqueQuoteNumberRule($this->all());
|
||||
$rules['line_items'] = 'array';
|
||||
|
@ -41,16 +41,17 @@ class UpdateQuoteRequest extends Request
|
||||
$documents = count($this->input('documents'));
|
||||
|
||||
foreach (range(0, $documents) as $index) {
|
||||
$rules['documents.'.$index] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
$rules['documents.'.$index] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
}
|
||||
} elseif ($this->input('documents')) {
|
||||
$rules['documents'] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
$rules['documents'] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
}
|
||||
|
||||
if($this->number)
|
||||
$rules['number'] = Rule::unique('quotes')->where('company_id', auth()->user()->company()->id)->ignore($this->quote->id);
|
||||
|
||||
$rules['line_items'] = 'array';
|
||||
$rules['discount'] = 'sometimes|numeric';
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ class UploadQuoteRequest extends Request
|
||||
$rules = [];
|
||||
|
||||
if($this->input('documents'))
|
||||
$rules['documents'] = 'file|mimes:html,csv,png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
|
||||
$rules['documents'] = 'file|mimes:csv,png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
|
||||
|
||||
return $rules;
|
||||
|
||||
|
@ -31,7 +31,7 @@ class UploadRecurringExpenseRequest extends Request
|
||||
$rules = [];
|
||||
|
||||
if($this->input('documents'))
|
||||
$rules['documents'] = 'file|mimes:html,csv,png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
|
||||
$rules['documents'] = 'file|mimes:csv,png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
|
||||
|
||||
return $rules;
|
||||
|
||||
|
@ -42,11 +42,11 @@ class StoreRecurringInvoiceRequest extends Request
|
||||
$documents = count($this->input('documents'));
|
||||
|
||||
foreach (range(0, $documents) as $index) {
|
||||
$rules['documents.'.$index] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
$rules['documents.'.$index] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
}
|
||||
|
||||
} elseif ($this->input('documents')) {
|
||||
$rules['documents'] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
$rules['documents'] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
}
|
||||
|
||||
$rules['client_id'] = 'required|exists:clients,id,company_id,'.auth()->user()->company()->id;
|
||||
|
@ -42,10 +42,10 @@ class UpdateRecurringInvoiceRequest extends Request
|
||||
$documents = count($this->input('documents'));
|
||||
|
||||
foreach (range(0, $documents) as $index) {
|
||||
$rules['documents.'.$index] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
$rules['documents.'.$index] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
}
|
||||
} elseif ($this->input('documents')) {
|
||||
$rules['documents'] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
$rules['documents'] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
}
|
||||
|
||||
if($this->number)
|
||||
|
@ -31,7 +31,7 @@ class UploadRecurringInvoiceRequest extends Request
|
||||
$rules = [];
|
||||
|
||||
if($this->input('documents'))
|
||||
$rules['documents'] = 'file|mimes:html,csv,png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
|
||||
$rules['documents'] = 'file|mimes:csv,png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
|
||||
|
||||
return $rules;
|
||||
|
||||
|
@ -42,11 +42,11 @@ class StoreRecurringQuoteRequest extends Request
|
||||
$documents = count($this->input('documents'));
|
||||
|
||||
foreach (range(0, $documents) as $index) {
|
||||
$rules['documents.'.$index] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
$rules['documents.'.$index] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
}
|
||||
|
||||
} elseif ($this->input('documents')) {
|
||||
$rules['documents'] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
$rules['documents'] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
}
|
||||
|
||||
$rules['client_id'] = 'required|exists:clients,id,company_id,'.auth()->user()->company()->id;
|
||||
|
@ -42,10 +42,10 @@ class UpdateRecurringQuoteRequest extends Request
|
||||
$documents = count($this->input('documents'));
|
||||
|
||||
foreach (range(0, $documents) as $index) {
|
||||
$rules['documents.'.$index] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
$rules['documents.'.$index] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
}
|
||||
} elseif ($this->input('documents')) {
|
||||
$rules['documents'] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
$rules['documents'] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
}
|
||||
|
||||
if($this->number)
|
||||
|
@ -31,7 +31,7 @@ class UploadRecurringQuoteRequest extends Request
|
||||
$rules = [];
|
||||
|
||||
if($this->input('documents'))
|
||||
$rules['documents'] = 'file|mimes:html,csv,png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
|
||||
$rules['documents'] = 'file|mimes:csv,png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
|
||||
|
||||
return $rules;
|
||||
|
||||
|
@ -43,10 +43,10 @@ class StoreShopClientRequest extends Request
|
||||
$documents = count($this->input('documents'));
|
||||
|
||||
foreach (range(0, $documents) as $index) {
|
||||
$rules['documents.'.$index] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
$rules['documents.'.$index] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
}
|
||||
} elseif ($this->input('documents')) {
|
||||
$rules['documents'] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
$rules['documents'] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
}
|
||||
|
||||
/* Ensure we have a client name, and that all emails are unique*/
|
||||
|
@ -42,10 +42,10 @@ class StoreShopInvoiceRequest extends Request
|
||||
$documents = count($this->input('documents'));
|
||||
|
||||
foreach (range(0, $documents) as $index) {
|
||||
$rules['documents.'.$index] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
$rules['documents.'.$index] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
}
|
||||
} elseif ($this->input('documents')) {
|
||||
$rules['documents'] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
$rules['documents'] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
}
|
||||
|
||||
$rules['client_id'] = 'required|exists:clients,id,company_id,'.$this->company->id;
|
||||
|
@ -31,7 +31,7 @@ class UploadTaskRequest extends Request
|
||||
$rules = [];
|
||||
|
||||
if($this->input('documents'))
|
||||
$rules['documents'] = 'file|mimes:html,csv,png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
|
||||
$rules['documents'] = 'file|mimes:csv,png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
|
||||
|
||||
return $rules;
|
||||
|
||||
|
@ -31,7 +31,7 @@ class UploadVendorRequest extends Request
|
||||
$rules = [];
|
||||
|
||||
if($this->input('documents'))
|
||||
$rules['documents'] = 'file|mimes:html,csv,png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
|
||||
$rules['documents'] = 'file|mimes:csv,png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
|
||||
|
||||
return $rules;
|
||||
|
||||
|
@ -121,6 +121,10 @@ class PortalComposer
|
||||
|
||||
$data[] = ['title' => ctrans('texts.statement'), 'url' => 'client.statement', 'icon' => 'activity'];
|
||||
|
||||
if(Ninja::isHosted() && auth('contact')->user()->company->id == config('ninja.ninja_default_company_id'))
|
||||
$data[] = ['title' => ctrans('texts.plan'), 'url' => 'client.plan', 'icon' => 'credit-card'];
|
||||
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
@ -188,9 +188,9 @@ class CreateRawPdf implements ShouldQueue
|
||||
nlog(print_r($e->getMessage(), 1));
|
||||
}
|
||||
|
||||
if (config('ninja.log_pdf_html')) {
|
||||
// if (config('ninja.log_pdf_html')) {
|
||||
info($maker->getCompiledHTML());
|
||||
}
|
||||
// }
|
||||
|
||||
if ($pdf)
|
||||
return $pdf;
|
||||
|
@ -42,8 +42,5 @@ class DiskCleanup implements ShouldQueue
|
||||
$files = Storage::allFiles(config('filesystems.default'), 'backups/');
|
||||
Storage::delete($files);
|
||||
|
||||
$files = Storage::allFiles('public', 'backups/');
|
||||
Storage::delete($files);
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -42,9 +42,6 @@ class Document extends BaseModel
|
||||
'ai' => [
|
||||
'mime' => 'application/postscript',
|
||||
],
|
||||
'svg' => [
|
||||
'mime' => 'image/svg+xml',
|
||||
],
|
||||
'jpeg' => [
|
||||
'mime' => 'image/jpeg',
|
||||
],
|
||||
|
@ -136,7 +136,10 @@ class CreditCard
|
||||
return $this->processSuccessfulPayment($result);
|
||||
}
|
||||
|
||||
return $this->processUnsuccessfulPayment($result);
|
||||
$error = $result ?: 'Undefined gateway error';
|
||||
|
||||
return $this->processUnsuccessfulPayment($error);
|
||||
|
||||
}
|
||||
|
||||
private function getPaymentToken(array $data, $customerId): ?string
|
||||
|
@ -16,6 +16,7 @@ use App\Http\Requests\ClientPortal\Payments\PaymentResponseRequest;
|
||||
use App\Http\Requests\Gateways\Mollie\Mollie3dsRequest;
|
||||
use App\Http\Requests\Payments\PaymentWebhookRequest;
|
||||
use App\Jobs\Util\SystemLogger;
|
||||
use App\Models\Client;
|
||||
use App\Models\ClientGatewayToken;
|
||||
use App\Models\GatewayType;
|
||||
use App\Models\Invoice;
|
||||
@ -175,7 +176,7 @@ class MolliePaymentDriver extends BaseDriver
|
||||
SystemLog::EVENT_GATEWAY_FAILURE,
|
||||
SystemLog::TYPE_MOLLIE,
|
||||
$this->client,
|
||||
$this->client->companyk
|
||||
$this->client->company
|
||||
);
|
||||
|
||||
nlog($e->getMessage());
|
||||
@ -238,7 +239,8 @@ class MolliePaymentDriver extends BaseDriver
|
||||
SystemLog::CATEGORY_GATEWAY_RESPONSE,
|
||||
SystemLog::EVENT_GATEWAY_SUCCESS,
|
||||
SystemLog::TYPE_MOLLIE,
|
||||
$this->client
|
||||
$this->client,
|
||||
$this->client->company
|
||||
);
|
||||
|
||||
return $payment;
|
||||
@ -258,7 +260,8 @@ class MolliePaymentDriver extends BaseDriver
|
||||
SystemLog::CATEGORY_GATEWAY_RESPONSE,
|
||||
SystemLog::EVENT_GATEWAY_FAILURE,
|
||||
SystemLog::TYPE_CHECKOUT,
|
||||
$this->client
|
||||
$this->client,
|
||||
$this->client->company
|
||||
);
|
||||
|
||||
return false;
|
||||
@ -303,12 +306,42 @@ class MolliePaymentDriver extends BaseDriver
|
||||
|
||||
try {
|
||||
$payment = $this->gateway->payments->get($request->id);
|
||||
$record = Payment::withTrashed()->where('transaction_reference', $request->id)->first();
|
||||
|
||||
$record = Payment::withTrashed()->where('transaction_reference', $request->id)->firstOrFail();
|
||||
$record->status_id = $codes[$payment->status];
|
||||
$record->save();
|
||||
if($record){
|
||||
$client = $record->client;
|
||||
}
|
||||
else{
|
||||
nlog("mollie webhook");
|
||||
nlog($payment);
|
||||
|
||||
$client = Client::withTrashed()->find($this->decodePrimaryKey($payment->metadata->client_id));
|
||||
}
|
||||
|
||||
$message = [
|
||||
'server_response' => $payment,
|
||||
'data' => $request->all(),
|
||||
];
|
||||
|
||||
$response = SystemLog::EVENT_GATEWAY_FAILURE;
|
||||
|
||||
if($record){
|
||||
$record->status_id = $codes[$payment->status];
|
||||
$record->save();
|
||||
$response = SystemLog::EVENT_GATEWAY_SUCCESS;
|
||||
}
|
||||
|
||||
SystemLogger::dispatch(
|
||||
$message,
|
||||
SystemLog::CATEGORY_GATEWAY_RESPONSE,
|
||||
$response,
|
||||
SystemLog::TYPE_MOLLIE,
|
||||
$client,
|
||||
$client->company
|
||||
);
|
||||
|
||||
return response()->json([], 200);
|
||||
|
||||
} catch (ApiException $e) {
|
||||
return response()->json(['message' => $e->getMessage(), 'gatewayStatusCode' => $e->getCode()], 500);
|
||||
}
|
||||
|
@ -231,12 +231,24 @@ class ACH
|
||||
$this->stripe->payment_hash->data = array_merge((array)$this->stripe->payment_hash->data, $state);
|
||||
$this->stripe->payment_hash->save();
|
||||
|
||||
$amount = array_sum(array_column($this->stripe->payment_hash->invoices(), 'amount')) + $this->stripe->payment_hash->fee_total;
|
||||
$invoice = Invoice::whereIn('id', $this->transformKeys(array_column($this->stripe->payment_hash->invoices(), 'invoice_id')))
|
||||
->withTrashed()
|
||||
->first();
|
||||
|
||||
if ($invoice) {
|
||||
$description = "Invoice {$invoice->number} for {$amount} for client {$this->stripe->client->present()->name()}";
|
||||
} else {
|
||||
$description = "Payment with no invoice for amount {$amount} for client {$this->stripe->client->present()->name()}";
|
||||
}
|
||||
|
||||
try {
|
||||
$state['charge'] = \Stripe\Charge::create([
|
||||
'amount' => $state['amount'],
|
||||
'currency' => $state['currency'],
|
||||
'customer' => $state['customer'],
|
||||
'source' => $state['source'],
|
||||
'description' => $description,
|
||||
], $this->stripe->stripe_connect_auth);
|
||||
|
||||
$state = array_merge($state, $request->all());
|
||||
|
@ -208,7 +208,7 @@ class WePayPaymentDriver extends BaseDriver
|
||||
|
||||
return 'Processed successfully';
|
||||
} elseif ($objectType == 'account') {
|
||||
if ($accountId !== $objectId) {
|
||||
if ($accountId != $objectId) {
|
||||
throw new \Exception('Unknown account ' . $accountId . ' does not equal '.$objectId);
|
||||
}
|
||||
|
||||
|
@ -176,7 +176,7 @@ class BaseRepository
|
||||
if(array_key_exists('client_id', $data))
|
||||
$model->client_id = $data['client_id'];
|
||||
|
||||
$client = Client::where('id', $model->client_id)->withTrashed()->first();
|
||||
$client = Client::where('id', $model->client_id)->withTrashed()->firstOrFail();
|
||||
|
||||
$state = [];
|
||||
|
||||
|
@ -185,13 +185,13 @@ class PaymentRepository extends BaseRepository {
|
||||
* @param $payment
|
||||
* @return
|
||||
*/
|
||||
private function processExchangeRates($data, $payment)
|
||||
public function processExchangeRates($data, $payment)
|
||||
{
|
||||
|
||||
if(array_key_exists('exchange_rate', $data) && isset($data['exchange_rate']))
|
||||
return $payment;
|
||||
|
||||
$client = Client::find($data['client_id']);
|
||||
$client = Client::withTrashed()->find($data['client_id']);
|
||||
|
||||
$client_currency = $client->getSetting('currency_id');
|
||||
$company_currency = $client->company->settings->currency_id;
|
||||
|
@ -11,8 +11,13 @@
|
||||
|
||||
namespace App\Services\Credit;
|
||||
|
||||
use App\Factory\PaymentFactory;
|
||||
use App\Jobs\Util\UnlinkFile;
|
||||
use App\Models\Credit;
|
||||
use App\Models\Payment;
|
||||
use App\Models\PaymentType;
|
||||
use App\Repositories\CreditRepository;
|
||||
use App\Repositories\PaymentRepository;
|
||||
use App\Services\Credit\CreateInvitations;
|
||||
use App\Services\Credit\TriggeredActions;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
@ -79,6 +84,61 @@ class CreditService
|
||||
return $this;
|
||||
}
|
||||
|
||||
/*
|
||||
For euro users - we mark a credit as paid when
|
||||
we need to document a refund of sorts.
|
||||
|
||||
Criteria: Credit must be a negative value
|
||||
A negative payment for the balance will be generated
|
||||
This amount will be reduced from the clients paid to date.
|
||||
|
||||
*/
|
||||
public function markPaid()
|
||||
{
|
||||
if($this->credit->balance > 0)
|
||||
return $this;
|
||||
|
||||
$payment_repo = new PaymentRepository(new CreditRepository());
|
||||
|
||||
//set credit balance to zero
|
||||
$adjustment = $this->credit->balance;
|
||||
|
||||
$this->updateBalance($adjustment)
|
||||
->updatePaidToDate($adjustment)
|
||||
->save();
|
||||
|
||||
//create a negative payment of total $this->credit->balance
|
||||
$payment = PaymentFactory::create($this->credit->company_id, $this->credit->user_id);
|
||||
$payment->client_id = $this->credit->client_id;
|
||||
$payment->amount = $adjustment;
|
||||
$payment->applied = $adjustment;
|
||||
$payment->refunded = 0;
|
||||
$payment->status_id = Payment::STATUS_COMPLETED;
|
||||
$payment->type_id = PaymentType::CREDIT;
|
||||
$payment->is_manual = true;
|
||||
$payment->date = now();
|
||||
|
||||
$payment->saveQuietly();
|
||||
$payment->number = $payment->client->getNextPaymentNumber($payment->client, $payment);
|
||||
$payment = $payment_repo->processExchangeRates(['client_id' => $this->credit->client_id], $payment);
|
||||
$payment->saveQuietly();
|
||||
|
||||
$payment
|
||||
->credits()
|
||||
->attach($this->credit->id, ['amount' => $adjustment]);
|
||||
|
||||
//reduce client paid_to_date by $this->credit->balance amount
|
||||
$this->credit
|
||||
->client
|
||||
->service()
|
||||
->updatePaidToDate($adjustment)
|
||||
->save();
|
||||
|
||||
event('eloquent.created: App\Models\Payment', $payment);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function markSent()
|
||||
{
|
||||
$this->credit = (new MarkSent($this->credit->client, $this->credit))->run();
|
||||
|
@ -130,7 +130,7 @@ class AutoBillInvoice extends AbstractService
|
||||
info("Auto Bill payment captured for ".$this->invoice->number);
|
||||
}
|
||||
|
||||
return $this->invoice;
|
||||
return $this->invoice->fresh();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -33,7 +33,7 @@ class CreateInvitations extends AbstractService
|
||||
public function run()
|
||||
{
|
||||
|
||||
$contacts = $this->invoice->client->contacts;
|
||||
$contacts = $this->invoice->client->contacts()->where('send_email', true)->get();
|
||||
|
||||
if($contacts->count() == 0){
|
||||
$this->createBlankContact();
|
||||
|
@ -139,8 +139,13 @@ class DeletePayment
|
||||
if ($this->payment->credits()->exists()) {
|
||||
$this->payment->credits()->each(function ($paymentable_credit) {
|
||||
|
||||
$multiplier = 1;
|
||||
|
||||
if($paymentable_credit->pivot->amount < 0)
|
||||
$multiplier = -1;
|
||||
|
||||
$paymentable_credit->service()
|
||||
->updateBalance($paymentable_credit->pivot->amount)
|
||||
->updateBalance($paymentable_credit->pivot->amount*$multiplier)
|
||||
->updatePaidToDate($paymentable_credit->pivot->amount*-1)
|
||||
->setStatus(Credit::STATUS_SENT)
|
||||
->save();
|
||||
|
@ -18,6 +18,7 @@ use App\Factory\InvoiceToRecurringInvoiceFactory;
|
||||
use App\Factory\RecurringInvoiceFactory;
|
||||
use App\Jobs\Util\SubscriptionWebhookHandler;
|
||||
use App\Jobs\Util\SystemLogger;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Client;
|
||||
use App\Models\ClientContact;
|
||||
use App\Models\Credit;
|
||||
@ -240,10 +241,10 @@ class SubscriptionService
|
||||
elseif ($outstanding->count() > 1) {
|
||||
//user is changing plan mid frequency cycle
|
||||
//we cannot handle this if there are more than one invoice outstanding.
|
||||
return null;
|
||||
return $target->price;
|
||||
}
|
||||
|
||||
return null;
|
||||
return $target->price;
|
||||
|
||||
}
|
||||
|
||||
@ -439,7 +440,7 @@ class SubscriptionService
|
||||
$credit = false;
|
||||
|
||||
/* Only generate a credit if the previous invoice was paid in full. */
|
||||
if($last_invoice->balance == 0)
|
||||
if($last_invoice && $last_invoice->balance == 0)
|
||||
$credit = $this->createCredit($last_invoice, $target_subscription, $is_credit);
|
||||
|
||||
$new_recurring_invoice = $this->createNewRecurringInvoice($recurring_invoice);
|
||||
@ -717,8 +718,9 @@ class SubscriptionService
|
||||
*/
|
||||
public function convertInvoiceToRecurring($client_id) :RecurringInvoice
|
||||
{
|
||||
MultiDB::setDb($this->subscription->company->db);
|
||||
|
||||
$client = Client::find($client_id);
|
||||
$client = Client::withTrashed()->find($client_id);
|
||||
|
||||
$subscription_repo = new SubscriptionRepository();
|
||||
|
||||
|
@ -60,7 +60,7 @@ class UserTransformer extends EntityTransformer
|
||||
'oauth_provider_id' => (string) $user->oauth_provider_id,
|
||||
'last_confirmed_email_address' => (string) $user->last_confirmed_email_address ?: '',
|
||||
'google_2fa_secret' => (bool) $user->google_2fa_secret,
|
||||
'has_password' => (bool) $user->has_password,
|
||||
'has_password' => (bool) empty($user->password) ? false : true,
|
||||
'oauth_user_token' => empty($user->oauth_user_token) ? '' : '***',
|
||||
];
|
||||
}
|
||||
|
@ -98,8 +98,7 @@
|
||||
"laravel/dusk": "^6.15",
|
||||
"mockery/mockery": "^1.3.1",
|
||||
"nunomaduro/collision": "^5.0",
|
||||
"phpunit/phpunit": "^9.0",
|
||||
"vimeo/psalm": "^4.0"
|
||||
"phpunit/phpunit": "^9.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
|
1655
composer.lock
generated
1655
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@ -14,8 +14,8 @@ return [
|
||||
'require_https' => env('REQUIRE_HTTPS', true),
|
||||
'app_url' => rtrim(env('APP_URL', ''), '/'),
|
||||
'app_domain' => env('APP_DOMAIN', 'invoicing.co'),
|
||||
'app_version' => '5.3.32',
|
||||
'app_tag' => '5.3.32',
|
||||
'app_version' => '5.3.33',
|
||||
'app_tag' => '5.3.33',
|
||||
'minimum_client_version' => '5.0.16',
|
||||
'terms_version' => '1.0.1',
|
||||
'api_secret' => env('API_SECRET', ''),
|
||||
|
10431
package-lock.json
generated
10431
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
54
public/flutter_service_worker.js
vendored
54
public/flutter_service_worker.js
vendored
@ -3,38 +3,38 @@ const MANIFEST = 'flutter-app-manifest';
|
||||
const TEMP = 'flutter-temp-cache';
|
||||
const CACHE_NAME = 'flutter-app-cache';
|
||||
const RESOURCES = {
|
||||
"icons/Icon-512.png": "0f9aff01367f0a0c69773d25ca16ef35",
|
||||
"icons/Icon-192.png": "bb1cf5f6982006952211c7c8404ffbed",
|
||||
"favicon.png": "dca91c54388f52eded692718d5a98b8b",
|
||||
"/": "6310be19342ca9ed6920572bcc23151f",
|
||||
"manifest.json": "ef43d90e57aa7682d7e2cfba2f484a40",
|
||||
"favicon.ico": "51636d3a390451561744c42188ccd628",
|
||||
"manifest.json": "ef43d90e57aa7682d7e2cfba2f484a40",
|
||||
"version.json": "9c7b0edc83733da56c726678aacd9fd3",
|
||||
"icons/Icon-192.png": "bb1cf5f6982006952211c7c8404ffbed",
|
||||
"icons/Icon-512.png": "0f9aff01367f0a0c69773d25ca16ef35",
|
||||
"favicon.png": "dca91c54388f52eded692718d5a98b8b",
|
||||
"favicon.ico": "51636d3a390451561744c42188ccd628",
|
||||
"main.dart.js": "40a468819694baa11ae02390609712fc",
|
||||
"/": "38d8084156eb614c31d736ce8c9bd255",
|
||||
"assets/NOTICES": "5a96be85b952e4fcd3a6965546c85b7f",
|
||||
"assets/packages/material_design_icons_flutter/lib/fonts/materialdesignicons-webfont.ttf": "015400679694f1f51047e46da0e1dc98",
|
||||
"assets/AssetManifest.json": "38d9aea341601f3a5c6fa7b5a1216ea5",
|
||||
"assets/fonts/MaterialIcons-Regular.otf": "4e6447691c9509f7acdbf8a931a85ca1",
|
||||
"assets/NOTICES": "7610cf8f301427a1104669ea3f4074ac",
|
||||
"assets/packages/material_design_icons_flutter/lib/fonts/materialdesignicons-webfont.ttf": "174c02fc4609e8fc4389f5d21f16a296",
|
||||
"assets/FontManifest.json": "cf3c681641169319e61b61bd0277378f",
|
||||
"assets/assets/images/payment_types/paypal.png": "8e06c094c1871376dfea1da8088c29d1",
|
||||
"assets/assets/images/payment_types/mastercard.png": "6f6cdc29ee2e22e06b1ac029cb52ef71",
|
||||
"assets/assets/images/payment_types/carteblanche.png": "d936e11fa3884b8c9f1bd5c914be8629",
|
||||
"assets/assets/images/payment_types/solo.png": "2030c3ccaccf5d5e87916a62f5b084d6",
|
||||
"assets/assets/images/payment_types/other.png": "d936e11fa3884b8c9f1bd5c914be8629",
|
||||
"assets/assets/images/payment_types/discover.png": "6c0a386a00307f87db7bea366cca35f5",
|
||||
"assets/assets/images/payment_types/amex.png": "c49a4247984b3732a4af50a3390aa978",
|
||||
"assets/assets/images/payment_types/ach.png": "7433f0aff779dc98a649b7a2daf777cf",
|
||||
"assets/assets/images/payment_types/jcb.png": "07e0942d16c5592118b72e74f2f7198c",
|
||||
"assets/assets/images/payment_types/laser.png": "b4e6e93dd35517ac429301119ff05868",
|
||||
"assets/assets/images/payment_types/visa.png": "3ddc4a4d25c946e8ad7e6998f30fd4e3",
|
||||
"assets/assets/images/payment_types/dinerscard.png": "06d85186ba858c18ab7c9caa42c92024",
|
||||
"assets/assets/images/payment_types/switch.png": "4fa11c45327f5fdc20205821b2cfd9cc",
|
||||
"assets/assets/images/payment_types/maestro.png": "e533b92bfb50339fdbfa79e3dfe81f08",
|
||||
"assets/assets/images/payment_types/unionpay.png": "7002f52004e0ab8cc0b7450b0208ccb2",
|
||||
"assets/assets/images/logo_light.png": "e5f46d5a78e226e7a9553d4ca6f69219",
|
||||
"assets/assets/images/logo_dark.png": "a233ed1d4d0f7414bf97a9a10f11fb0a",
|
||||
"assets/assets/images/icon.png": "090f69e23311a4b6d851b3880ae52541",
|
||||
"assets/assets/images/google_logo.png": "0f118259ce403274f407f5e982e681c3",
|
||||
"assets/assets/images/logo_light.png": "e5f46d5a78e226e7a9553d4ca6f69219",
|
||||
"assets/AssetManifest.json": "38d9aea341601f3a5c6fa7b5a1216ea5",
|
||||
"main.dart.js": "d57b5bf95106fcc642bac2ab5bc52fc3"
|
||||
"assets/assets/images/payment_types/paypal.png": "8e06c094c1871376dfea1da8088c29d1",
|
||||
"assets/assets/images/payment_types/maestro.png": "e533b92bfb50339fdbfa79e3dfe81f08",
|
||||
"assets/assets/images/payment_types/solo.png": "2030c3ccaccf5d5e87916a62f5b084d6",
|
||||
"assets/assets/images/payment_types/dinerscard.png": "06d85186ba858c18ab7c9caa42c92024",
|
||||
"assets/assets/images/payment_types/other.png": "d936e11fa3884b8c9f1bd5c914be8629",
|
||||
"assets/assets/images/payment_types/unionpay.png": "7002f52004e0ab8cc0b7450b0208ccb2",
|
||||
"assets/assets/images/payment_types/discover.png": "6c0a386a00307f87db7bea366cca35f5",
|
||||
"assets/assets/images/payment_types/jcb.png": "07e0942d16c5592118b72e74f2f7198c",
|
||||
"assets/assets/images/payment_types/carteblanche.png": "d936e11fa3884b8c9f1bd5c914be8629",
|
||||
"assets/assets/images/payment_types/laser.png": "b4e6e93dd35517ac429301119ff05868",
|
||||
"assets/assets/images/payment_types/ach.png": "7433f0aff779dc98a649b7a2daf777cf",
|
||||
"assets/assets/images/payment_types/mastercard.png": "6f6cdc29ee2e22e06b1ac029cb52ef71",
|
||||
"assets/assets/images/payment_types/amex.png": "c49a4247984b3732a4af50a3390aa978",
|
||||
"assets/assets/images/payment_types/switch.png": "4fa11c45327f5fdc20205821b2cfd9cc",
|
||||
"assets/assets/images/payment_types/visa.png": "3ddc4a4d25c946e8ad7e6998f30fd4e3",
|
||||
"assets/assets/images/google_logo.png": "0f118259ce403274f407f5e982e681c3"
|
||||
};
|
||||
|
||||
// The application shell files that are downloaded before a service worker can
|
||||
|
1
public/images/welcome/email.svg
Normal file
1
public/images/welcome/email.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg version="1.2" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" overflow="visible" preserveAspectRatio="none" viewBox="0 0 35 28" width="35" height="28"><g transform="translate(0, 0)"><g transform="translate(-6.938893903907228e-17, 0) rotate(0)"><path d="M31.5,0h-28c-1.925,0 -3.4825,1.575 -3.4825,3.5l-0.0175,21c0,1.925 1.575,3.5 3.5,3.5h28c1.925,0 3.5,-1.575 3.5,-3.5v-21c0,-1.925 -1.575,-3.5 -3.5,-3.5zM31.5,7l-14,8.75l-14,-8.75v-3.5l14,8.75l14,-8.75z" style="stroke-width: 0; stroke-linecap: butt; stroke-linejoin: miter; fill: rgb(18, 18, 18);" vector-effect="non-scaling-stroke"/></g><defs><path id="path-1637627912051353" d="M31.5,0h-28c-1.925,0 -3.4825,1.575 -3.4825,3.5l-0.0175,21c0,1.925 1.575,3.5 3.5,3.5h28c1.925,0 3.5,-1.575 3.5,-3.5v-21c0,-1.925 -1.575,-3.5 -3.5,-3.5zM31.5,7l-14,8.75l-14,-8.75v-3.5l14,8.75l14,-8.75z" vector-effect="non-scaling-stroke"/></defs></g></svg>
|
After Width: | Height: | Size: 919 B |
BIN
public/images/welcome/gateways.png
Normal file
BIN
public/images/welcome/gateways.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 196 KiB |
BIN
public/images/welcome/money_logo.png
Normal file
BIN
public/images/welcome/money_logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
385024
public/main.dart.js
vendored
385024
public/main.dart.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
381652
public/main.foss.dart.js
vendored
381652
public/main.foss.dart.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
380590
public/main.html.dart.js
vendored
380590
public/main.html.dart.js
vendored
File diff suppressed because one or more lines are too long
294385
public/main.next.dart.js
vendored
294385
public/main.next.dart.js
vendored
File diff suppressed because one or more lines are too long
451126
public/main.profile.dart.js
vendored
451126
public/main.profile.dart.js
vendored
File diff suppressed because one or more lines are too long
@ -4340,6 +4340,7 @@ $LANG = array(
|
||||
'no_available_methods' => 'We can\'t find any credit cards on your device. <a href="https://invoiceninja.github.io/docs/payments#apple-pay-google-pay-microsoft-pay" target="_blank" class="underline">Read more about this.</a>',
|
||||
'gocardless_mandate_not_ready' => 'Payment mandate is not ready. Please try again later.',
|
||||
'payment_type_instant_bank_pay' => 'Instant Bank Pay',
|
||||
|
||||
);
|
||||
|
||||
return $LANG;
|
||||
|
@ -2,8 +2,8 @@
|
||||
@import url($font_url);
|
||||
|
||||
:root {
|
||||
--primary-color: #298aab;
|
||||
--secondary-color: #7081e0;
|
||||
--primary-color: $primary_color;
|
||||
--secondary-color: $secondary_color;
|
||||
}
|
||||
|
||||
body {
|
||||
@ -149,13 +149,13 @@
|
||||
|
||||
#footer, #footer-spacer {
|
||||
height: 220px;
|
||||
padding: 1rem 3rem;
|
||||
padding: 1rem 1.5rem;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.footer-content {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
gap: 10px;
|
||||
width: 100%;
|
||||
/* grid-template-columns: 1fr 1fr 1fr; */
|
||||
color: #fff4e9;
|
||||
@ -165,8 +165,8 @@
|
||||
|
||||
.footer-company-details-address-wrapper {
|
||||
display: flex;
|
||||
gap: 25px;
|
||||
margin-right: 150px;
|
||||
gap: 5px;
|
||||
margin-right: 60px;
|
||||
}
|
||||
|
||||
#company-address,
|
||||
@ -348,7 +348,7 @@ $entity_images
|
||||
|
||||
<div id="footer">
|
||||
<div class="footer-content">
|
||||
<div>
|
||||
<div style="width: 70%;">
|
||||
<p data-ref="total_table-footer">$entity_footer</p>
|
||||
|
||||
<script>
|
||||
|
126
resources/views/portal/ninja2020/plan/index.blade.php
Normal file
126
resources/views/portal/ninja2020/plan/index.blade.php
Normal file
@ -0,0 +1,126 @@
|
||||
@extends('portal.ninja2020.layout.app')
|
||||
@section('meta_title', ctrans('texts.account_management'))
|
||||
|
||||
@section('body')
|
||||
|
||||
<!-- This example requires Tailwind CSS v2.0+ -->
|
||||
<div class="bg-white shadow overflow-hidden sm:rounded-lg">
|
||||
<div class="px-4 py-5 sm:px-6">
|
||||
<h3 class="text-lg leading-6 font-medium text-gray-900">
|
||||
{{ ctrans('texts.plan_status') }}
|
||||
</h3>
|
||||
</div>
|
||||
<div class="border-t border-gray-200">
|
||||
<dl>
|
||||
<div class="bg-white px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
|
||||
<dt class="text-sm font-medium text-gray-500">
|
||||
{{ ctrans('texts.plan') }}
|
||||
</dt>
|
||||
<dd class="mt-1 text-sm text-gray-900 sm:mt-0 sm:col-span-2">
|
||||
{{ $account->plan ? ucfirst($account->plan) : 'Free' }}
|
||||
</dd>
|
||||
</div>
|
||||
@if($account->plan)
|
||||
|
||||
<div class="bg-white px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
|
||||
<dt class="text-sm font-medium text-gray-500">
|
||||
{{ ctrans('texts.expires') }}
|
||||
</dt>
|
||||
<dd class="mt-1 text-sm text-gray-900 sm:mt-0 sm:col-span-2">
|
||||
{{ $client->formatDate($account->plan_expires, $client->date_format()) }}
|
||||
</dd>
|
||||
</div>
|
||||
|
||||
@if($account->plan == 'enterprise')
|
||||
|
||||
<div class="bg-white px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
|
||||
<dt class="text-sm font-medium text-gray-500">
|
||||
{{ ctrans('texts.users')}}
|
||||
</dt>
|
||||
<dd class="mt-1 text-sm text-gray-900 sm:mt-0 sm:col-span-2">
|
||||
{{ $account->num_users }}
|
||||
</dd>
|
||||
</div>
|
||||
|
||||
@endif
|
||||
|
||||
@endif
|
||||
|
||||
@if($late_invoice)
|
||||
|
||||
<div class="px-4 py-5 sm:px-6">
|
||||
<h3 class="text-lg leading-6 font-medium text-gray-900">
|
||||
{{ ctrans('texts.invoice_status_id') }}
|
||||
</h3>
|
||||
<p class="mt-1 max-w-2xl text-sm text-gray-500">
|
||||
{{ ctrans('texts.past_due') }}
|
||||
</p>
|
||||
</div>
|
||||
<dl>
|
||||
<div class="bg-white px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
|
||||
<dt class="text-sm font-medium text-gray-500">
|
||||
{{ ctrans('texts.invoice') }}
|
||||
</dt>
|
||||
<dd class="mt-1 text-sm text-gray-900 sm:mt-0 sm:col-span-2">
|
||||
{{ $late_invoice->number }} - {{ \App\Utils\Number::formatMoney($late_invoice->balance, $client) }} <a class="button-link text-primary" href="/client/invoices/{{$late_invoice->hashed_id}}">{{ ctrans('texts.pay_now')}}</a>
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
|
||||
@else
|
||||
|
||||
<div class="bg-white px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
|
||||
<dt class="text-sm font-medium text-gray-500">
|
||||
{{ ctrans('texts.plan_change') }}
|
||||
</dt>
|
||||
<dd class="mt-1 text-sm text-gray-900 sm:mt-0 sm:col-span-2">
|
||||
<div>
|
||||
<select id="newPlan" class="pl-3 pr-10 py-2 text-base border-gray-300 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm rounded-md">
|
||||
<option value="">Select Plan</option>
|
||||
@foreach($plans as $plan)
|
||||
<option value="{{ $plan->hashed_id}}">{{ $plan->name }} {{ \App\Utils\Number::formatMoney($plan->promo_price, $client) }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
|
||||
@if($current_recurring_id)
|
||||
<button id="handlePlanChange" class="bg-transparent hover:bg-blue-500 text-blue-700 font-semibold hover:text-white py-2 px-4 border border-blue-500 hover:border-transparent rounded">
|
||||
{{ ctrans('texts.plan_change') }}
|
||||
</button>
|
||||
@else
|
||||
<button id="handleNewPlan" class="bg-transparent hover:bg-blue-500 text-blue-700 font-semibold hover:text-white py-2 px-4 border border-blue-500 hover:border-transparent rounded">
|
||||
{{ ctrans('texts.plan_upgrade') }}
|
||||
</button>
|
||||
@endif
|
||||
</dd>
|
||||
</div>
|
||||
|
||||
@endif
|
||||
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@endsection
|
||||
|
||||
@push('footer')
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
@if($current_recurring_id)
|
||||
document.getElementById('handlePlanChange').addEventListener('click', function() {
|
||||
|
||||
if(document.getElementById("newPlan").value.length > 1)
|
||||
location.href = 'https://invoiceninja.invoicing.co/client/subscriptions/{{ $current_recurring_id }}/plan_switch/' + document.getElementById("newPlan").value + '';
|
||||
|
||||
});
|
||||
@else
|
||||
document.getElementById('handleNewPlan').addEventListener('click', function() {
|
||||
|
||||
if(document.getElementById("newPlan").value.length > 1)
|
||||
location.href = 'https://invoiceninja.invoicing.co/client/subscriptions/' + document.getElementById("newPlan").value + '/purchase';
|
||||
|
||||
});
|
||||
@endif
|
||||
|
||||
</script>
|
||||
@endpush
|
@ -38,6 +38,8 @@ Route::group(['middleware' => ['api_db', 'token_auth', 'locale'], 'prefix' => 'a
|
||||
Route::put('clients/{client}/upload', 'ClientController@upload')->name('clients.upload');
|
||||
Route::post('clients/bulk', 'ClientController@bulk')->name('clients.bulk');
|
||||
|
||||
Route::post('filters/{entity}', 'FilterController@index')->name('filters');
|
||||
|
||||
Route::resource('client_gateway_tokens', 'ClientGatewayTokenController');
|
||||
|
||||
Route::post('connected_account', 'ConnectedAccountController@index');
|
||||
|
@ -31,6 +31,8 @@ Route::get('client/ninja/{contact_key}/{company_key}', 'ClientPortal\NinjaPlanCo
|
||||
Route::group(['middleware' => ['auth:contact', 'locale', 'check_client_existence','domain_db'], 'prefix' => 'client', 'as' => 'client.'], function () {
|
||||
Route::get('dashboard', 'ClientPortal\DashboardController@index')->name('dashboard'); // name = (dashboard. index / create / show / update / destroy / edit
|
||||
|
||||
Route::get('plan', 'ClientPortal\NinjaPlanController@plan')->name('plan'); // name = (dashboard. index / create / show / update / destroy / edit
|
||||
|
||||
Route::get('invoices', 'ClientPortal\InvoiceController@index')->name('invoices.index')->middleware('portal_enabled');
|
||||
Route::post('invoices/payment', 'ClientPortal\InvoiceController@bulk')->name('invoices.bulk');
|
||||
Route::get('invoices/{invoice}', 'ClientPortal\InvoiceController@show')->name('invoice.show');
|
||||
|
83
tests/Feature/FilterApiTest.php
Normal file
83
tests/Feature/FilterApiTest.php
Normal file
@ -0,0 +1,83 @@
|
||||
<?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;
|
||||
|
||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||
use Illuminate\Routing\Middleware\ThrottleRequests;
|
||||
use Tests\MockAccountData;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @covers App\Http\Controllers\FilterController
|
||||
*/
|
||||
class FilterApiTest extends TestCase
|
||||
{
|
||||
use DatabaseTransactions;
|
||||
use MockAccountData;
|
||||
|
||||
public function setUp() :void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
|
||||
$this->makeTestData();
|
||||
|
||||
|
||||
$this->withoutMiddleware(
|
||||
ThrottleRequests::class
|
||||
);
|
||||
}
|
||||
|
||||
public function testActivityGet()
|
||||
{
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->post('/api/v1/filters/invoice');
|
||||
|
||||
$response->assertStatus(200);
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->post('/api/v1/filters/quote');
|
||||
|
||||
$response->assertStatus(200);
|
||||
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->post('/api/v1/filters/credit');
|
||||
|
||||
$response->assertStatus(200);
|
||||
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->post('/api/v1/filters/payment');
|
||||
|
||||
$response->assertStatus(200);
|
||||
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->post('/api/v1/filters/recurring_invoice');
|
||||
|
||||
$response->assertStatus(200);
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user