diff --git a/app/Http/Controllers/ProposalCategoryController.php b/app/Http/Controllers/ProposalCategoryController.php
new file mode 100644
index 000000000000..556b12c47e08
--- /dev/null
+++ b/app/Http/Controllers/ProposalCategoryController.php
@@ -0,0 +1,123 @@
+proposalCategoryRepo = $proposalCategoryRepo;
+ $this->proposalCategoryService = $proposalCategoryService;
+ }
+
+ /**
+ * Display a listing of the resource.
+ *
+ * @return Response
+ */
+ public function index()
+ {
+ return View::make('list_wrapper', [
+ 'entityType' => ENTITY_PROPOSAL_CATEGORY,
+ 'datatable' => new ProposalCategoryDatatable(),
+ 'title' => trans('texts.proposal_categories'),
+ ]);
+ }
+
+ public function getDatatable($expensePublicId = null)
+ {
+ $search = Input::get('sSearch');
+ $userId = Auth::user()->filterId();
+
+ return $this->proposalCategoryService->getDatatable($search, $userId);
+ }
+
+ public function create(ProposalCategoryRequest $request)
+ {
+ $data = [
+ 'account' => auth()->user()->account,
+ 'proposalCategory' => null,
+ 'method' => 'POST',
+ 'url' => 'proposal_categories',
+ 'title' => trans('texts.new_proposal_category'),
+ 'quotes' => Invoice::scope()->with('client.contacts')->quotes()->orderBy('id')->get(),
+ 'templates' => ProposalCategory::scope()->orderBy('name')->get(),
+ 'quotePublicId' => $request->quote_id,
+ ];
+
+ return View::make('proposal_categories.edit', $data);
+ }
+
+ public function edit(ProposalCategoryRequest $request)
+ {
+ $proposalCategory = $request->entity();
+
+ $data = [
+ 'account' => auth()->user()->account,
+ 'proposalCategory' => $proposalCategory,
+ 'method' => 'PUT',
+ 'url' => 'proposal_categories/' . $proposalCategory->public_id,
+ 'title' => trans('texts.edit_proposal_category'),
+ 'clients' => Client::scope()->with('contacts')->orderBy('name')->get(),
+ 'clientPublicId' => $proposalCategory->client ? $proposalCategory->client->public_id : null,
+ ];
+
+ return View::make('proposal_categories.edit', $data);
+ }
+
+ public function store(CreateProposalCategoryRequest $request)
+ {
+ $proposalCategory = $this->proposalCategoryService->save($request->input());
+
+ Session::flash('message', trans('texts.created_proposal_category'));
+
+ return redirect()->to($proposalCategory->getRoute());
+ }
+
+ public function update(UpdateProposalCategoryRequest $request)
+ {
+ $proposalCategory = $this->proposalCategoryService->save($request->input(), $request->entity());
+
+ Session::flash('message', trans('texts.updated_proposal_category'));
+
+ $action = Input::get('action');
+ if (in_array($action, ['archive', 'delete', 'restore'])) {
+ return self::bulk();
+ }
+
+ return redirect()->to($proposalCategory->getRoute());
+ }
+
+ public function bulk()
+ {
+ $action = Input::get('action');
+ $ids = Input::get('public_id') ? Input::get('public_id') : Input::get('ids');
+
+ $count = $this->proposalCategoryService->bulk($ids, $action);
+
+ if ($count > 0) {
+ $field = $count == 1 ? "{$action}d_proposal_category" : "{$action}d_proposal_categories";
+ $message = trans("texts.$field", ['count' => $count]);
+ Session::flash('message', $message);
+ }
+
+ return redirect()->to('/proposal_categories');
+ }
+}
diff --git a/app/Http/Controllers/ProposalController.php b/app/Http/Controllers/ProposalController.php
index cc76d4b9a661..9ead6fdf6f9b 100644
--- a/app/Http/Controllers/ProposalController.php
+++ b/app/Http/Controllers/ProposalController.php
@@ -2,7 +2,6 @@
namespace App\Http\Controllers;
-use App\Jobs\GenerateProposalChartData;
use App\Http\Requests\CreateProposalRequest;
use App\Http\Requests\ProposalRequest;
use App\Http\Requests\UpdateProposalRequest;
@@ -23,13 +22,11 @@ class ProposalController extends BaseController
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.
@@ -53,23 +50,6 @@ class ProposalController extends BaseController
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 = [
diff --git a/app/Http/Controllers/ProposalSnippetController.php b/app/Http/Controllers/ProposalSnippetController.php
new file mode 100644
index 000000000000..c61547928cb3
--- /dev/null
+++ b/app/Http/Controllers/ProposalSnippetController.php
@@ -0,0 +1,124 @@
+proposalSnippetRepo = $proposalSnippetRepo;
+ $this->proposalSnippetService = $proposalSnippetService;
+ }
+
+ /**
+ * Display a listing of the resource.
+ *
+ * @return Response
+ */
+ public function index()
+ {
+ return View::make('list_wrapper', [
+ 'entityType' => ENTITY_PROPOSAL_SNIPPET,
+ 'datatable' => new ProposalSnippetDatatable(),
+ 'title' => trans('texts.proposal_snippets'),
+ ]);
+ }
+
+ public function getDatatable($expensePublicId = null)
+ {
+ $search = Input::get('sSearch');
+ $userId = Auth::user()->filterId();
+
+ return $this->proposalSnippetService->getDatatable($search, $userId);
+ }
+
+ public function create(ProposalSnippetRequest $request)
+ {
+ $data = [
+ 'account' => auth()->user()->account,
+ 'proposalSnippet' => null,
+ 'method' => 'POST',
+ 'url' => 'proposal_snippets',
+ 'title' => trans('texts.new_proposal_snippet'),
+ 'quotes' => Invoice::scope()->with('client.contacts')->quotes()->orderBy('id')->get(),
+ 'templates' => ProposalSnippetTemplate::scope()->orderBy('name')->get(),
+ 'quotePublicId' => $request->quote_id,
+ ];
+
+ return View::make('proposal_snippets.edit', $data);
+ }
+
+ public function edit(ProposalSnippetRequest $request)
+ {
+ $proposalSnippet = $request->entity();
+
+ $data = [
+ 'account' => auth()->user()->account,
+ 'proposalSnippet' => $proposalSnippet,
+ 'method' => 'PUT',
+ 'url' => 'proposal_snippets/' . $proposalSnippet->public_id,
+ 'title' => trans('texts.edit_proposal_snippet'),
+ 'clients' => Client::scope()->with('contacts')->orderBy('name')->get(),
+ 'clientPublicId' => $proposalSnippet->client ? $proposalSnippet->client->public_id : null,
+ ];
+
+ return View::make('proposal_snippets.edit', $data);
+ }
+
+ public function store(CreateProposalSnippetRequest $request)
+ {
+ $proposalSnippet = $this->proposalSnippetService->save($request->input());
+
+ Session::flash('message', trans('texts.created_proposal_snippet'));
+
+ return redirect()->to($proposalSnippet->getRoute());
+ }
+
+ public function update(UpdateProposalSnippetRequest $request)
+ {
+ $proposalSnippet = $this->proposalSnippetService->save($request->input(), $request->entity());
+
+ Session::flash('message', trans('texts.updated_proposal_snippet'));
+
+ $action = Input::get('action');
+ if (in_array($action, ['archive', 'delete', 'restore'])) {
+ return self::bulk();
+ }
+
+ return redirect()->to($proposalSnippet->getRoute());
+ }
+
+ public function bulk()
+ {
+ $action = Input::get('action');
+ $ids = Input::get('public_id') ? Input::get('public_id') : Input::get('ids');
+
+ $count = $this->proposalSnippetService->bulk($ids, $action);
+
+ if ($count > 0) {
+ $field = $count == 1 ? "{$action}d_proposal_snippet" : "{$action}d_proposal_snippets";
+ $message = trans("texts.$field", ['count' => $count]);
+ Session::flash('message', $message);
+ }
+
+ return redirect()->to('/proposal_snippets');
+ }
+}
diff --git a/app/Http/Controllers/ProposalTemplateController.php b/app/Http/Controllers/ProposalTemplateController.php
new file mode 100644
index 000000000000..c7b792c57f79
--- /dev/null
+++ b/app/Http/Controllers/ProposalTemplateController.php
@@ -0,0 +1,124 @@
+proposalTemplateRepo = $proposalTemplateRepo;
+ $this->proposalTemplateService = $proposalTemplateService;
+ }
+
+ /**
+ * Display a listing of the resource.
+ *
+ * @return Response
+ */
+ public function index()
+ {
+ return View::make('list_wrapper', [
+ 'entityType' => ENTITY_PROPOSAL_TEMPLATE,
+ 'datatable' => new ProposalTemplateDatatable(),
+ 'title' => trans('texts.proposal_templates'),
+ ]);
+ }
+
+ public function getDatatable($expensePublicId = null)
+ {
+ $search = Input::get('sSearch');
+ $userId = Auth::user()->filterId();
+
+ return $this->proposalTemplateService->getDatatable($search, $userId);
+ }
+
+ public function create(ProposalTemplateRequest $request)
+ {
+ $data = [
+ 'account' => auth()->user()->account,
+ 'proposalTemplate' => null,
+ 'method' => 'POST',
+ 'url' => 'proposal_templates',
+ 'title' => trans('texts.new_proposal_template'),
+ 'quotes' => Invoice::scope()->with('client.contacts')->quotes()->orderBy('id')->get(),
+ 'templates' => ProposalTemplateTemplate::scope()->orderBy('name')->get(),
+ 'quotePublicId' => $request->quote_id,
+ ];
+
+ return View::make('proposal_templates.edit', $data);
+ }
+
+ public function edit(ProposalTemplateRequest $request)
+ {
+ $proposalTemplate = $request->entity();
+
+ $data = [
+ 'account' => auth()->user()->account,
+ 'proposalTemplate' => $proposalTemplate,
+ 'method' => 'PUT',
+ 'url' => 'proposal_templates/' . $proposalTemplate->public_id,
+ 'title' => trans('texts.edit_proposal_template'),
+ 'clients' => Client::scope()->with('contacts')->orderBy('name')->get(),
+ 'clientPublicId' => $proposalTemplate->client ? $proposalTemplate->client->public_id : null,
+ ];
+
+ return View::make('proposal_templates.edit', $data);
+ }
+
+ public function store(CreateProposalTemplateRequest $request)
+ {
+ $proposalTemplate = $this->proposalTemplateService->save($request->input());
+
+ Session::flash('message', trans('texts.created_proposal_template'));
+
+ return redirect()->to($proposalTemplate->getRoute());
+ }
+
+ public function update(UpdateProposalTemplateRequest $request)
+ {
+ $proposalTemplate = $this->proposalTemplateService->save($request->input(), $request->entity());
+
+ Session::flash('message', trans('texts.updated_proposal_template'));
+
+ $action = Input::get('action');
+ if (in_array($action, ['archive', 'delete', 'restore'])) {
+ return self::bulk();
+ }
+
+ return redirect()->to($proposalTemplate->getRoute());
+ }
+
+ public function bulk()
+ {
+ $action = Input::get('action');
+ $ids = Input::get('public_id') ? Input::get('public_id') : Input::get('ids');
+
+ $count = $this->proposalTemplateService->bulk($ids, $action);
+
+ if ($count > 0) {
+ $field = $count == 1 ? "{$action}d_proposal_template" : "{$action}d_proposal_templates";
+ $message = trans("texts.$field", ['count' => $count]);
+ Session::flash('message', $message);
+ }
+
+ return redirect()->to('/proposal_templates');
+ }
+}
diff --git a/app/Http/Requests/ProposalCategoryRequest.php b/app/Http/Requests/ProposalCategoryRequest.php
new file mode 100644
index 000000000000..debc35c5c4f1
--- /dev/null
+++ b/app/Http/Requests/ProposalCategoryRequest.php
@@ -0,0 +1,8 @@
+get();
+ }
+
+ public function find($filter = null, $userId = false)
+ {
+ $query = DB::table('proposal_categories')
+ ->where('proposal_categories.account_id', '=', Auth::user()->account_id)
+ ->leftjoin('invoices', 'invoices.id', '=', 'proposal_categories.quote_id')
+ ->leftjoin('clients', 'clients.id', '=', 'invoices.client_id')
+ ->leftJoin('contacts', 'contacts.client_id', '=', 'clients.id')
+ ->where('clients.deleted_at', '=', null)
+ ->where('contacts.deleted_at', '=', null)
+ ->where('contacts.is_primary', '=', true)
+ ->select(
+ 'proposal_categories.name as proposal',
+ 'proposal_categories.public_id',
+ 'proposal_categories.user_id',
+ 'proposal_categories.deleted_at',
+ 'proposal_categories.is_deleted',
+ 'proposal_categories.private_notes',
+ DB::raw("COALESCE(NULLIF(clients.name,''), NULLIF(CONCAT(contacts.first_name, ' ', contacts.last_name),''), NULLIF(contacts.email,'')) client_name"),
+ 'clients.user_id as client_user_id',
+ 'clients.public_id as client_public_id'
+ );
+
+ $this->applyFilters($query, ENTITY_PROPOSAL_CATEGORY);
+
+ if ($filter) {
+ $query->where(function ($query) use ($filter) {
+ $query->where('clients.name', 'like', '%'.$filter.'%')
+ ->orWhere('contacts.first_name', 'like', '%'.$filter.'%')
+ ->orWhere('contacts.last_name', 'like', '%'.$filter.'%')
+ ->orWhere('contacts.email', 'like', '%'.$filter.'%')
+ ->orWhere('proposal_categories.name', 'like', '%'.$filter.'%');
+ });
+ }
+
+ if ($userId) {
+ $query->where('proposal_categories.user_id', '=', $userId);
+ }
+
+ return $query;
+ }
+
+ public function save($input, $proposal = false)
+ {
+ $publicId = isset($data['public_id']) ? $data['public_id'] : false;
+
+ if (! $proposal) {
+ $proposal = ProposalCategory::createNew();
+ }
+
+ $proposal->fill($input);
+ $proposal->save();
+
+ return $proposal;
+ }
+}
diff --git a/app/Ninja/Repositories/ProposalRepository.php b/app/Ninja/Repositories/ProposalRepository.php
new file mode 100644
index 000000000000..00c2fbe9eb57
--- /dev/null
+++ b/app/Ninja/Repositories/ProposalRepository.php
@@ -0,0 +1,76 @@
+get();
+ }
+
+ public function find($filter = null, $userId = false)
+ {
+ $query = DB::table('proposals')
+ ->where('proposals.account_id', '=', Auth::user()->account_id)
+ ->leftjoin('invoices', 'invoices.id', '=', 'proposals.quote_id')
+ ->leftjoin('clients', 'clients.id', '=', 'invoices.client_id')
+ ->leftJoin('contacts', 'contacts.client_id', '=', 'clients.id')
+ ->where('clients.deleted_at', '=', null)
+ ->where('contacts.deleted_at', '=', null)
+ ->where('contacts.is_primary', '=', true)
+ ->select(
+ 'proposals.public_id',
+ 'proposals.user_id',
+ 'proposals.deleted_at',
+ 'proposals.is_deleted',
+ 'proposals.private_notes',
+ DB::raw("COALESCE(NULLIF(clients.name,''), NULLIF(CONCAT(contacts.first_name, ' ', contacts.last_name),''), NULLIF(contacts.email,'')) client_name"),
+ 'clients.user_id as client_user_id',
+ 'clients.public_id as client_public_id',
+ 'invoices.invoice_number as quote'
+ );
+
+ $this->applyFilters($query, ENTITY_PROPOSAL);
+
+ if ($filter) {
+ $query->where(function ($query) use ($filter) {
+ $query->where('clients.name', 'like', '%'.$filter.'%')
+ ->orWhere('contacts.first_name', 'like', '%'.$filter.'%')
+ ->orWhere('contacts.last_name', 'like', '%'.$filter.'%')
+ ->orWhere('contacts.email', 'like', '%'.$filter.'%')
+ ->orWhere('invoices.invoice_number', 'like', '%'.$filter.'%');
+ });
+ }
+
+ if ($userId) {
+ $query->where('proposals.user_id', '=', $userId);
+ }
+
+ return $query;
+ }
+
+ public function save($input, $proposal = false)
+ {
+ $publicId = isset($data['public_id']) ? $data['public_id'] : false;
+
+ if (! $proposal) {
+ $proposal = Proposal::createNew();
+ }
+
+ $proposal->fill($input);
+ $proposal->save();
+
+ return $proposal;
+ }
+}
diff --git a/app/Ninja/Repositories/ProposalSnippetRepository.php b/app/Ninja/Repositories/ProposalSnippetRepository.php
new file mode 100644
index 000000000000..e6b293a4a99e
--- /dev/null
+++ b/app/Ninja/Repositories/ProposalSnippetRepository.php
@@ -0,0 +1,76 @@
+get();
+ }
+
+ public function find($filter = null, $userId = false)
+ {
+ $query = DB::table('proposal_snippets')
+ ->where('proposal_snippets.account_id', '=', Auth::user()->account_id)
+ ->leftjoin('invoices', 'invoices.id', '=', 'proposal_snippets.quote_id')
+ ->leftjoin('clients', 'clients.id', '=', 'invoices.client_id')
+ ->leftJoin('contacts', 'contacts.client_id', '=', 'clients.id')
+ ->where('clients.deleted_at', '=', null)
+ ->where('contacts.deleted_at', '=', null)
+ ->where('contacts.is_primary', '=', true)
+ ->select(
+ 'proposal_snippets.name as proposal',
+ 'proposal_snippets.public_id',
+ 'proposal_snippets.user_id',
+ 'proposal_snippets.deleted_at',
+ 'proposal_snippets.is_deleted',
+ 'proposal_snippets.private_notes',
+ DB::raw("COALESCE(NULLIF(clients.name,''), NULLIF(CONCAT(contacts.first_name, ' ', contacts.last_name),''), NULLIF(contacts.email,'')) client_name"),
+ 'clients.user_id as client_user_id',
+ 'clients.public_id as client_public_id'
+ );
+
+ $this->applyFilters($query, ENTITY_PROPOSAL_SNIPPET);
+
+ if ($filter) {
+ $query->where(function ($query) use ($filter) {
+ $query->where('clients.name', 'like', '%'.$filter.'%')
+ ->orWhere('contacts.first_name', 'like', '%'.$filter.'%')
+ ->orWhere('contacts.last_name', 'like', '%'.$filter.'%')
+ ->orWhere('contacts.email', 'like', '%'.$filter.'%')
+ ->orWhere('proposal_snippets.name', 'like', '%'.$filter.'%');
+ });
+ }
+
+ if ($userId) {
+ $query->where('proposal_snippets.user_id', '=', $userId);
+ }
+
+ return $query;
+ }
+
+ public function save($input, $proposal = false)
+ {
+ $publicId = isset($data['public_id']) ? $data['public_id'] : false;
+
+ if (! $proposal) {
+ $proposal = ProposalSnippet::createNew();
+ }
+
+ $proposal->fill($input);
+ $proposal->save();
+
+ return $proposal;
+ }
+}
diff --git a/app/Ninja/Repositories/ProposalTemplateRepository.php b/app/Ninja/Repositories/ProposalTemplateRepository.php
new file mode 100644
index 000000000000..450d72f55fe5
--- /dev/null
+++ b/app/Ninja/Repositories/ProposalTemplateRepository.php
@@ -0,0 +1,76 @@
+get();
+ }
+
+ public function find($filter = null, $userId = false)
+ {
+ $query = DB::table('proposal_templates')
+ ->where('proposal_templates.account_id', '=', Auth::user()->account_id)
+ ->leftjoin('invoices', 'invoices.id', '=', 'proposal_templates.quote_id')
+ ->leftjoin('clients', 'clients.id', '=', 'invoices.client_id')
+ ->leftJoin('contacts', 'contacts.client_id', '=', 'clients.id')
+ ->where('clients.deleted_at', '=', null)
+ ->where('contacts.deleted_at', '=', null)
+ ->where('contacts.is_primary', '=', true)
+ ->select(
+ 'proposal_templates.name as proposal',
+ 'proposal_templates.public_id',
+ 'proposal_templates.user_id',
+ 'proposal_templates.deleted_at',
+ 'proposal_templates.is_deleted',
+ 'proposal_templates.private_notes',
+ DB::raw("COALESCE(NULLIF(clients.name,''), NULLIF(CONCAT(contacts.first_name, ' ', contacts.last_name),''), NULLIF(contacts.email,'')) client_name"),
+ 'clients.user_id as client_user_id',
+ 'clients.public_id as client_public_id'
+ );
+
+ $this->applyFilters($query, ENTITY_PROPOSAL_TEMPLATE);
+
+ if ($filter) {
+ $query->where(function ($query) use ($filter) {
+ $query->where('clients.name', 'like', '%'.$filter.'%')
+ ->orWhere('contacts.first_name', 'like', '%'.$filter.'%')
+ ->orWhere('contacts.last_name', 'like', '%'.$filter.'%')
+ ->orWhere('contacts.email', 'like', '%'.$filter.'%')
+ ->orWhere('proposal_templates.name', 'like', '%'.$filter.'%');
+ });
+ }
+
+ if ($userId) {
+ $query->where('proposal_templates.user_id', '=', $userId);
+ }
+
+ return $query;
+ }
+
+ public function save($input, $proposal = false)
+ {
+ $publicId = isset($data['public_id']) ? $data['public_id'] : false;
+
+ if (! $proposal) {
+ $proposal = ProposalTemplate::createNew();
+ }
+
+ $proposal->fill($input);
+ $proposal->save();
+
+ return $proposal;
+ }
+}
diff --git a/app/Services/ProposalCategoryService.php b/app/Services/ProposalCategoryService.php
new file mode 100644
index 000000000000..5fff79695831
--- /dev/null
+++ b/app/Services/ProposalCategoryService.php
@@ -0,0 +1,71 @@
+proposalCategoryRepo = $proposalCategoryRepo;
+ $this->datatableService = $datatableService;
+ }
+
+ /**
+ * @return CreditRepository
+ */
+ protected function getRepo()
+ {
+ return $this->proposalCategoryRepo;
+ }
+
+ /**
+ * @param $data
+ * @param mixed $proposalCategory
+ *
+ * @return mixed|null
+ */
+ public function save($data, $proposalCategory = false)
+ {
+ return $this->proposalCategoryRepo->save($data, $proposalCategory);
+ }
+
+ /**
+ * @param $clientPublicId
+ * @param $search
+ * @param mixed $userId
+ *
+ * @return \Illuminate\Http\JsonResponse
+ */
+ public function getDatatable($search, $userId)
+ {
+ // we don't support bulk edit and hide the client on the individual client page
+ $datatable = new ProposalCategoryDatatable();
+
+ $query = $this->proposalCategoryRepo->find($search, $userId);
+
+ return $this->datatableService->createDatatable($datatable, $query);
+ }
+}
diff --git a/app/Services/ProposalService.php b/app/Services/ProposalService.php
new file mode 100644
index 000000000000..eb7542738f13
--- /dev/null
+++ b/app/Services/ProposalService.php
@@ -0,0 +1,71 @@
+proposalRepo = $proposalRepo;
+ $this->datatableService = $datatableService;
+ }
+
+ /**
+ * @return CreditRepository
+ */
+ protected function getRepo()
+ {
+ return $this->proposalRepo;
+ }
+
+ /**
+ * @param $data
+ * @param mixed $proposal
+ *
+ * @return mixed|null
+ */
+ public function save($data, $proposal = false)
+ {
+ return $this->proposalRepo->save($data, $proposal);
+ }
+
+ /**
+ * @param $clientPublicId
+ * @param $search
+ * @param mixed $userId
+ *
+ * @return \Illuminate\Http\JsonResponse
+ */
+ public function getDatatable($search, $userId)
+ {
+ // we don't support bulk edit and hide the client on the individual client page
+ $datatable = new ProposalDatatable();
+
+ $query = $this->proposalRepo->find($search, $userId);
+
+ return $this->datatableService->createDatatable($datatable, $query);
+ }
+}
diff --git a/app/Services/ProposalSnippetService.php b/app/Services/ProposalSnippetService.php
new file mode 100644
index 000000000000..981ac8c3415c
--- /dev/null
+++ b/app/Services/ProposalSnippetService.php
@@ -0,0 +1,71 @@
+proposalSnippetRepo = $proposalSnippetRepo;
+ $this->datatableService = $datatableService;
+ }
+
+ /**
+ * @return CreditRepository
+ */
+ protected function getRepo()
+ {
+ return $this->proposalSnippetRepo;
+ }
+
+ /**
+ * @param $data
+ * @param mixed $proposalSnippet
+ *
+ * @return mixed|null
+ */
+ public function save($data, $proposalSnippet = false)
+ {
+ return $this->proposalSnippetRepo->save($data, $proposalSnippet);
+ }
+
+ /**
+ * @param $clientPublicId
+ * @param $search
+ * @param mixed $userId
+ *
+ * @return \Illuminate\Http\JsonResponse
+ */
+ public function getDatatable($search, $userId)
+ {
+ // we don't support bulk edit and hide the client on the individual client page
+ $datatable = new ProposalSnippetDatatable();
+
+ $query = $this->proposalSnippetRepo->find($search, $userId);
+
+ return $this->datatableService->createDatatable($datatable, $query);
+ }
+}
diff --git a/app/Services/ProposalTemplateService.php b/app/Services/ProposalTemplateService.php
new file mode 100644
index 000000000000..c367051b0e3b
--- /dev/null
+++ b/app/Services/ProposalTemplateService.php
@@ -0,0 +1,71 @@
+proposalTemplateRepo = $proposalTemplateRepo;
+ $this->datatableService = $datatableService;
+ }
+
+ /**
+ * @return CreditRepository
+ */
+ protected function getRepo()
+ {
+ return $this->proposalTemplateRepo;
+ }
+
+ /**
+ * @param $data
+ * @param mixed $proposalTemplate
+ *
+ * @return mixed|null
+ */
+ public function save($data, $proposalTemplate = false)
+ {
+ return $this->proposalTemplateRepo->save($data, $proposalTemplate);
+ }
+
+ /**
+ * @param $clientPublicId
+ * @param $search
+ * @param mixed $userId
+ *
+ * @return \Illuminate\Http\JsonResponse
+ */
+ public function getDatatable($search, $userId)
+ {
+ // we don't support bulk edit and hide the client on the individual client page
+ $datatable = new ProposalTemplateDatatable();
+
+ $query = $this->proposalTemplateRepo->find($search, $userId);
+
+ return $this->datatableService->createDatatable($datatable, $query);
+ }
+}
diff --git a/database/migrations/2018_01_10_073825_add_subscription_format.php b/database/migrations/2018_01_10_073825_add_subscription_format.php
index 7f1bc5bfd399..8b48211c7bf8 100644
--- a/database/migrations/2018_01_10_073825_add_subscription_format.php
+++ b/database/migrations/2018_01_10_073825_add_subscription_format.php
@@ -27,6 +27,7 @@ class AddSubscriptionFormat extends Migration
$table->unsignedInteger('user_id');
$table->timestamps();
$table->softDeletes();
+ $table->boolean('is_deleted')->default(false);
$table->string('name');
@@ -43,10 +44,12 @@ class AddSubscriptionFormat extends Migration
$table->unsignedInteger('user_id');
$table->timestamps();
$table->softDeletes();
+ $table->boolean('is_deleted')->default(false);
$table->unsignedInteger('proposal_category_id');
$table->string('name');
-
+ $table->text('private_notes');
+
$table->mediumText('html');
$table->mediumText('css');
@@ -63,6 +66,8 @@ class AddSubscriptionFormat extends Migration
$table->unsignedInteger('user_id');
$table->timestamps();
$table->softDeletes();
+ $table->boolean('is_deleted')->default(false);
+ $table->text('private_notes');
$table->string('name');
$table->text('tags');
@@ -82,9 +87,11 @@ class AddSubscriptionFormat extends Migration
$table->unsignedInteger('user_id');
$table->timestamps();
$table->softDeletes();
+ $table->boolean('is_deleted')->default(false);
$table->unsignedInteger('quote_id')->index();
$table->unsignedInteger('temlate_id')->index();
+ $table->text('private_notes');
$table->mediumText('html');
$table->mediumText('css');
diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php
index 52cb8c279e5a..ffe3f03c97bd 100644
--- a/resources/lang/en/texts.php
+++ b/resources/lang/en/texts.php
@@ -2659,8 +2659,63 @@ $LANG = array(
'tax_amount' => 'Tax Amount',
'tax_paid' => 'Tax Paid',
'none' => 'None',
+
+ 'proposal' => 'Proposal',
'proposals' => 'Proposals',
'new_proposal' => 'New Proposal',
+ 'edit_proposal' => 'Edit Proposal',
+ 'archive_proposal' => 'Archive Proposal',
+ 'delete_proposal' => 'Delete Proposal',
+ 'restored_proposal' => 'Restore Proposal',
+ 'created_proposal' => 'Successfully created proposal',
+ 'updated_proposal' => 'Successfully updated proposal',
+ 'archived_proposal' => 'Successfully archived proposal',
+ 'deleted_proposal' => 'Successfully archived proposal',
+ 'archived_proposals' => 'Successfully archived :count proposals',
+ 'deleted_proposals' => 'Successfully archived :count proposals',
+ 'restored_proposal' => 'Successfully restored proposal',
+ 'proposal_snippet' => 'Snippet',
+ 'proposal_snippets' => 'Snippets',
+ 'new_proposal_snippet' => 'New Snippet',
+ 'edit_proposal_snippet' => 'Edit Snippet',
+ 'archive_proposal_snippet' => 'Archive Snippet',
+ 'delete_proposal_snippet' => 'Delete Snippet',
+ 'restored_proposal_snippet' => 'Restore Snippet',
+ 'created_proposal_snippet' => 'Successfully created snippet',
+ 'updated_proposal_snippet' => 'Successfully updated snippet',
+ 'archived_proposal_snippet' => 'Successfully archived snippet',
+ 'deleted_proposal_snippet' => 'Successfully archived snippet',
+ 'archived_proposal_snippets' => 'Successfully archived :count snippets',
+ 'deleted_proposal_snippets' => 'Successfully archived :count snippets',
+ 'restored_proposal_snippet' => 'Successfully restored snippet',
+ 'proposal_template' => 'Template',
+ 'proposal_templates' => 'Templates',
+ 'new_proposal_template' => 'New Template',
+ 'edit_proposal_template' => 'Edit Template',
+ 'archive_proposal_template' => 'Archive Template',
+ 'delete_proposal_template' => 'Delete Template',
+ 'restored_proposal_template' => 'Restore Template',
+ 'created_proposal_template' => 'Successfully created temlate',
+ 'updated_proposal_template' => 'Successfully updated temlate',
+ 'archived_proposal_template' => 'Successfully archived temlate',
+ 'deleted_proposal_template' => 'Successfully archived temlate',
+ 'archived_proposal_templates' => 'Successfully archived :count temlates',
+ 'deleted_proposal_templates' => 'Successfully archived :count temlates',
+ 'restored_proposal_template' => 'Successfully restored temlate',
+ 'proposal_category' => 'Proposal Category',
+ 'proposal_categories' => 'Proposal Categories',
+ 'new_proposal_category' => 'New Category',
+ 'edit_proposal_category' => 'Edit Category',
+ 'archive_proposal_category' => 'Archive Category',
+ 'delete_proposal_category' => 'Delete Category',
+ 'restored_proposal_category' => 'Restore Category',
+ 'created_proposal_category' => 'Successfully created category',
+ 'updated_proposal_category' => 'Successfully updated category',
+ 'archived_proposal_category' => 'Successfully archived category',
+ 'deleted_proposal_category' => 'Successfully archived category',
+ 'archived_proposal_categories' => 'Successfully archived :count categories',
+ 'deleted_proposal_categories' => 'Successfully archived :count categories',
+ 'restored_proposal_category' => 'Successfully restored category',
);
diff --git a/resources/views/list.blade.php b/resources/views/list.blade.php
index d67e36effd16..9c9fd12a21d6 100644
--- a/resources/views/list.blade.php
+++ b/resources/views/list.blade.php
@@ -46,17 +46,40 @@
- @if (false && $entityType == ENTITY_INVOICE && auth()->user()->account->isModuleEnabled(ENTITY_RECURRING_INVOICE))
- {!! DropdownButton::normal(trans('texts.recurring'))
- ->withAttributes(['class'=>'recurringDropdown'])
+ @if ($entityType == ENTITY_PROPOSAL)
+ {!! DropdownButton::normal(trans('texts.templates'))
+ ->withAttributes(['class'=>'templatesDropdown'])
->withContents([
- ['label' => trans('texts.new_recurring_invoice'), 'url' => url('/recurring_invoices/create')],
+ ['label' => trans('texts.new_template'), 'url' => url('/proposal_templates/create')],
+ ]
+ )->split() !!}
+ {!! DropdownButton::normal(trans('texts.snippets'))
+ ->withAttributes(['class'=>'snippetsDropdown'])
+ ->withContents([
+ ['label' => trans('texts.new_snippet'), 'url' => url('/proposal_snippets/create')],
+ ]
+ )->split() !!}
+
+ @elseif ($entityType == ENTITY_PROPOSAL_SNIPPET)
+ {!! DropdownButton::normal(trans('texts.categories'))
+ ->withAttributes(['class'=>'categoriesDropdown'])
+ ->withContents([
+ ['label' => trans('texts.new_category'), 'url' => url('/proposal_categories/create')],
]
)->split() !!}
diff --git a/routes/web.php b/routes/web.php
index d2314ba2dc5e..cc0aeabdf3e1 100644
--- a/routes/web.php
+++ b/routes/web.php
@@ -207,15 +207,19 @@ Route::group(['middleware' => ['lookup:user', 'auth:user']], function () {
Route::get('proposals/create/{quote_id?}', 'ProposalController@create');
Route::resource('proposals', 'ProposalController');
+ Route::get('api/proposals', 'ProposalController@getDatatable');
Route::get('proposal_templates/create', 'ProposalTemplateController@create');
Route::resource('proposal_templates', 'ProposalTemplateController');
+ Route::get('api/proposal_templates', 'ProposalTemplateController@getDatatable');
Route::get('proposal_snippets/create', 'ProposalSnippetController@create');
Route::resource('proposal_snippets', 'ProposalSnippetController');
+ Route::get('api/proposal_snippets', 'ProposalSnippetController@getDatatable');
Route::get('proposal_categories/create', 'ProposalCategoryController@create');
- Route::resource('proposal_categories', 'ProposalController');
+ Route::resource('proposal_categories', 'ProposalCategoryController');
+ Route::get('api/proposal_categories', 'ProposalCategoryController@getDatatable');
Route::resource('payments', 'PaymentController');
Route::get('payments/create/{client_id?}/{invoice_id?}', 'PaymentController@create');