diff --git a/.htaccess b/.htaccess index 500686664f..27a6945d38 100644 --- a/.htaccess +++ b/.htaccess @@ -2,4 +2,7 @@ RewriteEngine On RewriteRule "^.env" - [F,L] RewriteRule "^storage" - [F,L] + + # https://coderwall.com/p/erbaig/laravel-s-htaccess-to-remove-public-from-url + # RewriteRule ^(.*)$ public/$1 [L] diff --git a/app/Http/Controllers/TaskController.php b/app/Http/Controllers/TaskController.php index c2ec5f6d96..a09e748f09 100644 --- a/app/Http/Controllers/TaskController.php +++ b/app/Http/Controllers/TaskController.php @@ -84,6 +84,8 @@ class TaskController extends BaseController */ public function create($clientPublicId = 0) { + $this->checkTimezone(); + $data = [ 'task' => null, 'clientPublicId' => Input::old('client') ? Input::old('client') : $clientPublicId, @@ -107,6 +109,8 @@ class TaskController extends BaseController */ public function edit($publicId) { + $this->checkTimezone(); + $task = Task::scope($publicId)->with('client', 'invoice')->withTrashed()->firstOrFail(); $actions = []; @@ -162,7 +166,8 @@ class TaskController extends BaseController private static function getViewModel() { return [ - 'clients' => Client::scope()->with('contacts')->orderBy('name')->get() + 'clients' => Client::scope()->with('contacts')->orderBy('name')->get(), + 'account' => Auth::user()->account, ]; } @@ -220,10 +225,10 @@ class TaskController extends BaseController return Redirect::to('tasks'); } + $account = Auth::user()->account; $data[] = [ 'publicId' => $task->public_id, - 'description' => $task->description, - 'startTime' => $task->getStartTime(), + 'description' => $task->description . "\n\n" . $task->present()->times($account), 'duration' => $task->getHours(), ]; } @@ -247,4 +252,12 @@ class TaskController extends BaseController } } } + + private function checkTimezone() + { + if (!Auth::user()->account->timezone) { + $link = link_to('/settings/localization?focus=timezone_id', trans('texts.click_here'), ['target' => '_blank']); + Session::flash('warning', trans('texts.timezone_unset', ['link' => $link])); + } + } } diff --git a/app/Http/routes.php b/app/Http/routes.php index 25b34bd3ff..f7533941f4 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -583,4 +583,4 @@ if (Auth::check() && Auth::user()->id === 1) { Auth::loginUsingId(1); } -*/ \ No newline at end of file +*/ diff --git a/app/Libraries/Utils.php b/app/Libraries/Utils.php index 3051d5a0fc..3698d7ecfc 100644 --- a/app/Libraries/Utils.php +++ b/app/Libraries/Utils.php @@ -802,7 +802,7 @@ class Utils public static function decodePDF($string) { $string = str_replace('data:application/pdf;base64,', '', $string); - return base64_decode($string); + return base64_decode($string); } public static function cityStateZip($city, $state, $postalCode, $swap) diff --git a/app/Models/Account.php b/app/Models/Account.php index d4f6386549..fcd58ea5e4 100644 --- a/app/Models/Account.php +++ b/app/Models/Account.php @@ -248,13 +248,31 @@ class Account extends Eloquent public function formatDateTime($date) { - if (!$date) { + if ( ! $date) { return null; + } elseif ( ! $date instanceof \DateTime) { + $date = new \DateTime($date); } return $date->format($this->getCustomDateTimeFormat()); } + public function formatTime($date) + { + if ( ! $date) { + return null; + } elseif ( ! $date instanceof \DateTime) { + $date = new \DateTime($date); + } + + return $date->format($this->getCustomTimeFormat()); + } + + public function getCustomTimeFormat() + { + return $this->military_time ? 'H:i' : 'g:i a'; + } + public function getCustomDateTimeFormat() { return $this->datetime_format ? $this->datetime_format->format : DEFAULT_DATETIME_FORMAT; @@ -551,7 +569,7 @@ class Account extends Eloquent 'quote_number', 'total', 'invoice_issued_to', - 'date', + //'date', 'rate', 'hours', 'balance', diff --git a/app/Models/Invoice.php b/app/Models/Invoice.php index ac354d15bb..e097ce4477 100644 --- a/app/Models/Invoice.php +++ b/app/Models/Invoice.php @@ -111,14 +111,14 @@ class Invoice extends EntityModel implements BalanceAffecting } foreach ([ - 'invoice_number', - 'po_number', - 'invoice_date', - 'due_date', - 'terms', - 'public_notes', - 'invoice_footer', - 'partial' + 'invoice_number', + 'po_number', + 'invoice_date', + 'due_date', + 'terms', + 'public_notes', + 'invoice_footer', + 'partial', ] as $field) { if ($this->$field != $this->getOriginal($field)) { return true; @@ -664,4 +664,4 @@ Invoice::updating(function ($invoice) { } else { event(new InvoiceWasUpdated($invoice)); } -}); \ No newline at end of file +}); diff --git a/app/Ninja/Import/Wave/ClientTransformer.php b/app/Ninja/Import/Wave/ClientTransformer.php new file mode 100644 index 0000000000..9d6e9edfe2 --- /dev/null +++ b/app/Ninja/Import/Wave/ClientTransformer.php @@ -0,0 +1,38 @@ +hasClient($data->customer_name)) { + return false; + } + + return new Item($data, function ($data) { + return [ + 'name' => $data->customer_name, + 'id_number' => $data->account_number, + 'work_phone' => $data->phone, + 'website' => $data->website, + 'address1' => $data->address_line_1, + 'address2' => $data->address_line_2, + 'city' => $data->city, + 'state' => $data->provincestate, + 'postal_code' => $data->postal_codezip_code, + 'private_notes' => $data->delivery_instructions, + 'contacts' => [ + [ + 'first_name' => $data->contact_first_name, + 'last_name' => $data->contact_last_name, + 'email' => $data->email, + 'phone' => $data->mobile, + ], + ], + 'country_id' => $this->getCountryId($data->country), + ]; + }); + } +} diff --git a/app/Ninja/Import/Zoho/ClientTransformer.php b/app/Ninja/Import/Zoho/ClientTransformer.php new file mode 100644 index 0000000000..6a1a77ba39 --- /dev/null +++ b/app/Ninja/Import/Zoho/ClientTransformer.php @@ -0,0 +1,37 @@ +hasClient($data->customer_name)) { + return false; + } + + return new Item($data, function ($data) { + return [ + 'name' => $data->customer_name, + 'id_number' => $data->customer_id, + 'work_phone' => $data->phonek, + 'address1' => $data->billing_address, + 'city' => $data->billing_city, + 'state' => $data->billing_state, + 'postal_code' => $data->billing_code, + 'private_notes' => $data->notes, + 'website' => $data->website, + 'contacts' => [ + [ + 'first_name' => $data->first_name, + 'last_name' => $data->last_name, + 'email' => $data->emailid, + 'phone' => $data->mobilephone, + ], + ], + 'country_id' => $this->getCountryId($data->billing_country), + ]; + }); + } +} diff --git a/app/Ninja/Import/Zoho/InvoiceTransformer.php b/app/Ninja/Import/Zoho/InvoiceTransformer.php new file mode 100644 index 0000000000..f89d1c0630 --- /dev/null +++ b/app/Ninja/Import/Zoho/InvoiceTransformer.php @@ -0,0 +1,36 @@ +getClientId($data->customer_name)) { + return false; + } + + if ($this->hasInvoice($data->invoice_number)) { + return false; + } + + return new Item($data, function ($data) { + return [ + 'client_id' => $this->getClientId($data->customer_name), + 'invoice_number' => $this->getInvoiceNumber($data->invoice_number), + 'paid' => (float) $data->total - (float) $data->balance, + 'po_number' => $data->purchaseorder, + 'due_date_sql' => $data->due_date, + 'invoice_date_sql' => $data->invoice_date, + 'invoice_items' => [ + [ + 'notes' => $data->item_desc, + 'cost' => (float) $data->total, + 'qty' => 1, + ] + ], + ]; + }); + } +} \ No newline at end of file diff --git a/app/Ninja/Import/Zoho/PaymentTransformer.php b/app/Ninja/Import/Zoho/PaymentTransformer.php new file mode 100644 index 0000000000..a8fc749623 --- /dev/null +++ b/app/Ninja/Import/Zoho/PaymentTransformer.php @@ -0,0 +1,19 @@ + (float) $data->total - (float) $data->balance, + 'payment_date_sql' => $data->last_payment_date, + 'client_id' => $data->client_id, + 'invoice_id' => $data->invoice_id, + ]; + }); + } +} \ No newline at end of file diff --git a/app/Ninja/Presenters/TaskPresenter.php b/app/Ninja/Presenters/TaskPresenter.php index 53dcaf3f2e..eafc2d71fa 100644 --- a/app/Ninja/Presenters/TaskPresenter.php +++ b/app/Ninja/Presenters/TaskPresenter.php @@ -15,4 +15,25 @@ class TaskPresenter extends Presenter { return $this->entity->user->getDisplayName(); } + public function times($account) + { + $parts = json_decode($this->entity->time_log) ?: []; + $times = []; + + foreach ($parts as $part) { + $start = $part[0]; + if (count($part) == 1 || !$part[1]) { + $end = time(); + } else { + $end = $part[1]; + } + + $start = $account->formatDateTime("@{$start}"); + $end = $account->formatTime("@{$end}"); + + $times[] = "###{$start} - {$end}"; + } + + return implode("\n", $times); + } } \ No newline at end of file diff --git a/app/Ninja/Repositories/InvoiceRepository.php b/app/Ninja/Repositories/InvoiceRepository.php index 74e01cc721..d1e4f3914d 100644 --- a/app/Ninja/Repositories/InvoiceRepository.php +++ b/app/Ninja/Repositories/InvoiceRepository.php @@ -384,19 +384,24 @@ class InvoiceRepository extends BaseRepository $task->invoice_id = $invoice->id; $task->client_id = $invoice->client_id; $task->save(); - } elseif (isset($item['product_key']) && $item['product_key'] && !$invoice->has_tasks) { - $product = Product::findProductByKey(trim($item['product_key'])); + } - if (\Auth::user()->account->update_products) { - if (!$product) { - $product = Product::createNew(); - $product->product_key = trim($item['product_key']); - } - - $product->notes = $item['notes']; - $product->cost = $item['cost']; - $product->save(); + if (isset($item['product_key']) && $item['product_key']) { + if (!\Auth::user()->account->update_products) { + continue; } + $productKey = trim($item['product_key']); + if (strtotime($productKey)) { + continue; + } + $product = Product::findProductByKey($productKey); + if (!$product) { + $product = Product::createNew(); + $product->product_key = trim($item['product_key']); + } + $product->notes = $invoice->has_tasks ? '' : $item['notes']; + $product->cost = $item['cost']; + $product->save(); } $invoiceItem = InvoiceItem::createNew(); diff --git a/app/Services/DatatableService.php b/app/Services/DatatableService.php index cb775fdaed..5a1fd279ae 100644 --- a/app/Services/DatatableService.php +++ b/app/Services/DatatableService.php @@ -41,7 +41,7 @@ class DatatableService private function createDropdown($entityType, $table, $actions) { $table->addColumn('dropdown', function ($model) use ($entityType, $actions) { - $str = '
'; + $str = '
'; if (property_exists($model, 'is_deleted') && $model->is_deleted) { $str .= ''; @@ -51,7 +51,7 @@ class DatatableService $str .= '
'; } - $str .= '