1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-09-18 23:42:25 +02:00
This commit is contained in:
Hillel Coren 2015-12-13 22:12:54 +02:00
parent 4b328acc67
commit 19d5d49e63
26 changed files with 270 additions and 55 deletions

View File

@ -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]
</IfModule>

View File

@ -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]));
}
}
}

View File

@ -583,4 +583,4 @@ if (Auth::check() && Auth::user()->id === 1)
{
Auth::loginUsingId(1);
}
*/
*/

View File

@ -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)

View File

@ -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',

View File

@ -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));
}
});
});

View File

@ -0,0 +1,38 @@
<?php namespace App\Ninja\Import\Wave;
use App\Ninja\Import\BaseTransformer;
use League\Fractal\Resource\Item;
class ClientTransformer extends BaseTransformer
{
public function transform($data)
{
if ($this->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),
];
});
}
}

View File

@ -0,0 +1,37 @@
<?php namespace App\Ninja\Import\Zoho;
use App\Ninja\Import\BaseTransformer;
use League\Fractal\Resource\Item;
class ClientTransformer extends BaseTransformer
{
public function transform($data)
{
if ($this->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),
];
});
}
}

View File

@ -0,0 +1,36 @@
<?php namespace App\Ninja\Import\Zoho;
use App\Ninja\Import\BaseTransformer;
use League\Fractal\Resource\Item;
class InvoiceTransformer extends BaseTransformer
{
public function transform($data)
{
if ( ! $this->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,
]
],
];
});
}
}

View File

@ -0,0 +1,19 @@
<?php namespace App\Ninja\Import\Zoho;
use App\Ninja\Import\BaseTransformer;
use League\Fractal\Resource\Item;
class PaymentTransformer extends BaseTransformer
{
public function transform($data, $maps)
{
return new Item($data, function ($data) use ($maps) {
return [
'amount' => (float) $data->total - (float) $data->balance,
'payment_date_sql' => $data->last_payment_date,
'client_id' => $data->client_id,
'invoice_id' => $data->invoice_id,
];
});
}
}

View File

@ -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);
}
}

View File

@ -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();

View File

@ -41,7 +41,7 @@ class DatatableService
private function createDropdown($entityType, $table, $actions)
{
$table->addColumn('dropdown', function ($model) use ($entityType, $actions) {
$str = '<center>';
$str = '<center style="min-width:100px">';
if (property_exists($model, 'is_deleted') && $model->is_deleted) {
$str .= '<button type="button" class="btn btn-sm btn-danger tr-status">'.trans('texts.deleted').'</button>';
@ -51,7 +51,7 @@ class DatatableService
$str .= '<div class="tr-status"></div>';
}
$str .= '<div class="btn-group tr-action" style="height:auto;visibility:hidden">
$str .= '<div class="btn-group tr-action" style="height:auto;display:none">
<button type="button" class="btn btn-xs btn-default dropdown-toggle" data-toggle="dropdown" style="width:100px">
'.trans('texts.select').' <span class="caret"></span>
</button>

View File

@ -39,8 +39,8 @@ class ImportService
IMPORT_INVOICEABLE,
//IMPORT_NUTCACHE,
IMPORT_RONIN,
//IMPORT_WAVE,
//IMPORT_ZOHO,
IMPORT_WAVE,
IMPORT_ZOHO,
];
public function __construct(Manager $manager, ClientRepository $clientRepo, InvoiceRepository $invoiceRepo, PaymentRepository $paymentRepo, ContactRepository $contactRepo)

View File

@ -118,6 +118,7 @@ class PaymentLibrariesSeeder extends Seeder
['name' => 'West African Franc', 'code' => 'XOF', 'symbol' => 'CFA ', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['name' => 'Chinese Renminbi', 'code' => 'CNY', 'symbol' => 'RMB ', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['name' => 'Rwandan Franc', 'code' => 'RWF', 'symbol' => 'RF ', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['name' => 'Tanzanian Shilling', 'code' => 'TZS', 'symbol' => 'TSh ', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
];
foreach ($currencies as $currency) {

View File

@ -30867,13 +30867,13 @@ function truncate(string, length){
// Show/hide the 'Select' option in the datalists
function actionListHandler() {
$('tbody tr').mouseover(function() {
$(this).closest('tr').find('.tr-action').css('visibility', 'visible');
$(this).closest('tr').find('.tr-status').css('visibility', 'hidden');
$(this).closest('tr').find('.tr-action').show();
$(this).closest('tr').find('.tr-status').hide();
}).mouseout(function() {
$dropdown = $(this).closest('tr').find('.tr-action');
if (!$dropdown.hasClass('open')) {
$dropdown.css('visibility', 'hidden');
$(this).closest('tr').find('.tr-status').css('visibility', 'visible');
$dropdown.hide();
$(this).closest('tr').find('.tr-status').show();
}
});
}
@ -31019,6 +31019,7 @@ NINJA.decodeJavascript = function(invoice, javascript)
'fontSize': NINJA.fontSize,
'fontSizeLarger': NINJA.fontSize + 1,
'fontSizeLargest': NINJA.fontSize + 2,
'fontSizeSmaller': NINJA.fontSize - 1,
}
for (var key in json) {
@ -31482,6 +31483,7 @@ NINJA.parseMarkdownText = function(val, groupText)
var rules = [
['\\\*\\\*(\\\w.+?)\\\*\\\*', {'bold': true}], // **value**
['\\\*(\\\w.+?)\\\*', {'italics': true}], // *value*
['^###(.*)', {'style': 'help'}], // ### Small/gray help
['^##(.*)', {'style': 'subheader'}], // ## Header
['^#(.*)', {'style': 'header'}] // # Subheader
];

View File

@ -140,6 +140,7 @@ NINJA.decodeJavascript = function(invoice, javascript)
'fontSize': NINJA.fontSize,
'fontSizeLarger': NINJA.fontSize + 1,
'fontSizeLargest': NINJA.fontSize + 2,
'fontSizeSmaller': NINJA.fontSize - 1,
}
for (var key in json) {
@ -603,6 +604,7 @@ NINJA.parseMarkdownText = function(val, groupText)
var rules = [
['\\\*\\\*(\\\w.+?)\\\*\\\*', {'bold': true}], // **value**
['\\\*(\\\w.+?)\\\*', {'italics': true}], // *value*
['^###(.*)', {'style': 'help'}], // ### Small/gray help
['^##(.*)', {'style': 'subheader'}], // ## Header
['^#(.*)', {'style': 'header'}] // # Subheader
];

View File

@ -989,13 +989,13 @@ function truncate(string, length){
// Show/hide the 'Select' option in the datalists
function actionListHandler() {
$('tbody tr').mouseover(function() {
$(this).closest('tr').find('.tr-action').css('visibility', 'visible');
$(this).closest('tr').find('.tr-status').css('visibility', 'hidden');
$(this).closest('tr').find('.tr-action').show();
$(this).closest('tr').find('.tr-status').hide();
}).mouseout(function() {
$dropdown = $(this).closest('tr').find('.tr-action');
if (!$dropdown.hasClass('open')) {
$dropdown.css('visibility', 'hidden');
$(this).closest('tr').find('.tr-status').css('visibility', 'visible');
$dropdown.hide();
$(this).closest('tr').find('.tr-status').show();
}
});
}

View File

@ -175,7 +175,7 @@
<thead>
<tr>
<th style="min-width:32px;" class="hide-border"></th>
<th style="min-width:160px" data-bind="text: productLabel">{{ $invoiceLabels['item'] }}</th>
<th style="min-width:160px">{{ $invoiceLabels['item'] }}</th>
<th style="width:100%">{{ $invoiceLabels['description'] }}</th>
<th style="min-width:120px" data-bind="text: costLabel">{{ $invoiceLabels['unit_cost'] }}</th>
<th style="{{ $account->hide_quantity ? 'display:none' : 'min-width:120px' }}" data-bind="text: qtyLabel">{{ $invoiceLabels['quantity'] }}</th>
@ -676,16 +676,15 @@
var tasks = {!! $tasks !!};
for (var i=0; i<tasks.length; i++) {
var task = tasks[i];
var task = tasks[i];
var item = model.invoice().addItem();
item.notes(task.description);
item.product_key(task.startTime);
item.qty(task.duration);
item.task_public_id(task.publicId);
}
}
model.invoice().invoice_items.push(blank);
model.invoice().has_tasks(true);
@endif
@endif
@endif
model.invoice().tax(model.getTaxRate(model.invoice().tax_name(), model.invoice().tax_rate()));
@ -827,7 +826,9 @@
var product = products[i];
if (product.product_key == key) {
var model = ko.dataFor(this);
model.notes(product.notes);
if (product.notes) {
model.notes(product.notes);
}
model.cost(accounting.toFixed(product.cost,2));
if (!model.qty()) {
model.qty(1);
@ -874,9 +875,9 @@
invoice.imageHeight = {{ $account->getLogoHeight() }};
@endif
invoiceLabels.item = invoice.has_tasks ? invoiceLabels.date : invoiceLabels.item_orig;
//invoiceLabels.item = invoice.has_tasks ? invoiceLabels.date : invoiceLabels.item_orig;
invoiceLabels.quantity = invoice.has_tasks ? invoiceLabels.hours : invoiceLabels.quantity_orig;
invoiceLabels.unit_cost = invoice.has_tasks ? invoiceLabels.rate : invoiceLabels.unit_cost_orig;
invoiceLabels.unit_cost = invoice.has_tasks ? invoiceLabels.rate : invoiceLabels.unit_cost_orig;
return invoice;
}

View File

@ -271,10 +271,6 @@ function InvoiceModel(data) {
self.addItem();
}
self.productLabel = ko.computed(function() {
return self.has_tasks() ? invoiceLabels['date'] : invoiceLabels['item'];
}, this);
self.qtyLabel = ko.computed(function() {
return self.has_tasks() ? invoiceLabels['hours'] : invoiceLabels['quantity'];
}, this);

View File

@ -80,7 +80,7 @@
var invoiceLabels = {!! json_encode($account->getInvoiceLabels()) !!};
if (window.invoice) {
invoiceLabels.item = invoice.has_tasks ? invoiceLabels.date : invoiceLabels.item_orig;
//invoiceLabels.item = invoice.has_tasks ? invoiceLabels.date : invoiceLabels.item_orig;
invoiceLabels.quantity = invoice.has_tasks ? invoiceLabels.hours : invoiceLabels.quantity_orig;
invoiceLabels.unit_cost = invoice.has_tasks ? invoiceLabels.rate : invoiceLabels.unit_cost_orig;
}

View File

@ -160,13 +160,21 @@
ko.bindingHandlers.dateTimePicker = {
init: function (element, valueAccessor, allBindingsAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor());
// http://xdsoft.net/jqplugins/datetimepicker/
$(element).datetimepicker({
lang: '{{ App::getLocale() }}',
lazyInit: true,
validateOnBlur: false,
step: 30,
format: '{{ $datetimeFormat }}',
formatDate: '{{ Auth::user()->account->getMomentDateFormat() }}',
formatTime: '{{ Auth::user()->account->military_time ? 'H:mm' : 'h:mm A' }}',
formatDate: '{{ $account->getMomentDateFormat() }}',
formatTime: '{{ $account->military_time ? 'H:mm' : 'h:mm A' }}',
onSelectTime: function(current_time, $input){
current_time.setSeconds(0);
$(element).datetimepicker({
value: current_time
});
}
});
$(element).change(function() {
@ -180,7 +188,6 @@
}
}
var clients = {!! $clients !!};
var timeLabels = {};
@foreach (['hour', 'minute', 'second'] as $period)

View File

@ -246,6 +246,10 @@
},
"subheader": {
"fontSize": "$fontSizeLarger"
},
"help": {
"fontSize": "$fontSizeSmaller",
"color": "#737373"
}
},
"pageMargins": [0, 80, 0, 40]

View File

@ -192,6 +192,10 @@
},
"subheader": {
"fontSize": "$fontSizeLarger"
},
"help": {
"fontSize": "$fontSizeSmaller",
"color": "#737373"
}
},
"pageMargins": [40, 40, 40, 60]

View File

@ -237,6 +237,10 @@
},
"subheader": {
"fontSize": "$fontSizeLarger"
},
"help": {
"fontSize": "$fontSizeSmaller",
"color": "#737373"
}
},
"pageMargins": [40, 80, 40, 50]

View File

@ -159,6 +159,10 @@
},
"subheader": {
"fontSize": "$fontSizeLarger"
},
"help": {
"fontSize": "$fontSizeSmaller",
"color": "#737373"
}
},
"pageMargins": [40, 40, 40, 60]