Working on proposals
This commit is contained in:
parent
63fcac391f
commit
7c3cbc3a7e
|
|
@ -42,6 +42,10 @@ if (! defined('APP_NAME')) {
|
||||||
define('ENTITY_RECURRING_EXPENSE', 'recurring_expense');
|
define('ENTITY_RECURRING_EXPENSE', 'recurring_expense');
|
||||||
define('ENTITY_CUSTOMER', 'customer');
|
define('ENTITY_CUSTOMER', 'customer');
|
||||||
define('ENTITY_SUBSCRIPTION', 'subscription');
|
define('ENTITY_SUBSCRIPTION', 'subscription');
|
||||||
|
define('ENTITY_PROPOSAL', 'proposal');
|
||||||
|
define('ENTITY_PROPOSAL_TEMPLATE', 'proposal_template');
|
||||||
|
define('ENTITY_PROPOSAL_SNIPPET', 'proposal_snippet');
|
||||||
|
define('ENTITY_PROPOSAL_CATEGORY', 'proposal_category');
|
||||||
|
|
||||||
define('INVOICE_TYPE_STANDARD', 1);
|
define('INVOICE_TYPE_STANDARD', 1);
|
||||||
define('INVOICE_TYPE_QUOTE', 2);
|
define('INVOICE_TYPE_QUOTE', 2);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,144 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Jobs\GenerateProposalChartData;
|
||||||
|
use App\Http\Requests\CreateProposalRequest;
|
||||||
|
use App\Http\Requests\ProposalRequest;
|
||||||
|
use App\Http\Requests\UpdateProposalRequest;
|
||||||
|
use App\Models\Invoice;
|
||||||
|
use App\Models\Proposal;
|
||||||
|
use App\Models\ProposalTemplate;
|
||||||
|
use App\Ninja\Datatables\ProposalDatatable;
|
||||||
|
use App\Ninja\Repositories\ProposalRepository;
|
||||||
|
use App\Services\ProposalService;
|
||||||
|
use Auth;
|
||||||
|
use Input;
|
||||||
|
use Session;
|
||||||
|
use View;
|
||||||
|
|
||||||
|
class ProposalController extends BaseController
|
||||||
|
{
|
||||||
|
protected $proposalRepo;
|
||||||
|
protected $proposalService;
|
||||||
|
protected $entityType = ENTITY_PROPOSAL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
public function __construct(ProposalRepository $proposalRepo, ProposalService $proposalService)
|
||||||
|
{
|
||||||
|
$this->proposalRepo = $proposalRepo;
|
||||||
|
$this->proposalService = $proposalService;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display a listing of the resource.
|
||||||
|
*
|
||||||
|
* @return Response
|
||||||
|
*/
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
return View::make('list_wrapper', [
|
||||||
|
'entityType' => ENTITY_PROPOSAL,
|
||||||
|
'datatable' => new ProposalDatatable(),
|
||||||
|
'title' => trans('texts.proposals'),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDatatable($expensePublicId = null)
|
||||||
|
{
|
||||||
|
$search = Input::get('sSearch');
|
||||||
|
$userId = Auth::user()->filterId();
|
||||||
|
|
||||||
|
return $this->proposalService->getDatatable($search, $userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
public function show(ProposalRequest $request)
|
||||||
|
{
|
||||||
|
$account = auth()->user()->account;
|
||||||
|
$proposal = $request->entity();
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'account' => auth()->user()->account,
|
||||||
|
'proposal' => $proposal,
|
||||||
|
'title' => trans('texts.view_proposal'),
|
||||||
|
'showBreadcrumbs' => false,
|
||||||
|
];
|
||||||
|
|
||||||
|
return View::make('proposals.show', $data);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
public function create(ProposalRequest $request)
|
||||||
|
{
|
||||||
|
$data = [
|
||||||
|
'account' => auth()->user()->account,
|
||||||
|
'proposal' => null,
|
||||||
|
'method' => 'POST',
|
||||||
|
'url' => 'proposals',
|
||||||
|
'title' => trans('texts.new_proposal'),
|
||||||
|
'quotes' => Invoice::scope()->with('client.contacts')->quotes()->orderBy('id')->get(),
|
||||||
|
'templates' => ProposalTemplate::scope()->orderBy('name')->get(),
|
||||||
|
'quotePublicId' => $request->quote_id,
|
||||||
|
];
|
||||||
|
|
||||||
|
return View::make('proposals.edit', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function edit(ProposalRequest $request)
|
||||||
|
{
|
||||||
|
$proposal = $request->entity();
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'account' => auth()->user()->account,
|
||||||
|
'proposal' => $proposal,
|
||||||
|
'method' => 'PUT',
|
||||||
|
'url' => 'proposals/' . $proposal->public_id,
|
||||||
|
'title' => trans('texts.edit_proposal'),
|
||||||
|
'clients' => Client::scope()->with('contacts')->orderBy('name')->get(),
|
||||||
|
'clientPublicId' => $proposal->client ? $proposal->client->public_id : null,
|
||||||
|
];
|
||||||
|
|
||||||
|
return View::make('proposals.edit', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function store(CreateProposalRequest $request)
|
||||||
|
{
|
||||||
|
$proposal = $this->proposalService->save($request->input());
|
||||||
|
|
||||||
|
Session::flash('message', trans('texts.created_proposal'));
|
||||||
|
|
||||||
|
return redirect()->to($proposal->getRoute());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update(UpdateProposalRequest $request)
|
||||||
|
{
|
||||||
|
$proposal = $this->proposalService->save($request->input(), $request->entity());
|
||||||
|
|
||||||
|
Session::flash('message', trans('texts.updated_proposal'));
|
||||||
|
|
||||||
|
$action = Input::get('action');
|
||||||
|
if (in_array($action, ['archive', 'delete', 'restore'])) {
|
||||||
|
return self::bulk();
|
||||||
|
}
|
||||||
|
|
||||||
|
return redirect()->to($proposal->getRoute());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function bulk()
|
||||||
|
{
|
||||||
|
$action = Input::get('action');
|
||||||
|
$ids = Input::get('public_id') ? Input::get('public_id') : Input::get('ids');
|
||||||
|
|
||||||
|
$count = $this->proposalService->bulk($ids, $action);
|
||||||
|
|
||||||
|
if ($count > 0) {
|
||||||
|
$field = $count == 1 ? "{$action}d_proposal" : "{$action}d_proposals";
|
||||||
|
$message = trans("texts.$field", ['count' => $count]);
|
||||||
|
Session::flash('message', $message);
|
||||||
|
}
|
||||||
|
|
||||||
|
return redirect()->to('/proposals');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Requests;
|
||||||
|
|
||||||
|
class ProposalRequest extends EntityRequest
|
||||||
|
{
|
||||||
|
protected $entityType = ENTITY_PROPOSAL;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Requests;
|
||||||
|
|
||||||
|
class ProposalSnippetRequest extends EntityRequest
|
||||||
|
{
|
||||||
|
protected $entityType = ENTITY_PROPOSAL_SNIPPET;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Requests;
|
||||||
|
|
||||||
|
class ProposalTemplateRequest extends EntityRequest
|
||||||
|
{
|
||||||
|
protected $entityType = ENTITY_PROPOSAL_TEMPLATE;
|
||||||
|
}
|
||||||
|
|
@ -321,6 +321,7 @@ class EntityModel extends Eloquent
|
||||||
'recurring_expenses' => 'files-o',
|
'recurring_expenses' => 'files-o',
|
||||||
'credits' => 'credit-card',
|
'credits' => 'credit-card',
|
||||||
'quotes' => 'file-text-o',
|
'quotes' => 'file-text-o',
|
||||||
|
'proposals' => 'tasks',
|
||||||
'tasks' => 'clock-o',
|
'tasks' => 'clock-o',
|
||||||
'expenses' => 'file-image-o',
|
'expenses' => 'file-image-o',
|
||||||
'vendors' => 'building',
|
'vendors' => 'building',
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,76 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
use Laracasts\Presenter\PresentableTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class ExpenseCategory.
|
||||||
|
*/
|
||||||
|
class Proposal extends EntityModel
|
||||||
|
{
|
||||||
|
use SoftDeletes;
|
||||||
|
use PresentableTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $dates = ['deleted_at'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $fillable = [
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
//protected $presenter = 'App\Ninja\Presenters\ProjectPresenter';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getEntityType()
|
||||||
|
{
|
||||||
|
return ENTITY_PROPOSAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getRoute()
|
||||||
|
{
|
||||||
|
return "/proposals/{$this->public_id}";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||||
|
*/
|
||||||
|
public function account()
|
||||||
|
{
|
||||||
|
return $this->belongsTo('App\Models\Account');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function quote()
|
||||||
|
{
|
||||||
|
return $this->belongsTo('App\Models\Invoice')->withTrashed();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDisplayName()
|
||||||
|
{
|
||||||
|
return 'TODO';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Proposal::creating(function ($project) {
|
||||||
|
$project->setNullValues();
|
||||||
|
});
|
||||||
|
|
||||||
|
Proposal::updating(function ($project) {
|
||||||
|
$project->setNullValues();
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,70 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
use Laracasts\Presenter\PresentableTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class ExpenseCategory.
|
||||||
|
*/
|
||||||
|
class ProposalCategory extends EntityModel
|
||||||
|
{
|
||||||
|
use SoftDeletes;
|
||||||
|
use PresentableTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $dates = ['deleted_at'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $fillable = [
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
//protected $presenter = 'App\Ninja\Presenters\ProjectPresenter';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getEntityType()
|
||||||
|
{
|
||||||
|
return ENTITY_PROPOSAL_CATEGORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getRoute()
|
||||||
|
{
|
||||||
|
return "/proposal_categories/{$this->public_id}";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||||
|
*/
|
||||||
|
public function account()
|
||||||
|
{
|
||||||
|
return $this->belongsTo('App\Models\Account');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDisplayName()
|
||||||
|
{
|
||||||
|
return $this->name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Proposal::creating(function ($project) {
|
||||||
|
$project->setNullValues();
|
||||||
|
});
|
||||||
|
|
||||||
|
Proposal::updating(function ($project) {
|
||||||
|
$project->setNullValues();
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
|
@ -0,0 +1,70 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
use Laracasts\Presenter\PresentableTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class ExpenseCategory.
|
||||||
|
*/
|
||||||
|
class ProposalSnippet extends EntityModel
|
||||||
|
{
|
||||||
|
use SoftDeletes;
|
||||||
|
use PresentableTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $dates = ['deleted_at'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $fillable = [
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
//protected $presenter = 'App\Ninja\Presenters\ProjectPresenter';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getEntityType()
|
||||||
|
{
|
||||||
|
return ENTITY_PROPOSAL_SNIPPET;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getRoute()
|
||||||
|
{
|
||||||
|
return "/proposal_snippets/{$this->public_id}";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||||
|
*/
|
||||||
|
public function account()
|
||||||
|
{
|
||||||
|
return $this->belongsTo('App\Models\Account');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDisplayName()
|
||||||
|
{
|
||||||
|
return $this->name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Proposal::creating(function ($project) {
|
||||||
|
$project->setNullValues();
|
||||||
|
});
|
||||||
|
|
||||||
|
Proposal::updating(function ($project) {
|
||||||
|
$project->setNullValues();
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
|
@ -0,0 +1,70 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
use Laracasts\Presenter\PresentableTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class ExpenseCategory.
|
||||||
|
*/
|
||||||
|
class ProposalTemplate extends EntityModel
|
||||||
|
{
|
||||||
|
use SoftDeletes;
|
||||||
|
use PresentableTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $dates = ['deleted_at'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $fillable = [
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
//protected $presenter = 'App\Ninja\Presenters\ProjectPresenter';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getEntityType()
|
||||||
|
{
|
||||||
|
return ENTITY_PROPOSAL_TEMPLATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getRoute()
|
||||||
|
{
|
||||||
|
return "/proposal_templates/{$this->public_id}";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||||
|
*/
|
||||||
|
public function account()
|
||||||
|
{
|
||||||
|
return $this->belongsTo('App\Models\Account');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDisplayName()
|
||||||
|
{
|
||||||
|
return $this->name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Proposal::creating(function ($project) {
|
||||||
|
$project->setNullValues();
|
||||||
|
});
|
||||||
|
|
||||||
|
Proposal::updating(function ($project) {
|
||||||
|
$project->setNullValues();
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Ninja\Datatables;
|
||||||
|
|
||||||
|
use Auth;
|
||||||
|
use URL;
|
||||||
|
use Utils;
|
||||||
|
|
||||||
|
class ProposalCategoryDatatable extends EntityDatatable
|
||||||
|
{
|
||||||
|
public $entityType = ENTITY_PROPOSAL_CATEGORY;
|
||||||
|
public $sortCol = 1;
|
||||||
|
|
||||||
|
public function columns()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
'name',
|
||||||
|
function ($model) {
|
||||||
|
return $model->name;
|
||||||
|
},
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function actions()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
trans('texts.edit_category'),
|
||||||
|
function ($model) {
|
||||||
|
return URL::to("proposal_categories/{$model->public_id}/edit");
|
||||||
|
},
|
||||||
|
function ($model) {
|
||||||
|
return Auth::user()->can('editByOwner', [ENTITY_PROPOSAL_CATEGORY, $model->user_id]);
|
||||||
|
},
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,64 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Ninja\Datatables;
|
||||||
|
|
||||||
|
use Auth;
|
||||||
|
use URL;
|
||||||
|
use Utils;
|
||||||
|
|
||||||
|
class ProposalDatatable extends EntityDatatable
|
||||||
|
{
|
||||||
|
public $entityType = ENTITY_PROPOSAL;
|
||||||
|
public $sortCol = 1;
|
||||||
|
|
||||||
|
public function columns()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
'quote',
|
||||||
|
function ($model) {
|
||||||
|
if (! Auth::user()->can('viewByOwner', [ENTITY_QUOTE, $model->user_id])) {
|
||||||
|
return $model->quote_number;
|
||||||
|
}
|
||||||
|
|
||||||
|
return link_to("quotes/{$model->quote_public_id}", $model->quote_number)->toHtml();
|
||||||
|
//$str = link_to("quotes/{$model->quote_public_id}", $model->quote_number)->toHtml();
|
||||||
|
//return $this->addNote($str, $model->private_notes);
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'template',
|
||||||
|
function ($model) {
|
||||||
|
return $model->template_name;
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'created',
|
||||||
|
function ($model) {
|
||||||
|
return Utils::fromSqlDate($model->created_at);
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'valid_until',
|
||||||
|
function ($model) {
|
||||||
|
return Utils::fromSqlDate($model->due_date);
|
||||||
|
},
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function actions()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
trans('texts.edit_proposal'),
|
||||||
|
function ($model) {
|
||||||
|
return URL::to("proposals/{$model->public_id}/edit");
|
||||||
|
},
|
||||||
|
function ($model) {
|
||||||
|
return Auth::user()->can('editByOwner', [ENTITY_PROPOSAL, $model->user_id]);
|
||||||
|
},
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Ninja\Datatables;
|
||||||
|
|
||||||
|
use Auth;
|
||||||
|
use URL;
|
||||||
|
use Utils;
|
||||||
|
|
||||||
|
class ProposalSnippetDatatable extends EntityDatatable
|
||||||
|
{
|
||||||
|
public $entityType = ENTITY_PROPOSAL_SNIPPET;
|
||||||
|
public $sortCol = 1;
|
||||||
|
|
||||||
|
public function columns()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
'name',
|
||||||
|
function ($model) {
|
||||||
|
if (! Auth::user()->can('editByOwner', [ENTITY_PROPOSAL_SNIPPET, $model->user_id])) {
|
||||||
|
return $model->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
return link_to("proposal_snippets/{$model->public_id}", $model->name)->toHtml();
|
||||||
|
//$str = link_to("quotes/{$model->quote_public_id}", $model->quote_number)->toHtml();
|
||||||
|
//return $this->addNote($str, $model->private_notes);
|
||||||
|
},
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function actions()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
trans('texts.edit_snippet'),
|
||||||
|
function ($model) {
|
||||||
|
return URL::to("proposals_snippets/{$model->public_id}/edit");
|
||||||
|
},
|
||||||
|
function ($model) {
|
||||||
|
return Auth::user()->can('editByOwner', [ENTITY_PROPOSAL_SNIPPET, $model->user_id]);
|
||||||
|
},
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Ninja\Datatables;
|
||||||
|
|
||||||
|
use Auth;
|
||||||
|
use URL;
|
||||||
|
use Utils;
|
||||||
|
|
||||||
|
class ProposalTemplateDatatable extends EntityDatatable
|
||||||
|
{
|
||||||
|
public $entityType = ENTITY_PROPOSAL_TEMPLATE;
|
||||||
|
public $sortCol = 1;
|
||||||
|
|
||||||
|
public function columns()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
'quote',
|
||||||
|
function ($model) {
|
||||||
|
if (! Auth::user()->can('editByOwner', [ENTITY_PROPOSAL_TEMPLATE, $model->user_id])) {
|
||||||
|
return $model->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
return link_to("proposal_templates/{$model->public_id}", $model->name)->toHtml();
|
||||||
|
//$str = link_to("quotes/{$model->quote_public_id}", $model->quote_number)->toHtml();
|
||||||
|
//return $this->addNote($str, $model->private_notes);
|
||||||
|
},
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function actions()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
trans('texts.edit_template'),
|
||||||
|
function ($model) {
|
||||||
|
return URL::to("proposal_templates/{$model->public_id}/edit");
|
||||||
|
},
|
||||||
|
function ($model) {
|
||||||
|
return Auth::user()->can('editByOwner', [ENTITY_PROPOSAL_TEMPLATE, $model->user_id]);
|
||||||
|
},
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Policies;
|
||||||
|
|
||||||
|
class ProposalCategoryPolicy extends EntityPolicy
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Policies;
|
||||||
|
|
||||||
|
class ProposalPolicy extends EntityPolicy
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Policies;
|
||||||
|
|
||||||
|
class ProposalSnippetPolicy extends EntityPolicy
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Policies;
|
||||||
|
|
||||||
|
class ProposalTemplatePolicy extends EntityPolicy
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
@ -34,6 +34,10 @@ class AuthServiceProvider extends ServiceProvider
|
||||||
\App\Models\PaymentTerm::class => \App\Policies\PaymentTermPolicy::class,
|
\App\Models\PaymentTerm::class => \App\Policies\PaymentTermPolicy::class,
|
||||||
\App\Models\Project::class => \App\Policies\ProjectPolicy::class,
|
\App\Models\Project::class => \App\Policies\ProjectPolicy::class,
|
||||||
\App\Models\AccountGatewayToken::class => \App\Policies\CustomerPolicy::class,
|
\App\Models\AccountGatewayToken::class => \App\Policies\CustomerPolicy::class,
|
||||||
|
\App\Models\Proposal::class => \App\Policies\Proposal::class,
|
||||||
|
\App\Models\ProposalSnippet::class => \App\Policies\ProposalSnippet::class,
|
||||||
|
\App\Models\ProposalTemplate::class => \App\Policies\ProposalTemplate::class,
|
||||||
|
\App\Models\ProposalCategory::class => \App\Policies\ProposalCategory::class,
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,8 @@
|
||||||
"toastr": "^2.1.3",
|
"toastr": "^2.1.3",
|
||||||
"jt.timepicker": "jquery-timepicker-jt#^1.11.12",
|
"jt.timepicker": "jquery-timepicker-jt#^1.11.12",
|
||||||
"qrcode.js": "qrcode-js#*",
|
"qrcode.js": "qrcode-js#*",
|
||||||
"money.js": "^0.1.3"
|
"money.js": "^0.1.3",
|
||||||
|
"grapesjs": "^0.13.8"
|
||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
"jquery": "~1.11"
|
"jquery": "~1.11"
|
||||||
|
|
|
||||||
|
|
@ -338,7 +338,6 @@ class ConfideSetupUsersTable extends Migration
|
||||||
$t->timestamp('viewed_date')->nullable();
|
$t->timestamp('viewed_date')->nullable();
|
||||||
|
|
||||||
$t->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
|
$t->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
|
||||||
;
|
|
||||||
$t->foreign('contact_id')->references('id')->on('contacts')->onDelete('cascade');
|
$t->foreign('contact_id')->references('id')->on('contacts')->onDelete('cascade');
|
||||||
$t->foreign('invoice_id')->references('id')->on('invoices')->onDelete('cascade');
|
$t->foreign('invoice_id')->references('id')->on('invoices')->onDelete('cascade');
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,83 @@ class AddSubscriptionFormat extends Migration
|
||||||
Schema::table('accounts', function ($table) {
|
Schema::table('accounts', function ($table) {
|
||||||
$table->boolean('ubl_email_attachment')->default(false);
|
$table->boolean('ubl_email_attachment')->default(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Schema::create('proposal_categories', function ($table) {
|
||||||
|
$table->increments('id');
|
||||||
|
$table->unsignedInteger('account_id');
|
||||||
|
$table->unsignedInteger('user_id');
|
||||||
|
$table->timestamps();
|
||||||
|
$table->softDeletes();
|
||||||
|
|
||||||
|
$table->string('name');
|
||||||
|
|
||||||
|
$table->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade');
|
||||||
|
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
|
||||||
|
|
||||||
|
$table->unsignedInteger('public_id')->index();
|
||||||
|
$table->unique(['account_id', 'public_id']);
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::create('proposal_snippets', function ($table) {
|
||||||
|
$table->increments('id');
|
||||||
|
$table->unsignedInteger('account_id');
|
||||||
|
$table->unsignedInteger('user_id');
|
||||||
|
$table->timestamps();
|
||||||
|
$table->softDeletes();
|
||||||
|
|
||||||
|
$table->unsignedInteger('proposal_category_id');
|
||||||
|
$table->string('name');
|
||||||
|
|
||||||
|
$table->mediumText('html');
|
||||||
|
$table->mediumText('css');
|
||||||
|
|
||||||
|
$table->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade');
|
||||||
|
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
|
||||||
|
|
||||||
|
$table->unsignedInteger('public_id')->index();
|
||||||
|
$table->unique(['account_id', 'public_id']);
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::create('proposal_templates', function ($table) {
|
||||||
|
$table->increments('id');
|
||||||
|
$table->unsignedInteger('account_id');
|
||||||
|
$table->unsignedInteger('user_id');
|
||||||
|
$table->timestamps();
|
||||||
|
$table->softDeletes();
|
||||||
|
|
||||||
|
$table->string('name');
|
||||||
|
$table->text('tags');
|
||||||
|
$table->mediumText('html');
|
||||||
|
$table->mediumText('css');
|
||||||
|
|
||||||
|
$table->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade');
|
||||||
|
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
|
||||||
|
|
||||||
|
$table->unsignedInteger('public_id')->index();
|
||||||
|
$table->unique(['account_id', 'public_id']);
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::create('proposals', function ($table) {
|
||||||
|
$table->increments('id');
|
||||||
|
$table->unsignedInteger('account_id');
|
||||||
|
$table->unsignedInteger('user_id');
|
||||||
|
$table->timestamps();
|
||||||
|
$table->softDeletes();
|
||||||
|
|
||||||
|
$table->unsignedInteger('quote_id')->index();
|
||||||
|
$table->unsignedInteger('temlate_id')->index();
|
||||||
|
$table->mediumText('html');
|
||||||
|
$table->mediumText('css');
|
||||||
|
|
||||||
|
$table->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade');
|
||||||
|
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
|
||||||
|
$table->foreign('quote_id')->references('id')->on('invoices')->onDelete('cascade');
|
||||||
|
$table->foreign('temlate_id')->references('id')->on('proposal_templates')->onDelete('cascade');
|
||||||
|
|
||||||
|
$table->unsignedInteger('public_id')->index();
|
||||||
|
$table->unique(['account_id', 'public_id']);
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -36,5 +113,10 @@ class AddSubscriptionFormat extends Migration
|
||||||
Schema::table('accounts', function ($table) {
|
Schema::table('accounts', function ($table) {
|
||||||
$table->dropColumn('ubl_email_attachment');
|
$table->dropColumn('ubl_email_attachment');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Schema::dropIfExists('proposals');
|
||||||
|
Schema::dropIfExists('proposal_templates');
|
||||||
|
Schema::dropIfExists('proposal_snippets');
|
||||||
|
Schema::dropIfExists('proposal_categories');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
11
gulpfile.js
11
gulpfile.js
|
|
@ -67,6 +67,11 @@ elixir(function(mix) {
|
||||||
bowerDir + '/bootstrap-daterangepicker/daterangepicker.css'
|
bowerDir + '/bootstrap-daterangepicker/daterangepicker.css'
|
||||||
], 'public/css/daterangepicker.css');
|
], 'public/css/daterangepicker.css');
|
||||||
|
|
||||||
|
mix.styles([
|
||||||
|
bowerDir + '/grapesjs/dist/css/grapes.min.css',
|
||||||
|
//'grapesjs-preset-newsletter.css',
|
||||||
|
], 'public/css/grapesjs.css');
|
||||||
|
|
||||||
mix.styles([
|
mix.styles([
|
||||||
bowerDir + '/jt.timepicker/jquery.timepicker.css'
|
bowerDir + '/jt.timepicker/jquery.timepicker.css'
|
||||||
], 'public/css/jquery.timepicker.css');
|
], 'public/css/jquery.timepicker.css');
|
||||||
|
|
@ -103,6 +108,12 @@ elixir(function(mix) {
|
||||||
bowerDir + '/bootstrap-daterangepicker/daterangepicker.js'
|
bowerDir + '/bootstrap-daterangepicker/daterangepicker.js'
|
||||||
], 'public/js/daterangepicker.min.js');
|
], 'public/js/daterangepicker.min.js');
|
||||||
|
|
||||||
|
mix.scripts([
|
||||||
|
bowerDir + '/grapesjs/dist/grapes.js',
|
||||||
|
'grapesjs-blocks-basic.min.js',
|
||||||
|
'grapesjs-preset-newsletter.min.js',
|
||||||
|
], 'public/js/grapesjs.min.js');
|
||||||
|
|
||||||
mix.scripts([
|
mix.scripts([
|
||||||
bowerDir + '/jt.timepicker/jquery.timepicker.js'
|
bowerDir + '/jt.timepicker/jquery.timepicker.js'
|
||||||
], 'public/js/jquery.timepicker.js');
|
], 'public/js/jquery.timepicker.js');
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,211 @@
|
||||||
|
/* Class names prefixes */
|
||||||
|
/* Colors / Theme */
|
||||||
|
.gjs-clm-tags .gjs-sm-title,
|
||||||
|
.gjs-sm-sector .gjs-sm-title {
|
||||||
|
border-top: none; }
|
||||||
|
|
||||||
|
.gjs-clm-tags .gjs-clm-tag {
|
||||||
|
background-color: #4c9790;
|
||||||
|
border: none;
|
||||||
|
box-shadow: none;
|
||||||
|
padding: 5px 8px;
|
||||||
|
text-shadow: none; }
|
||||||
|
|
||||||
|
.gjs-field {
|
||||||
|
background-color: rgba(0, 0, 0, 0.15);
|
||||||
|
box-shadow: none; }
|
||||||
|
|
||||||
|
.gjs-btnt.gjs-pn-active,
|
||||||
|
.gjs-pn-btn.gjs-pn-active {
|
||||||
|
box-shadow: none; }
|
||||||
|
|
||||||
|
.gjs-pn-btn:hover {
|
||||||
|
color: rgba(255, 255, 255, 0.75); }
|
||||||
|
|
||||||
|
.gjs-btnt.gjs-pn-active,
|
||||||
|
.gjs-color-active,
|
||||||
|
.gjs-pn-btn.gjs-pn-active,
|
||||||
|
.gjs-pn-btn:active,
|
||||||
|
.gjs-block:hover {
|
||||||
|
color: #35d7bb; }
|
||||||
|
|
||||||
|
#gjs-rte-toolbar .gjs-rte-btn,
|
||||||
|
.gjs-btn-prim,
|
||||||
|
.gjs-btnt,
|
||||||
|
.gjs-clm-tags .gjs-sm-composite.gjs-clm-field,
|
||||||
|
.gjs-clm-tags .gjs-sm-field.gjs-sm-composite,
|
||||||
|
.gjs-clm-tags .gjs-sm-stack #gjs-sm-add,
|
||||||
|
.gjs-color-main,
|
||||||
|
.gjs-mdl-dialog,
|
||||||
|
.gjs-off-prv,
|
||||||
|
.gjs-pn-btn,
|
||||||
|
.gjs-pn-panel,
|
||||||
|
.gjs-sm-sector .gjs-sm-composite.gjs-clm-field,
|
||||||
|
.gjs-sm-sector .gjs-sm-field.gjs-sm-composite,
|
||||||
|
.gjs-sm-sector .gjs-sm-stack #gjs-sm-add {
|
||||||
|
color: #a0aabf; }
|
||||||
|
|
||||||
|
#gjs-rte-toolbar,
|
||||||
|
.gjs-bg-main,
|
||||||
|
.gjs-clm-select option,
|
||||||
|
.gjs-clm-tags .gjs-sm-colorp-c,
|
||||||
|
.gjs-editor,
|
||||||
|
.gjs-mdl-dialog,
|
||||||
|
.gjs-nv-item .gjs-nv-title-c,
|
||||||
|
.gjs-off-prv,
|
||||||
|
.gjs-pn-panel,
|
||||||
|
.gjs-block,
|
||||||
|
.gjs-select option,
|
||||||
|
.gjs-sm-sector .gjs-sm-colorp-c,
|
||||||
|
.gjs-sm-select option,
|
||||||
|
.gjs-sm-unit option,
|
||||||
|
.sp-container {
|
||||||
|
background-color: #373d49; }
|
||||||
|
|
||||||
|
.gjs-import-label,
|
||||||
|
.gjs-export-label {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
font-size: 13px; }
|
||||||
|
|
||||||
|
.gjs-mdl-dialog .gjs-btn-import {
|
||||||
|
margin-top: 10px; }
|
||||||
|
|
||||||
|
.CodeMirror {
|
||||||
|
border-radius: 3px;
|
||||||
|
height: 450px;
|
||||||
|
font-family: sans-serif, monospace;
|
||||||
|
letter-spacing: 0.3px;
|
||||||
|
font-size: 12px; }
|
||||||
|
|
||||||
|
/* Extra */
|
||||||
|
.gjs-block {
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.2);
|
||||||
|
border-radius: 3px;
|
||||||
|
margin: 10px 2.5% 5px;
|
||||||
|
box-shadow: 0 1px 0 0 rgba(0, 0, 0, 0.15);
|
||||||
|
transition: box-shadow, color 0.2s ease 0s; }
|
||||||
|
|
||||||
|
.gjs-block:hover {
|
||||||
|
box-shadow: 0 3px 4px 0 rgba(0, 0, 0, 0.15); }
|
||||||
|
|
||||||
|
#gjs-pn-views-container.gjs-pn-panel {
|
||||||
|
padding: 39px 0 0; }
|
||||||
|
|
||||||
|
#gjs-pn-views.gjs-pn-panel {
|
||||||
|
padding: 0;
|
||||||
|
border: none; }
|
||||||
|
|
||||||
|
#gjs-pn-views .gjs-pn-btn {
|
||||||
|
margin: 0;
|
||||||
|
height: 40px;
|
||||||
|
padding: 10px;
|
||||||
|
width: 25%;
|
||||||
|
border-bottom: 2px solid rgba(0, 0, 0, 0.3); }
|
||||||
|
|
||||||
|
#gjs-pn-views .gjs-pn-active {
|
||||||
|
color: rgba(255, 255, 255, 0.9);
|
||||||
|
border-bottom: 2px solid #35d7bb;
|
||||||
|
border-radius: 0; }
|
||||||
|
|
||||||
|
#gjs-pn-devices-c {
|
||||||
|
padding-left: 30px; }
|
||||||
|
|
||||||
|
#gjs-pn-options {
|
||||||
|
padding-right: 30px; }
|
||||||
|
|
||||||
|
.gjs-sm-composite .gjs-sm-properties {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: row wrap;
|
||||||
|
justify-content: space-between; }
|
||||||
|
|
||||||
|
#gjs-sm-border-top-left-radius,
|
||||||
|
#gjs-sm-border-top-right-radius,
|
||||||
|
#gjs-sm-border-bottom-left-radius,
|
||||||
|
#gjs-sm-border-bottom-right-radius,
|
||||||
|
#gjs-sm-margin-top,
|
||||||
|
#gjs-sm-margin-bottom,
|
||||||
|
#gjs-sm-margin-right,
|
||||||
|
#gjs-sm-margin-left,
|
||||||
|
#gjs-sm-padding-top,
|
||||||
|
#gjs-sm-padding-bottom,
|
||||||
|
#gjs-sm-padding-right,
|
||||||
|
#gjs-sm-padding-left {
|
||||||
|
flex: 999 1 60px; }
|
||||||
|
|
||||||
|
#gjs-sm-border-width,
|
||||||
|
#gjs-sm-border-style,
|
||||||
|
#gjs-sm-border-color {
|
||||||
|
flex: 999 1 80px; }
|
||||||
|
|
||||||
|
#gjs-sm-margin-left,
|
||||||
|
#gjs-sm-padding-left {
|
||||||
|
order: 2; }
|
||||||
|
|
||||||
|
#gjs-sm-margin-right,
|
||||||
|
#gjs-sm-padding-right {
|
||||||
|
order: 3; }
|
||||||
|
|
||||||
|
#gjs-sm-margin-bottom,
|
||||||
|
#gjs-sm-padding-bottom {
|
||||||
|
order: 4; }
|
||||||
|
|
||||||
|
.gjs-field-radio {
|
||||||
|
width: 100%; }
|
||||||
|
|
||||||
|
.gjs-field-radio #gjs-sm-input-holder {
|
||||||
|
display: flex; }
|
||||||
|
|
||||||
|
.gjs-radio-item {
|
||||||
|
flex: 1 0 auto;
|
||||||
|
text-align: center; }
|
||||||
|
|
||||||
|
.gjs-sm-sector .gjs-sm-property.gjs-sm-list {
|
||||||
|
width: 50%; }
|
||||||
|
|
||||||
|
.gjs-mdl-content {
|
||||||
|
border-top: none; }
|
||||||
|
|
||||||
|
.gjs-sm-sector .gjs-sm-property .gjs-sm-layer.gjs-sm-active {
|
||||||
|
background-color: rgba(255, 255, 255, 0.09); }
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
#gjs-pn-views-container,
|
||||||
|
#gjs-pn-views{
|
||||||
|
min-width: 270px;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
.gjs-f-button::before {
|
||||||
|
content: 'B'; }
|
||||||
|
|
||||||
|
.gjs-f-divider::before {
|
||||||
|
content: 'D'; }
|
||||||
|
|
||||||
|
.gjs-mdl-dialog-sm {
|
||||||
|
width: 300px; }
|
||||||
|
|
||||||
|
.gjs-mdl-dialog form .gjs-sm-property {
|
||||||
|
font-size: 12px;
|
||||||
|
margin-bottom: 15px; }
|
||||||
|
|
||||||
|
.gjs-mdl-dialog form .gjs-sm-label {
|
||||||
|
margin-bottom: 5px; }
|
||||||
|
|
||||||
|
#gjs-clm-status-c {
|
||||||
|
display: none; }
|
||||||
|
|
||||||
|
.anim-spin {
|
||||||
|
animation: 0.5s linear 0s normal none infinite running spin; }
|
||||||
|
|
||||||
|
.form-status {
|
||||||
|
float: right;
|
||||||
|
font-size: 14px; }
|
||||||
|
|
||||||
|
.text-danger {
|
||||||
|
color: #f92929; }
|
||||||
|
|
||||||
|
@keyframes spin {
|
||||||
|
0% {
|
||||||
|
transform: rotate(0deg); }
|
||||||
|
100% {
|
||||||
|
transform: rotate(360deg); } }
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -2659,6 +2659,8 @@ $LANG = array(
|
||||||
'tax_amount' => 'Tax Amount',
|
'tax_amount' => 'Tax Amount',
|
||||||
'tax_paid' => 'Tax Paid',
|
'tax_paid' => 'Tax Paid',
|
||||||
'none' => 'None',
|
'none' => 'None',
|
||||||
|
'proposals' => 'Proposals',
|
||||||
|
'new_proposal' => 'New Proposal',
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -348,6 +348,7 @@
|
||||||
'recurring_invoices' => 'recurring',
|
'recurring_invoices' => 'recurring',
|
||||||
'credits' => false,
|
'credits' => false,
|
||||||
'quotes' => false,
|
'quotes' => false,
|
||||||
|
'proposals' => false,
|
||||||
'projects' => false,
|
'projects' => false,
|
||||||
'tasks' => false,
|
'tasks' => false,
|
||||||
'expenses' => false,
|
'expenses' => false,
|
||||||
|
|
@ -376,6 +377,7 @@
|
||||||
'recurring_invoices',
|
'recurring_invoices',
|
||||||
'credits',
|
'credits',
|
||||||
'quotes',
|
'quotes',
|
||||||
|
'proposals',
|
||||||
'projects',
|
'projects',
|
||||||
'tasks',
|
'tasks',
|
||||||
'expenses',
|
'expenses',
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,122 @@
|
||||||
|
@extends('header')
|
||||||
|
|
||||||
|
@section('head')
|
||||||
|
@parent
|
||||||
|
|
||||||
|
<script src="{{ asset('js/grapesjs.min.js') }}?no_cache={{ NINJA_VERSION }}" type="text/javascript"></script>
|
||||||
|
<link href="{{ asset('css/grapesjs.css') }}?no_cache={{ NINJA_VERSION }}" rel="stylesheet" type="text/css"/>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.gjs-four-color {
|
||||||
|
color: white !important;
|
||||||
|
}
|
||||||
|
.gjs-block.fa {
|
||||||
|
font-size: 4em !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
@stop
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
|
||||||
|
{!! Former::open() !!}
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-12">
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-body">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
{!! Former::select('quote_id')->addOption('', '')
|
||||||
|
->label(trans('texts.quote'))
|
||||||
|
->addGroupClass('quote-select') !!}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
{!! Former::select('template_id')->addOption('', '')
|
||||||
|
->label(trans('texts.template'))
|
||||||
|
->addGroupClass('template-select') !!}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<center class="buttons">
|
||||||
|
{!! Button::normal(trans('texts.cancel'))
|
||||||
|
->appendIcon(Icon::create('remove-circle'))
|
||||||
|
->asLinkTo(HTMLUtils::previousUrl('/proposals')) !!}
|
||||||
|
|
||||||
|
{!! Button::success(trans("texts.save"))
|
||||||
|
->withAttributes(array('id' => 'saveButton', 'onclick' => 'onSaveClick()'))
|
||||||
|
->appendIcon(Icon::create('floppy-disk')) !!}
|
||||||
|
</center>
|
||||||
|
|
||||||
|
{!! Former::close() !!}
|
||||||
|
|
||||||
|
<div id="gjs"></div>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
var quotes = {!! $quotes !!};
|
||||||
|
var quoteMap = {};
|
||||||
|
|
||||||
|
var templates = {!! $templates !!};
|
||||||
|
var templateMap = {};
|
||||||
|
|
||||||
|
$(function() {
|
||||||
|
var quoteId = {{ $quotePublicId ?: 0 }};
|
||||||
|
var $quoteSelect = $('select#quote_id');
|
||||||
|
for (var i = 0; i < quotes.length; i++) {
|
||||||
|
var quote = quotes[i];
|
||||||
|
quoteMap[quote.public_id] = quote;
|
||||||
|
$quoteSelect.append(new Option(quote.invoice_number + ' - ' + getClientDisplayName(quote.client), quote.public_id));
|
||||||
|
}
|
||||||
|
@include('partials/entity_combobox', ['entityType' => ENTITY_QUOTE])
|
||||||
|
|
||||||
|
var $proposal_templateSelect = $('select#template_id');
|
||||||
|
for (var i = 0; i < templates.length; i++) {
|
||||||
|
var template = templates[i];
|
||||||
|
templateMap[template.public_id] = template;
|
||||||
|
$templateSelect.append(new Option(template.name, template.public_id));
|
||||||
|
}
|
||||||
|
@include('partials/entity_combobox', ['entityType' => ENTITY_PROPOSAL_TEMPLATE])
|
||||||
|
|
||||||
|
var editor = grapesjs.init({
|
||||||
|
container : '#gjs',
|
||||||
|
components: '',
|
||||||
|
style: '',
|
||||||
|
showDevices: false,
|
||||||
|
plugins: ['gjs-preset-newsletter'],
|
||||||
|
//plugins: ['gjs-blocks-basic'],
|
||||||
|
storageManager: {type: 'none'},
|
||||||
|
panels: {
|
||||||
|
Xdefaults : [{
|
||||||
|
id : 'commands',
|
||||||
|
buttons : [{
|
||||||
|
id : 'smile',
|
||||||
|
className : 'fa fa-smile-o',
|
||||||
|
attributes : { title: 'Smile' }
|
||||||
|
}],
|
||||||
|
}],
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
|
var blockManager = editor.BlockManager;
|
||||||
|
blockManager.add('h1-block', {
|
||||||
|
label: 'Heading',
|
||||||
|
category: 'Basic',
|
||||||
|
content: '<h1>Put your title here</h1>',
|
||||||
|
attributes: {
|
||||||
|
title: 'Insert h1 block',
|
||||||
|
class:'fa fa-smile-o'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
@stop
|
||||||
|
|
@ -205,6 +205,18 @@ Route::group(['middleware' => ['lookup:user', 'auth:user']], function () {
|
||||||
Route::get('api/quotes/{client_id?}', 'QuoteController@getDatatable');
|
Route::get('api/quotes/{client_id?}', 'QuoteController@getDatatable');
|
||||||
Route::post('quotes/bulk', 'QuoteController@bulk');
|
Route::post('quotes/bulk', 'QuoteController@bulk');
|
||||||
|
|
||||||
|
Route::get('proposals/create/{quote_id?}', 'ProposalController@create');
|
||||||
|
Route::resource('proposals', 'ProposalController');
|
||||||
|
|
||||||
|
Route::get('proposal_templates/create', 'ProposalTemplateController@create');
|
||||||
|
Route::resource('proposal_templates', 'ProposalTemplateController');
|
||||||
|
|
||||||
|
Route::get('proposal_snippets/create', 'ProposalSnippetController@create');
|
||||||
|
Route::resource('proposal_snippets', 'ProposalSnippetController');
|
||||||
|
|
||||||
|
Route::get('proposal_categories/create', 'ProposalCategoryController@create');
|
||||||
|
Route::resource('proposal_categories', 'ProposalController');
|
||||||
|
|
||||||
Route::resource('payments', 'PaymentController');
|
Route::resource('payments', 'PaymentController');
|
||||||
Route::get('payments/create/{client_id?}/{invoice_id?}', 'PaymentController@create');
|
Route::get('payments/create/{client_id?}/{invoice_id?}', 'PaymentController@create');
|
||||||
Route::get('api/payments/{client_id?}', 'PaymentController@getDatatable');
|
Route::get('api/payments/{client_id?}', 'PaymentController@getDatatable');
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue