diff --git a/app/Http/Controllers/ExpenseController.php b/app/Http/Controllers/ExpenseController.php
index b1fd28e41941..7e6b254f9d01 100644
--- a/app/Http/Controllers/ExpenseController.php
+++ b/app/Http/Controllers/ExpenseController.php
@@ -99,7 +99,7 @@ class ExpenseController extends BaseController
public function edit($publicId)
{
- $expense = Expense::scope($publicId)->firstOrFail();
+ $expense = Expense::scope($publicId)->with('documents')->firstOrFail();
if(!$this->checkEditPermission($expense, $response)){
return $response;
@@ -163,7 +163,14 @@ class ExpenseController extends BaseController
*/
public function update(UpdateExpenseRequest $request)
{
- $expense = $this->expenseService->save($request->input());
+ $data = $request->input();
+ $data['documents'] = $request->file('documents');
+
+ if(!$this->checkUpdatePermission($data, $response)){
+ return $response;
+ }
+
+ $expense = $this->expenseService->save($data, true);
Session::flash('message', trans('texts.updated_expense'));
@@ -195,8 +202,7 @@ class ExpenseController extends BaseController
$expenses = Expense::scope($ids)->with('client')->get();
$clientPublicId = null;
$currencyId = null;
- $data = [];
-
+
// Validate that either all expenses do not have a client or if there is a client, it is the same client
foreach ($expenses as $expense)
{
@@ -220,19 +226,11 @@ class ExpenseController extends BaseController
Session::flash('error', trans('texts.expense_error_invoiced'));
return Redirect::to('expenses');
}
-
- $account = Auth::user()->account;
- $data[] = [
- 'publicId' => $expense->public_id,
- 'description' => $expense->public_notes,
- 'qty' => 1,
- 'cost' => $expense->present()->converted_amount,
- ];
}
return Redirect::to("invoices/create/{$clientPublicId}")
->with('expenseCurrencyId', $currencyId)
- ->with('expenses', $data);
+ ->with('expenses', $ids);
break;
default:
diff --git a/app/Http/Controllers/InvoiceController.php b/app/Http/Controllers/InvoiceController.php
index 70b8e66fa4f2..93d4a2a2432a 100644
--- a/app/Http/Controllers/InvoiceController.php
+++ b/app/Http/Controllers/InvoiceController.php
@@ -17,6 +17,7 @@ use App\Models\Invoice;
use App\Models\Client;
use App\Models\Account;
use App\Models\Product;
+use App\Models\Expense;
use App\Models\TaxRate;
use App\Models\InvoiceDesign;
use App\Models\Activity;
@@ -91,7 +92,7 @@ class InvoiceController extends BaseController
{
$account = Auth::user()->account;
$invoice = Invoice::scope($publicId)
- ->with('invitations', 'account.country', 'client.contacts', 'client.country', 'invoice_items', 'documents', 'payments')
+ ->with('invitations', 'account.country', 'client.contacts', 'client.country', 'invoice_items', 'documents', 'expenses', 'expenses.documents', 'payments')
->withTrashed()
->firstOrFail();
@@ -241,6 +242,12 @@ class InvoiceController extends BaseController
$invoice = $account->createInvoice($entityType, $clientId);
$invoice->public_id = 0;
+
+ $invoice->expenses = Expense::scope([2])->with('documents')->get();
+ if(Session::get('expenses')){
+ $invoice->expenses = Expense::scope(Session::get('expenses'))->with('documents')->get();
+ }
+
$clients = Client::scope()->with('contacts', 'country')->orderBy('name');
if(!Auth::user()->hasPermission('view_all')){
@@ -352,7 +359,6 @@ class InvoiceController extends BaseController
'recurringDueDateHelp' => $recurringDueDateHelp,
'invoiceLabels' => Auth::user()->account->getInvoiceLabels(),
'tasks' => Session::get('tasks') ? json_encode(Session::get('tasks')) : null,
- 'expenses' => Session::get('expenses') ? json_encode(Session::get('expenses')) : null,
'expenseCurrencyId' => Session::get('expenseCurrencyId') ?: null,
];
@@ -537,7 +543,7 @@ class InvoiceController extends BaseController
public function invoiceHistory($publicId)
{
$invoice = Invoice::withTrashed()->scope($publicId)->firstOrFail();
- $invoice->load('user', 'invoice_items', 'documents', 'account.country', 'client.contacts', 'client.country');
+ $invoice->load('user', 'invoice_items', 'documents', 'expenses', 'expenses.documents', 'account.country', 'client.contacts', 'client.country');
$invoice->invoice_date = Utils::fromSqlDate($invoice->invoice_date);
$invoice->due_date = Utils::fromSqlDate($invoice->due_date);
$invoice->is_pro = Auth::user()->isPro();
diff --git a/app/Http/Controllers/PublicClientController.php b/app/Http/Controllers/PublicClientController.php
index 2d7f980af2a8..755dbb1ca86a 100644
--- a/app/Http/Controllers/PublicClientController.php
+++ b/app/Http/Controllers/PublicClientController.php
@@ -179,12 +179,6 @@ class PublicClientController extends BaseController
return $paymentTypes;
}
-
- protected function humanFilesize($bytes, $decimals = 2) {
- $size = array('B','kB','MB','GB','TB','PB','EB','ZB','YB');
- $factor = floor((strlen($bytes) - 1) / 3);
- return sprintf("%.{$decimals}f", $bytes / pow(1024, $factor)) . @$size[$factor];
- }
public function download($invitationKey)
{
@@ -473,7 +467,13 @@ class PublicClientController extends BaseController
}
protected function getInvoiceZipDocuments($invoice, &$size=0){
- $documents = $invoice->documents->sortBy('size');
+ $documents = $invoice->documents;
+
+ foreach($invoice->expenses as $expense){
+ $documents = $documents->merge($expense->documents);
+ }
+
+ $documents = $documents->sortBy('size');
$size = 0;
$maxSize = MAX_ZIP_DOCUMENTS_SIZE * 1000;
@@ -520,10 +520,6 @@ class PublicClientController extends BaseController
$invoice = $invitation->invoice;
- if(!count($invoice->documents)){
- return Response::view('error', array('error'=>'No documents'), 404);
- }
-
$toZip = $this->getInvoiceZipDocuments($invoice);
if(!count($toZip)){
diff --git a/app/Models/Document.php b/app/Models/Document.php
index 905ea09235c9..0d822ea4d18e 100644
--- a/app/Models/Document.php
+++ b/app/Models/Document.php
@@ -200,8 +200,10 @@ class Document extends EntityModel
public function toArray()
{
$array = parent::toArray();
- $array['url'] = $this->getUrl();
- $array['preview_url'] = $this->getPreviewUrl();
+
+ if(empty($this->visible) || in_array('url', $this->visible))$array['url'] = $this->getUrl();
+ if(empty($this->visible) || in_array('preview_url', $this->visible))$array['preview_url'] = $this->getPreviewUrl();
+
return $array;
}
diff --git a/app/Models/Expense.php b/app/Models/Expense.php
index 2d1b8041d7ee..4da74ca0c4b7 100644
--- a/app/Models/Expense.php
+++ b/app/Models/Expense.php
@@ -53,6 +53,11 @@ class Expense extends EntityModel
return $this->belongsTo('App\Models\Invoice')->withTrashed();
}
+ public function documents()
+ {
+ return $this->hasMany('App\Models\Document')->orderBy('id');
+ }
+
public function getName()
{
if($this->expense_number)
@@ -80,6 +85,20 @@ class Expense extends EntityModel
{
return $this->invoice_currency_id != $this->expense_currency_id;
}
+
+ public function convertedAmount()
+ {
+ return round($this->amount * $this->exchange_rate, 2);
+ }
+
+ public function toArray()
+ {
+ $array = parent::toArray();
+
+ if(empty($this->visible) || in_array('converted_amount', $this->visible))$array['previewconverted_amount_url'] = $this->convertedAmount();
+
+ return $array;
+ }
}
Expense::creating(function ($expense) {
diff --git a/app/Models/Invoice.php b/app/Models/Invoice.php
index 42706a60b821..f1e943339f7e 100644
--- a/app/Models/Invoice.php
+++ b/app/Models/Invoice.php
@@ -2,6 +2,7 @@
use Utils;
use DateTime;
+use URL;
use Illuminate\Database\Eloquent\SoftDeletes;
use Laracasts\Presenter\PresentableTrait;
use App\Models\BalanceAffecting;
@@ -391,6 +392,7 @@ class Invoice extends EntityModel implements BalanceAffecting
'balance',
'invoice_items',
'documents',
+ 'expenses',
'client',
'tax_name',
'tax_rate',
@@ -463,6 +465,7 @@ class Invoice extends EntityModel implements BalanceAffecting
'custom_invoice_text_label2',
'custom_invoice_item_label1',
'custom_invoice_item_label2',
+ 'invoice_embed_documents'
]);
foreach ($this->invoice_items as $invoiceItem) {
@@ -487,6 +490,26 @@ class Invoice extends EntityModel implements BalanceAffecting
]);
}
+ foreach ($this->documents as $document) {
+ $document->setVisible([
+ 'public_id',
+ 'name',
+ ]);
+ }
+
+ foreach ($this->expenses as $expense) {
+ $expense->setVisible([
+ 'documents',
+ ]);
+
+ foreach ($expense->documents as $document) {
+ $document->setVisible([
+ 'public_id',
+ 'name',
+ ]);
+ }
+ }
+
return $this;
}
@@ -867,6 +890,18 @@ class Invoice extends EntityModel implements BalanceAffecting
return $taxes;
}
+
+ public function hasDocuments(){
+ if(count($this->documents))return true;
+ return $this->hasExpenseDocuments();
+ }
+
+ public function hasExpenseDocuments(){
+ foreach($this->expenses as $expense){
+ if(count($expense->documents))return true;
+ }
+ return false;
+ }
}
Invoice::creating(function ($invoice) {
diff --git a/app/Ninja/Mailers/ContactMailer.php b/app/Ninja/Mailers/ContactMailer.php
index 7f96efb7da87..229895067e42 100644
--- a/app/Ninja/Mailers/ContactMailer.php
+++ b/app/Ninja/Mailers/ContactMailer.php
@@ -63,8 +63,14 @@ class ContactMailer extends Mailer
}
$documentStrings = array();
- if ($account->document_email_attachment && !empty($invoice->documents)) {
- $documents = $invoice->documents->sortBy('size');
+ if ($account->document_email_attachment && $invoice->hasDocuments()) {
+ $documents = $invoice->documents;
+
+ foreach($invoice->expenses as $expense){
+ $documents = $documents->merge($expense->documents);
+ }
+
+ $documents = $documents->sortBy('size');
$size = 0;
$maxSize = MAX_EMAIL_DOCUMENTS_SIZE * 1000;
@@ -285,11 +291,16 @@ class ContactMailer extends Mailer
$passwordHTML = isset($data['password'])?'
'.trans('texts.password').': '.$data['password'].'
':false;
$documentsHTML = '';
- if($account->isPro() && count($invoice->documents)){
+ if($account->isPro() && $invoice->hasDocuments()){
$documentsHTML .= trans('texts.email_documents_header').'
';
}
diff --git a/app/Ninja/Presenters/ExpensePresenter.php b/app/Ninja/Presenters/ExpensePresenter.php
index 6b66080ded85..1980480a2f53 100644
--- a/app/Ninja/Presenters/ExpensePresenter.php
+++ b/app/Ninja/Presenters/ExpensePresenter.php
@@ -16,14 +16,9 @@ class ExpensePresenter extends Presenter {
return Utils::fromSqlDate($this->entity->expense_date);
}
- public function converted_amount()
- {
- return round($this->entity->amount * $this->entity->exchange_rate, 2);
- }
-
public function invoiced_amount()
{
- return $this->entity->invoice_id ? $this->converted_amount() : 0;
+ return $this->entity->invoice_id ? $this->entity->convertedAmount() : 0;
}
public function link()
diff --git a/app/Ninja/Repositories/DocumentRepository.php b/app/Ninja/Repositories/DocumentRepository.php
index 78be3bf78655..6342fe8eab84 100644
--- a/app/Ninja/Repositories/DocumentRepository.php
+++ b/app/Ninja/Repositories/DocumentRepository.php
@@ -59,7 +59,7 @@ class DocumentRepository extends BaseRepository
public function upload($uploaded, &$doc_array=null)
{
- $extension = strtolower($uploaded->extension());
+ $extension = strtolower($uploaded->getClientOriginalExtension());
if(empty(Document::$types[$extension]) && !empty(Document::$extraExtensions[$extension])){
$documentType = Document::$extraExtensions[$extension];
}
@@ -68,7 +68,7 @@ class DocumentRepository extends BaseRepository
}
if(empty(Document::$types[$documentType])){
- return 'Unsupported extension';
+ return 'Unsupported file type';
}
$documentTypeData = Document::$types[$documentType];
@@ -180,10 +180,14 @@ class DocumentRepository extends BaseRepository
public function getClientDatatable($contactId, $entityType, $search)
{
- $query = DB::table('invitations')
+
+ $query = DB::table('invitations')
->join('accounts', 'accounts.id', '=', 'invitations.account_id')
->join('invoices', 'invoices.id', '=', 'invitations.invoice_id')
- ->join('documents', 'documents.invoice_id', '=', 'invitations.invoice_id')
+ ->join('expenses', 'expenses.invoice_id', '=', 'invitations.invoice_id')
+ ->join('documents', function($join){
+ $join->on('documents.invoice_id', '=', 'invitations.invoice_id')->orOn('documents.expense_id', '=', 'expenses.id');
+ })
->join('clients', 'clients.id', '=', 'invoices.client_id')
->where('invitations.contact_id', '=', $contactId)
->where('invitations.deleted_at', '=', null)
diff --git a/app/Ninja/Repositories/ExpenseRepository.php b/app/Ninja/Repositories/ExpenseRepository.php
index 49988435e598..db39857bcf4b 100644
--- a/app/Ninja/Repositories/ExpenseRepository.php
+++ b/app/Ninja/Repositories/ExpenseRepository.php
@@ -4,17 +4,25 @@ use DB;
use Utils;
use App\Models\Expense;
use App\Models\Vendor;
+use App\Models\Document;
use App\Ninja\Repositories\BaseRepository;
use Session;
class ExpenseRepository extends BaseRepository
{
+ protected $documentRepo;
+
// Expenses
public function getClassName()
{
return 'App\Models\Expense';
}
+ public function __construct(DocumentRepository $documentRepo)
+ {
+ $this->documentRepo = $documentRepo;
+ }
+
public function all()
{
return Expense::scope()
@@ -113,7 +121,7 @@ class ExpenseRepository extends BaseRepository
return $query;
}
- public function save($input)
+ public function save($input, $checkSubPermissions=false)
{
$publicId = isset($input['public_id']) ? $input['public_id'] : false;
@@ -145,6 +153,45 @@ class ExpenseRepository extends BaseRepository
$expense->exchange_rate = round($rate, 4);
$expense->amount = round(Utils::parseFloat($input['amount']), 2);
+ // Documents
+ $document_ids = !empty($input['document_ids'])?array_map('intval', $input['document_ids']):array();;
+ foreach ($document_ids as $document_id){
+ $document = Document::scope($document_id)->first();
+ if($document && !$checkSubPermissions || $document->canEdit()){
+ $document->invoice_id = null;
+ $document->expense_id = $expense->id;
+ $document->save();
+ }
+ }
+
+ if(!empty($input['documents']) && Document::canCreate()){
+ // Fallback upload
+ $doc_errors = array();
+ foreach($input['documents'] as $upload){
+ $result = $this->documentRepo->upload($upload);
+ if(is_string($result)){
+ $doc_errors[] = $result;
+ }
+ else{
+ $result->expense_id = $expense->id;
+ $result->save();
+ $document_ids[] = $result->public_id;
+ }
+ }
+ if(!empty($doc_errors)){
+ Session::flash('error', implode('
',array_map('htmlentities',$doc_errors)));
+ }
+ }
+
+ foreach ($expense->documents as $document){
+ if(!in_array($document->public_id, $document_ids)){
+ // Removed
+ if(!$checkSubPermissions || $document->canEdit()){
+ $document->delete();
+ }
+ }
+ }
+
$expense->save();
return $expense;
diff --git a/app/Ninja/Repositories/InvoiceRepository.php b/app/Ninja/Repositories/InvoiceRepository.php
index a927cb068c0d..686f9573d12d 100644
--- a/app/Ninja/Repositories/InvoiceRepository.php
+++ b/app/Ninja/Repositories/InvoiceRepository.php
@@ -415,6 +415,7 @@ class InvoiceRepository extends BaseRepository
}
$document->invoice_id = $invoice->id;
+ $document->expense_id = null;
$document->save();
}
}
diff --git a/app/Services/ExpenseService.php b/app/Services/ExpenseService.php
index 2fc2afbc84b1..b574aa9983b8 100644
--- a/app/Services/ExpenseService.php
+++ b/app/Services/ExpenseService.php
@@ -28,7 +28,7 @@ class ExpenseService extends BaseService
return $this->expenseRepo;
}
- public function save($data)
+ public function save($data, $checkSubPermissions=false)
{
if (isset($data['client_id']) && $data['client_id']) {
$data['client_id'] = Client::getPrivateId($data['client_id']);
@@ -38,7 +38,7 @@ class ExpenseService extends BaseService
$data['vendor_id'] = Vendor::getPrivateId($data['vendor_id']);
}
- return $this->expenseRepo->save($data);
+ return $this->expenseRepo->save($data, $checkSubPermissions);
}
public function getDatatable($search)
diff --git a/public/built.js b/public/built.js
index 0b46c5659d41..a883d6a37219 100644
--- a/public/built.js
+++ b/public/built.js
@@ -31396,13 +31396,19 @@ NINJA.invoiceLines = function(invoice) {
}
NINJA.invoiceDocuments = function(invoice) {
- if(!invoice.documents || !invoice.account.invoice_embed_documents)return[];
+ if(!invoice.account.invoice_embed_documents)return[];
var stack = [];
var stackItem = null;
var j = 0;
- for (var i = 0; i < invoice.documents.length; i++) {
- var document = invoice.documents[i];
+ for (var i = 0; i < invoice.documents.length; i++)addDoc(invoice.documents[i]);
+
+ for (var i = 0; i < invoice.expenses.length; i++) {
+ var expense = invoice.expenses[i];
+ for (var i = 0; i < expense.documents.length; i++)addDoc(expense.documents[i]);
+ }
+
+ function addDoc(document){
var path = document.base64;
if(!path)path = 'docs/'+document.public_id+'/'+document.name;
@@ -31415,9 +31421,9 @@ NINJA.invoiceDocuments = function(invoice) {
stackItem.columns.push({stack:[{image:path,style:'invoiceDocument',fit:[150,150]}], width:175})
j++;
}
- }
+ }
- return {stack:stack};
+ return stack.length?{stack:stack}:[];
}
NINJA.subtotals = function(invoice, hideBalance)
diff --git a/public/css/built.css b/public/css/built.css
index 7278b9ed4236..559bd1a22c17 100644
--- a/public/css/built.css
+++ b/public/css/built.css
@@ -3193,10 +3193,21 @@ div.panel-body div.panel-body {
}
/* Attached Documents */
-.dropzone {
+#document-upload {
border:1px solid #ebe7e7;
background:#f9f9f9 !important;
border-radius:3px;
+ padding:20px;
+}
+
+.invoice-table #document-upload{
+ max-width:560px;
+}
+
+#document-upload .dropzone{
+ background:none;
+ border:none;
+ padding:0;
}
.dropzone .dz-preview.dz-image-preview{
@@ -3204,8 +3215,6 @@ div.panel-body div.panel-body {
}
.dropzone .dz-preview .dz-image{
- width:119px;
- height:119px;
border-radius:5px!important;
}
diff --git a/public/css/style.css b/public/css/style.css
index 1499d26f573a..6514f82ae975 100644
--- a/public/css/style.css
+++ b/public/css/style.css
@@ -1064,10 +1064,21 @@ div.panel-body div.panel-body {
}
/* Attached Documents */
-.dropzone {
+#document-upload {
border:1px solid #ebe7e7;
background:#f9f9f9 !important;
border-radius:3px;
+ padding:20px;
+}
+
+.invoice-table #document-upload{
+ max-width:560px;
+}
+
+#document-upload .dropzone{
+ background:none;
+ border:none;
+ padding:0;
}
.dropzone .dz-preview.dz-image-preview{
@@ -1075,8 +1086,6 @@ div.panel-body div.panel-body {
}
.dropzone .dz-preview .dz-image{
- width:119px;
- height:119px;
border-radius:5px!important;
}
diff --git a/public/js/pdf.pdfmake.js b/public/js/pdf.pdfmake.js
index 29da491112da..9f38f6e42f3e 100644
--- a/public/js/pdf.pdfmake.js
+++ b/public/js/pdf.pdfmake.js
@@ -404,13 +404,19 @@ NINJA.invoiceLines = function(invoice) {
}
NINJA.invoiceDocuments = function(invoice) {
- if(!invoice.documents || !invoice.account.invoice_embed_documents)return[];
+ if(!invoice.account.invoice_embed_documents)return[];
var stack = [];
var stackItem = null;
var j = 0;
- for (var i = 0; i < invoice.documents.length; i++) {
- var document = invoice.documents[i];
+ for (var i = 0; i < invoice.documents.length; i++)addDoc(invoice.documents[i]);
+
+ for (var i = 0; i < invoice.expenses.length; i++) {
+ var expense = invoice.expenses[i];
+ for (var i = 0; i < expense.documents.length; i++)addDoc(expense.documents[i]);
+ }
+
+ function addDoc(document){
var path = document.base64;
if(!path)path = 'docs/'+document.public_id+'/'+document.name;
@@ -423,9 +429,9 @@ NINJA.invoiceDocuments = function(invoice) {
stackItem.columns.push({stack:[{image:path,style:'invoiceDocument',fit:[150,150]}], width:175})
j++;
}
- }
+ }
- return {stack:stack};
+ return stack.length?{stack:stack}:[];
}
NINJA.subtotals = function(invoice, hideBalance)
diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php
index 4db617943e20..3c7b2dcacc42 100644
--- a/resources/lang/en/texts.php
+++ b/resources/lang/en/texts.php
@@ -1103,11 +1103,13 @@ $LANG = array(
'email_documents_example_1' => 'Widgets Receipt.pdf',
'email_documents_example_2' => 'Final Deliverable.zip',
'invoice_documents' => 'Attached Documents',
+ 'expense_documents' => 'Attached Documents',
'document_upload_message' => 'Drop files here or click to upload.',
'invoice_embed_documents' => 'Embed Documents',
'invoice_embed_documents_help' => 'Include attached images in the invoice.',
'document_email_attachment' => 'Attach Documents',
'download_documents' => 'Download Documents (:size)',
+ 'documents_from_expenses' => 'From Expenses:',
'documents' => 'Documents',
'document_date' => 'Document Date',
'document_size' => 'Size',
diff --git a/resources/views/expenses/edit.blade.php b/resources/views/expenses/edit.blade.php
index 589c8d8cda8f..5aaae895fbfa 100644
--- a/resources/views/expenses/edit.blade.php
+++ b/resources/views/expenses/edit.blade.php
@@ -105,6 +105,27 @@
+ @if ($account->isPro())
+
+
{{trans('texts.expense_documents')}}
+
+
+ @endif
@@ -122,6 +143,7 @@
{!! Former::close() !!}
@stop
\ No newline at end of file
diff --git a/resources/views/invoices/edit.blade.php b/resources/views/invoices/edit.blade.php
index a383a1b0cb32..c6aaa7230309 100644
--- a/resources/views/invoices/edit.blade.php
+++ b/resources/views/invoices/edit.blade.php
@@ -38,9 +38,8 @@
{!! Former::open($url)
->method($method)
- ->addClass('warn-on-exit')
+ ->addClass('warn-on-exit main-form')
->autocomplete('off')
- ->attributes(array('enctype'=>'multipart/form-data'))
->onsubmit('return onFormSubmit(event)')
->rules(array(
'client' => 'required',
@@ -307,17 +306,27 @@
@if ($account->isPro())
-
-
-
-
-
-
-
-
-
+
+
+ @if ($invoice->hasExpenseDocuments())
+
{{trans('texts.documents_from_expenses')}}
+ @foreach($invoice->expenses as $expense)
+ @foreach($expense->documents as $document)
+
{{$document->name}}
+ @endforeach
+ @endforeach
+ @endif
@endif
@@ -778,24 +787,24 @@
model.invoice().has_tasks(true);
@endif
- @if (isset($expenses) && $expenses)
+ if(model.invoice().expenses() && !model.invoice().public_id()){
model.expense_currency_id({{ $expenseCurrencyId }});
// move the blank invoice line item to the end
var blank = model.invoice().invoice_items.pop();
- var expenses = {!! $expenses !!};
+ var expenses = model.invoice().expenses();
for (var i=0; i
account->isPro())
+ $('.main-form').submit(function(){
+ if($('#document-upload .dropzone .fallback input').val())$(this).attr('enctype', 'multipart/form-data')
+ else $(this).removeAttr('enctype')
+ })
+
// Initialize document upload
- dropzone = new Dropzone('#document-upload', {
+ dropzone = new Dropzone('#document-upload .dropzone', {
url:{!! json_encode(url('document')) !!},
params:{
_token:"{{ Session::getToken() }}"
@@ -1360,6 +1374,13 @@
@endif
@endforeach
+ @foreach ($invoice->expenses as $expense)
+ @foreach ($expense->documents as $document)
+ @if($document->isPDFEmbeddable())
+
+ @endif
+ @endforeach
+ @endforeach
@endif
@stop
diff --git a/resources/views/invoices/history.blade.php b/resources/views/invoices/history.blade.php
index 256f541f02e0..baa5c925aadb 100644
--- a/resources/views/invoices/history.blade.php
+++ b/resources/views/invoices/history.blade.php
@@ -57,11 +57,18 @@
@include('invoices.pdf', ['account' => Auth::user()->account, 'pdfHeight' => 800])
- @if (Auth::user()->account->isPro() && Auth::user()->account->invoice_embed_documents)
+ @if (Utils::isPro() && $invoice->account->invoice_embed_documents)
@foreach ($invoice->documents as $document)
@if($document->isPDFEmbeddable())
@endif
@endforeach
+ @foreach ($invoice->expenses as $expense)
+ @foreach ($expense->documents as $document)
+ @if($document->isPDFEmbeddable())
+
+ @endif
+ @endforeach
+ @endforeach
@endif
@stop
\ No newline at end of file
diff --git a/resources/views/invoices/knockout.blade.php b/resources/views/invoices/knockout.blade.php
index 886eb985512e..9db13abe1910 100644
--- a/resources/views/invoices/knockout.blade.php
+++ b/resources/views/invoices/knockout.blade.php
@@ -227,6 +227,7 @@ function InvoiceModel(data) {
self.invoice_status_id = ko.observable(0);
self.invoice_items = ko.observableArray();
self.documents = ko.observableArray();
+ self.expenses = ko.observableArray();
self.amount = ko.observable(0);
self.balance = ko.observable(0);
self.invoice_design_id = ko.observable(1);
@@ -257,6 +258,11 @@ function InvoiceModel(data) {
return new DocumentModel(options.data);
}
},
+ 'expenses': {
+ create: function(options) {
+ return new ExpenseModel(options.data);
+ }
+ },
'tax': {
create: function(options) {
return new TaxRateModel(options.data);
@@ -846,6 +852,28 @@ function DocumentModel(data) {
self.update(data);
}
}
+
+var ExpenseModel = function(data) {
+ var self = this;
+
+ self.mapping = {
+ 'documents': {
+ create: function(options) {
+ return new DocumentModel(options.data);
+ }
+ }
+ }
+
+ self.description = ko.observable('');
+ self.qty = ko.observable(0);
+ self.public_id = ko.observable(0);
+ self.amount = ko.observable();
+ self.converted_amount = ko.observable();
+
+ if (data) {
+ ko.mapping.fromJS(data, self.mapping, this);
+ }
+};
/* Custom binding for product key typeahead */
ko.bindingHandlers.typeahead = {
diff --git a/resources/views/invoices/view.blade.php b/resources/views/invoices/view.blade.php
index bad07e9e7321..08f5cd9827cd 100644
--- a/resources/views/invoices/view.blade.php
+++ b/resources/views/invoices/view.blade.php
@@ -49,13 +49,18 @@
@endif
- @if ($account->isPro() && count($invoice->documents))
+ @if ($account->isPro() && $invoice->hasDocuments())
{{ trans('texts.documents_header') }}
@endif
@@ -63,9 +68,16 @@
@if ($account->isPro() && $account->invoice_embed_documents)
@foreach ($invoice->documents as $document)
@if($document->isPDFEmbeddable())
-
+
@endif
@endforeach
+ @foreach ($invoice->expenses as $expense)
+ @foreach ($expense->documents as $document)
+ @if($document->isPDFEmbeddable())
+
+ @endif
+ @endforeach
+ @endforeach
@endif