diff --git a/app/Http/Controllers/ReportController.php b/app/Http/Controllers/ReportController.php index f0ab3fa7ef30..0a2e7500788f 100644 --- a/app/Http/Controllers/ReportController.php +++ b/app/Http/Controllers/ReportController.php @@ -72,6 +72,7 @@ class ReportController extends BaseController 'activity', 'aging', 'client', + 'document', 'expense', 'invoice', 'payment', @@ -98,6 +99,8 @@ class ReportController extends BaseController 'date_field' => $dateField, 'invoice_status' => request()->invoice_status, 'group_dates_by' => request()->group_dates_by, + 'document_filter' => request()->document_filter, + 'export_format' => $format, ]; $report = new $reportClass($startDate, $endDate, $isExport, $options); if (Input::get('report_type')) { @@ -138,8 +141,8 @@ class ReportController extends BaseController $filename = "{$params['startDate']}-{$params['endDate']}_invoiceninja-".strtolower(Utils::normalizeChars(trans("texts.$reportType")))."-report"; - $formats = ['csv', 'pdf', 'xlsx']; - if(!in_array($format, $formats)) { + $formats = ['csv', 'pdf', 'xlsx', 'zip']; + if (! in_array($format, $formats)) { throw new \Exception("Invalid format request to export report"); } diff --git a/app/Jobs/DownloadInvoices.php b/app/Jobs/DownloadInvoices.php index 651d0012837c..4304e3cc2170 100644 --- a/app/Jobs/DownloadInvoices.php +++ b/app/Jobs/DownloadInvoices.php @@ -46,7 +46,7 @@ class DownloadInvoices extends Job */ public function handle(UserMailer $userMailer) { - $zip = Archive::instance_by_useragent(date('Y-m-d') . '-Invoice_PDFs'); + $zip = Archive::instance_by_useragent(date('Y-m-d') . '_' . str_replace(' ', '_', trans('texts.invoice_pdfs'))); foreach ($this->invoices as $invoice) { $zip->add_file($invoice->getFileName(), $invoice->getPDFString()); diff --git a/app/Ninja/Presenters/EntityPresenter.php b/app/Ninja/Presenters/EntityPresenter.php index 17f08a323cc1..3bc6ab03096e 100644 --- a/app/Ninja/Presenters/EntityPresenter.php +++ b/app/Ninja/Presenters/EntityPresenter.php @@ -84,7 +84,7 @@ class EntityPresenter extends Presenter $entity = $this->entity; $entityType = $entity->getEntityType(); - return sprintf('%s: %s', trans('texts.' . $entityType), $entity->getDisplayName()); + return sprintf('%s %s', trans('texts.' . $entityType), $entity->getDisplayName()); } public function calendarEvent($subColors = false) diff --git a/app/Ninja/Reports/ExpenseReport.php b/app/Ninja/Reports/ExpenseReport.php index 10a4220e1ce8..505db8e7f170 100644 --- a/app/Ninja/Reports/ExpenseReport.php +++ b/app/Ninja/Reports/ExpenseReport.php @@ -2,6 +2,7 @@ namespace App\Ninja\Reports; +use Barracuda\ArchiveStream\Archive; use App\Models\Expense; use Auth; use Utils; @@ -19,6 +20,12 @@ class ExpenseReport extends AbstractReport public function run() { $account = Auth::user()->account; + $exportFormat = $this->options['export_format']; + $with = ['client.contacts', 'vendor']; + + if ($exportFormat == 'zip') { + $with[] = ['documents']; + } $expenses = Expense::scope() ->orderBy('expense_date', 'desc') @@ -27,6 +34,19 @@ class ExpenseReport extends AbstractReport ->where('expense_date', '>=', $this->startDate) ->where('expense_date', '<=', $this->endDate); + if ($this->isExport && $exportFormat == 'zip') { + $zip = Archive::instance_by_useragent(date('Y-m-d') . '_' . str_replace(' ', '_', trans('texts.expense_documents'))); + foreach ($expenses->get() as $expense) { + foreach ($expense->documents as $document) { + $name = sprintf('%s_%s_%s_%s', date('Y-m-d'), trans('texts.expense'), $expense->public_id, $document->name); + $name = str_replace(' ', '_', $name); + $zip->add_file($name, $document->getRaw()); + } + } + $zip->finish(); + exit; + } + foreach ($expenses->get() as $expense) { $amount = $expense->amountWithTax(); diff --git a/app/Ninja/Reports/InvoiceReport.php b/app/Ninja/Reports/InvoiceReport.php index 1da6e8c5ef25..2341ae6f2de7 100644 --- a/app/Ninja/Reports/InvoiceReport.php +++ b/app/Ninja/Reports/InvoiceReport.php @@ -4,6 +4,7 @@ namespace App\Ninja\Reports; use App\Models\Client; use Auth; +use Barracuda\ArchiveStream\Archive; class InvoiceReport extends AbstractReport { @@ -22,6 +23,7 @@ class InvoiceReport extends AbstractReport { $account = Auth::user()->account; $status = $this->options['invoice_status']; + $exportFormat = $this->options['export_format']; $clients = Client::scope() ->orderBy('name') @@ -44,6 +46,21 @@ class InvoiceReport extends AbstractReport }, 'invoice_items']); }]); + + if ($this->isExport && $exportFormat == 'zip') { + $zip = Archive::instance_by_useragent(date('Y-m-d') . '_' . str_replace(' ', '_', trans('texts.invoice_documents'))); + foreach ($clients->get() as $client) { + foreach ($client->invoices as $invoice) { + foreach ($invoice->documents as $document) { + $name = sprintf('%s_%s_%s', date('Y-m-d'), $invoice->present()->titledName, $document->name); + $zip->add_file($name, $document->getRaw()); + } + } + } + $zip->finish(); + exit; + } + foreach ($clients->get() as $client) { foreach ($client->invoices as $invoice) { $payments = count($invoice->payments) ? $invoice->payments : [false]; diff --git a/app/Ninja/Reports/QuoteReport.php b/app/Ninja/Reports/QuoteReport.php index e53f6e0fb8fe..876268c45a3f 100644 --- a/app/Ninja/Reports/QuoteReport.php +++ b/app/Ninja/Reports/QuoteReport.php @@ -4,6 +4,7 @@ namespace App\Ninja\Reports; use App\Models\Client; use Auth; +use Barracuda\ArchiveStream\Archive; class QuoteReport extends AbstractReport { @@ -19,6 +20,7 @@ class QuoteReport extends AbstractReport { $account = Auth::user()->account; $status = $this->options['invoice_status']; + $exportFormat = $this->options['export_format']; $clients = Client::scope() ->orderBy('name') @@ -35,6 +37,21 @@ class QuoteReport extends AbstractReport ->with(['invoice_items']); }]); + if ($this->isExport && $exportFormat == 'zip') { + $zip = Archive::instance_by_useragent(date('Y-m-d') . '_' . str_replace(' ', '_', trans('texts.quote_documents'))); + foreach ($clients->get() as $client) { + foreach ($client->invoices as $invoice) { + foreach ($invoice->documents as $document) { + $name = sprintf('%s_%s_%s', date('Y-m-d'), $invoice->present()->titledName, $document->name); + $name = str_replace(' ', '_', $name); + $zip->add_file($name, $document->getRaw()); + } + } + } + $zip->finish(); + exit; + } + foreach ($clients->get() as $client) { foreach ($client->invoices as $invoice) { $this->data[] = [ @@ -46,7 +63,7 @@ class QuoteReport extends AbstractReport ]; $this->addToTotals($client->currency_id, 'amount', $invoice->amount); - } + } } } } diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php index 43fb7e45a474..ce08e7d12b6e 100644 --- a/resources/lang/en/texts.php +++ b/resources/lang/en/texts.php @@ -1099,8 +1099,9 @@ $LANG = array( 'email_documents_header' => 'Documents:', 'email_documents_example_1' => 'Widgets Receipt.pdf', 'email_documents_example_2' => 'Final Deliverable.zip', - 'invoice_documents' => 'Documents', - 'expense_documents' => 'Attached Documents', + 'quote_documents' => 'Quote Documents', + 'invoice_documents' => 'Invoice Documents', + 'expense_documents' => 'Expense Documents', 'invoice_embed_documents' => 'Embed Documents', 'invoice_embed_documents_help' => 'Include attached images in the invoice.', 'document_email_attachment' => 'Attach Documents', @@ -2488,6 +2489,9 @@ $LANG = array( 'task_rate' => 'Task Rate', 'task_rate_help' => 'Set the default rate for invoiced tasks.', 'past_due' => 'Past Due', + 'document' => 'Document', + 'invoice_or_expense' => 'Invoice/Expense', + 'invoice_pdfs' => 'Invoice PDFs', ); diff --git a/resources/views/invoices/edit.blade.php b/resources/views/invoices/edit.blade.php index b17375bfab2e..1b68ce67c397 100644 --- a/resources/views/invoices/edit.blade.php +++ b/resources/views/invoices/edit.blade.php @@ -288,7 +288,7 @@
  • {{ trans("texts.footer") }}
  • @if ($account->hasFeature(FEATURE_DOCUMENTS))
  • - {{ trans("texts.invoice_documents") }} + {{ trans("texts.documents") }} @if ($count = ($invoice->countDocuments($expenses))) ({{ $count }}) @endif diff --git a/resources/views/reports/chart_builder.blade.php b/resources/views/reports/chart_builder.blade.php index 5ff95c8ab945..029631b8cb38 100644 --- a/resources/views/reports/chart_builder.blade.php +++ b/resources/views/reports/chart_builder.blade.php @@ -141,6 +141,13 @@ ->addOption(trans('texts.payment_date'), FILTER_PAYMENT_DATE) !!} + +
    @@ -167,6 +174,7 @@
    {!! DropdownButton::primary(trans('texts.export')) ->large() + ->withAttributes(array('id' => 'export-button')) ->withContents([ ['url' => 'javascript:onExportClick("csv")', 'label' => 'CSV'], ['url' => 'javascript:onExportClick("xlsx")', 'label' => 'XLSX'], @@ -268,20 +276,25 @@ $('#action').val('export'); $('#format').val(format); $('#submitButton').click(); - $('#action').val(''); + $('#action').val(''); } function setFiltersShown() { var val = $('#report_type').val(); - if (val == '{{ ENTITY_TAX_RATE }}') { - $('#dateField').show(); - } else { - $('#dateField').hide(); - } - if (val == '{{ ENTITY_INVOICE }}' || val == '{{ ENTITY_PRODUCT }}') { - $('#statusField').show(); - } else { - $('#statusField').hide(); + $('#dateField').toggle(val == '{{ ENTITY_TAX_RATE }}'); + $('#statusField').toggle(val == '{{ ENTITY_INVOICE }}' || val == '{{ ENTITY_PRODUCT }}'); + $('#invoiceOrExpenseField').toggle(val == '{{ ENTITY_DOCUMENT }}'); + } + + function setDocumentZipShown() { + var $ul = $('#export-button').next(); + var val = $('#report_type').val(); + var showOption = ['invoice', 'quote', 'expense', 'document'].indexOf(val) >= 0; + var numOptions = $ul.children().length; + if (showOption && numOptions == 3) { + $ul.append('
  • ZIP - {{ trans('texts.documents') }}
  • '); + } else if (! showOption && numOptions == 4) { + $ul.find('li:last-child').remove(); } } @@ -298,9 +311,17 @@ toggleDatePicker('end_date'); }); + $('#document_filter').change(function() { + var val = $('#document_filter').val(); + if (isStorageSupported()) { + localStorage.setItem('last:document_filter', val); + } + }); + $('#report_type').change(function() { var val = $('#report_type').val(); setFiltersShown(); + setDocumentZipShown(); if (isStorageSupported()) { localStorage.setItem('last:report_type', val); } @@ -361,11 +382,19 @@ widgets: ['zebra', 'uitheme'], }).show(); - var lastReportType = localStorage.getItem('last:report_type'); - if (lastReportType) { - $('#report_type').val(lastReportType); + if (isStorageSupported()) { + var lastReportType = localStorage.getItem('last:report_type'); + if (lastReportType) { + $('#report_type').val(lastReportType); + } + var lastDocumentFilter = localStorage.getItem('last:document_filter'); + if (lastDocumentFilter) { + $('#document_filter').val(lastDocumentFilter); + } } + setFiltersShown(); + setDocumentZipShown(); }); })