1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-09-19 16:01:34 +02:00

Minor fixes

This commit is contained in:
Hillel Coren 2014-11-02 14:47:28 +02:00
commit e8dad88d15
47 changed files with 19129 additions and 15295 deletions

View File

@ -5,9 +5,37 @@ module.exports = function(grunt) {
concat: {
options: {
process: function(src, filepath) {
// Fix path for image and font resources
var basepath = filepath.substring(7, filepath.lastIndexOf('/') + 1);
console.log(filepath);
// Fix relative paths for css files
if(filepath.indexOf('.css', filepath.length - 4) !== -1) {
return src.replace(/\.\.\/(images|fonts)\//g, '$1/');
return src.replace(/(url\s*[\("']+)\s*([^'"\)]+)(['"\)]+;?)/gi, function(match, start, url, end, offset, string) {
if(url.indexOf('data:') === 0) {
// Skip data urls
return match;
} else if(url.indexOf('/') === 0) {
// Skip absolute urls
return match;
} else {
return start + basepath + url + end;
}
});
// Fix source maps locations
} else if(filepath.indexOf('.js', filepath.length - 4) !== -1) {
return src.replace(/(\/[*\/][#@]\s*sourceMappingURL=)([^\s]+)/gi, function(match, start, url, offset, string) {
if(url.indexOf('/') === 0) {
// Skip absolute urls
return match;
} else {
return start + basepath + url;
}
});
// Don't do anything for unknown file types
} else {
return src;
@ -17,7 +45,7 @@ module.exports = function(grunt) {
js: {
src: [
'public/vendor/jquery/dist/jquery.js',
'public/vendor/jquery-ui/ui/jquery-ui.js',
'public/vendor/jquery-ui/jquery-ui.min.js',
'public/vendor/bootstrap/dist/js/bootstrap.min.js',
'public/vendor/datatables/media/js/jquery.dataTables.js',
'public/vendor/datatables-bootstrap3/BS3/assets/js/datatables.js',
@ -32,6 +60,7 @@ module.exports = function(grunt) {
'public/js/bootstrap-combobox.js',
'public/vendor/jspdf/dist/jspdf.min.js',
'public/vendor/lightbox2/js/lightbox.min.js',
'public/vendor/handsontable/dist/jquery.handsontable.full.min.js',
'public/js/script.js',
],
dest: 'public/built.js',
@ -42,7 +71,7 @@ module.exports = function(grunt) {
'public/js/simpleexpand.js',
'public/js/valign.js',
'public/js/bootstrap.min.js',
'public/js/simpleexpand.js',
'public/js/simpleexpand.js',
],
dest: 'public/js/built.public.js',
nonull: true
@ -58,6 +87,7 @@ module.exports = function(grunt) {
'public/css/bootstrap-combobox.css',
'public/css/typeahead.js-bootstrap.css',
'public/vendor/lightbox2/css/lightbox.css',
'public/vendor/handsontable/dist/jquery.handsontable.full.css',
'public/css/style.css',
],
dest: 'public/built.css',

View File

@ -89,6 +89,9 @@ Add public/ to your web server root
sudo npm install -g bower
sudo ln -s /usr/local/lib/node_modules/bower/bin/bower /usr/local/bin/bower
# Install Grunt (For development only)
npm install -g grunt-cli
### Frameworks/Libraries
* [laravel/laravel](https://github.com/laravel/laravel) - A PHP Framework For Web Artisans
* [twbs/bootstrap](https://github.com/twbs/bootstrap) - Sleek, intuitive, and powerful front-end framework for faster and easier web development.

14351
_ide_helper.php Normal file

File diff suppressed because it is too large Load Diff

1239
_ide_helper_models.php Normal file

File diff suppressed because it is too large Load Diff

View File

@ -23,16 +23,16 @@ class CreateRandomData extends Command {
$productNames = ['Arkansas', 'New York', 'Arizona', 'California', 'Colorado', 'Alabama', 'Connecticut', 'Delaware', 'Florida', 'Georgia', 'Hawaii', 'Idaho', 'Illinois', 'Indiana', 'Iowa', 'Kansas', 'Kentucky', 'Louisiana', 'Maine', 'Maryland', 'Massachusetts', 'Michigan', 'Minnesota', 'Mississippi', 'Missouri', 'Montana', 'Nebraska', 'Nevada', 'New Hampshire', 'New Jersey', 'New Mexico', 'Alaska', 'North Carolina', 'North Dakota', 'Ohio', 'Oklahoma', 'Oregon', 'Pennsylvania', 'Rhode Island', 'South Carolina', 'South Dakota', 'Tennessee', 'Texas', 'Utah', 'Vermont', 'Virginia', 'Washington', 'West Virginia', 'Wisconsin', 'Wyoming'];
$clientNames = ['IBM', 'Nestle', 'Mitsubishi UFJ Financial', 'Vodafone', 'Eni', 'Procter & Gamble', 'Johnson & Johnson', 'American International Group', 'Banco Santander', 'BHP Billiton', 'Pfizer', 'Itaú Unibanco Holding', 'Ford Motor', 'BMW Group', 'Commonwealth Bank', 'EDF', 'Statoil', 'Google', 'Siemens', 'Novartis', 'Royal Bank of Canada', 'Sumitomo Mitsui Financial', 'Comcast', 'Sberbank', 'Goldman Sachs Group', 'Westpac Banking Group', 'Nippon Telegraph & Tel', 'Ping An Insurance Group', 'Banco Bradesco', 'Anheuser-Busch InBev', 'Bank of Communications', 'China Life Insurance', 'General Motors', 'Telefónica', 'MetLife', 'Honda Motor', 'Enel', 'BASF', 'Softbank', 'National Australia Bank', 'ANZ', 'ConocoPhillips', 'TD Bank Group', 'Intel', 'UBS', 'Hewlett-Packard', 'Coca-Cola', 'Cisco Systems', 'UnitedHealth Group', 'Boeing', 'Zurich Insurance Group', 'Hyundai Motor', 'Sanofi', 'Credit Agricole', 'United Technologies', 'Roche Holding', 'Munich Re', 'PepsiCo', 'Oracle', 'Bank of Nova Scotia'];
for ($i=1; $i<=40; $i++) {
foreach ($productNames as $i => $value) {
$product = Product::createNew($user);
$product->id = $i;
$product->product_key = $productNames[$i-1];
$product->id = $i+1;
$product->product_key = $value;
$product->save();
}
for ($i=0; $i<60; $i++) {
foreach ($clientNames as $i => $value) {
$client = Client::createNew($user);
$client->name = $clientNames[$i];
$client->name = $value;
$client->save();
$contact = Contact::createNew($user);

View File

@ -0,0 +1,335 @@
<?php
use Illuminate\Console\Command;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;
use PHPBenchTime\Timer;
class ImportTimesheetData extends Command {
protected $name = 'ninja:import-timesheet-data';
protected $description = 'Import timesheet data';
public function fire() {
$this->info(date('Y-m-d') . ' Running ImportTimesheetData...');
// Seems we are using the console timezone
DB::statement("SET SESSION time_zone = '+00:00'");
// Get the Unix epoch
$unix_epoch = new DateTime('1970-01-01T00:00:01', new DateTimeZone("UTC"));
// Create some initial sources we can test with
$user = User::first();
if (!$user) {
$this->error("Error: please create user account by logging in");
return;
}
// TODO: Populate with own test data until test data has been created
// Truncate the tables
/*$this->info("Truncate tables");
DB::statement('SET FOREIGN_KEY_CHECKS=0;');
DB::table('projects')->truncate();
DB::table('project_codes')->truncate();
DB::table('timesheet_event_sources')->truncate();
DB::table('timesheet_events')->truncate();
DB::statement('SET FOREIGN_KEY_CHECKS=1;'); */
if (!Project::find(1)) {
$this->info("Import old project codes");
$oldcodes = json_decode(file_get_contents("/home/tlb/git/itktime/codes.json"), true);
foreach ($oldcodes as $name => $options) {
$project = Project::createNew($user);
$project->name = $options['description'];
$project->save();
$code = ProjectCode::createNew($user);
$code->name = $name;
$project->codes()->save($code);
}
}
if (!TimesheetEventSource::find(1)) {
$this->info("Import old event sources");
$oldevent_sources = json_decode(file_get_contents("/home/tlb/git/itktime/employes.json"), true);
foreach ($oldevent_sources as $source) {
$event_source = TimesheetEventSource::createNew($user);
$event_source->name = $source['name'];
$event_source->url = $source['url'];
$event_source->owner = $source['owner'];
$event_source->type = 'ical';
//$event_source->from_date = new DateTime("2009-01-01");
$event_source->save();
}
}
// Add all URL's to Curl
$this->info("Download ICAL feeds");
$T = new Timer;
$T->start();
$T->lap("Get Event Sources");
$event_sources = TimesheetEventSource::all(); // TODO: Filter based on ical feeds
$T->lap("Get ICAL responses");
$urls = [];
$event_sources->map(function($item) use(&$urls) {
$urls[] = $item->url;
});
$icalresponses = TimesheetUtils::curlGetUrls($urls);
$T->lap("Fetch all codes so we can do a quick lookup");
$codes = array();
ProjectCode::all()->map(function($item) use(&$codes) {
$codes[$item->name] = $item;
});
$this->info("Start parsing ICAL files");
foreach ($event_sources as $i => $event_source) {
if (!is_array($icalresponses[$i])) {
$this->info("Find events in " . $event_source->name);
file_put_contents("/tmp/" . $event_source->name . ".ical", $icalresponses[$i]); // FIXME: Remove
$T->lap("Split on events for ".$event_source->name);
// Check if the file is complete
if(!preg_match("/^\s*BEGIN:VCALENDAR/", $icalresponses[$i]) || !preg_match("/END:VCALENDAR\s*$/", $icalresponses[$i])) {
$this->error("Missing start or end of ical file");
continue;
}
// Extract all events from ical file
if (preg_match_all('/BEGIN:VEVENT\r?\n(.+?)\r?\nEND:VEVENT/s', $icalresponses[$i], $icalmatches)) {
$this->info("Found ".(count($icalmatches[1])-1)." events");
$T->lap("Fetch all uids and last updated at so we can do a quick lookup to find out if the event needs to be updated in the database".$event_source->name);
$uids = [];
$org_deleted = []; // Create list of events we know are deleted on the source, but still have in the db
$event_source->events()->withTrashed()->get(['uid', 'org_updated_at', 'updated_data_at', 'org_deleted_at'])->map(function($item) use(&$uids, &$org_deleted) {
if($item->org_updated_at > $item->updated_data_at) {
$uids[$item->uid] = $item->org_updated_at;
} else {
$uids[$item->uid] = $item->updated_data_at;
}
if($item->org_deleted_at > '0000-00-00 00:00:00') {
$org_deleted[$item->uid] = $item->updated_data_at;
}
});
$deleted = $uids;
// Loop over all the found events
$T->lap("Parse events for ".$event_source->name);
foreach ($icalmatches[1] as $eventstr) {
//print "---\n";
//print $eventstr."\n";
//print "---\n";
//$this->info("Match event");
# Fix lines broken by 76 char limit
$eventstr = preg_replace('/\r?\n\s/s', '', $eventstr);
//$this->info("Parse data");
$data = TimesheetUtils::parseICALEvent($eventstr);
if ($data) {
// Extract code for summary so we only import events we use
list($codename, $tags, $title) = TimesheetUtils::parseEventSummary($data['summary']);
if ($codename != null) {
$event = TimesheetEvent::createNew($user);
// Copy data to new object
$event->uid = $data['uid'];
$event->summary = $title;
$event->org_data = $eventstr;
$event->org_code = $codename;
if(isset($data['description'])) {
$event->description = $data['description'];
}
$event->owner = $event_source->owner;
$event->timesheet_event_source_id = $event_source->id;
if (isset($codes[$codename])) {
$event->project_id = $codes[$codename]->project_id;
$event->project_code_id = $codes[$codename]->id;
}
if (isset($data['location'])) {
$event->location = $data['location'];
}
# Add RECURRENCE-ID to the UID to make sure the event is unique
if (isset($data['recurrence-id'])) {
$event->uid .= "::".$data['recurrence-id'];
}
//TODO: Add support for recurring event, make limit on number of events created : https://github.com/tplaner/When
// Bail on RRULE as we don't support that
if(isset($event['rrule'])) {
die("Recurring event not supported: {$event['summary']} - {$event['dtstart']}");
}
// Convert to DateTime objects
foreach (['dtstart', 'dtend', 'created', 'last-modified'] as $key) {
// Parse and create DataTime object from ICAL format
list($dt, $timezone) = TimesheetUtils::parseICALDate($data[$key]);
// Handle bad dates in created and last-modified
if ($dt == null || $dt < $unix_epoch) {
if ($key == 'created' || $key == 'last-modified') {
$dt = $unix_epoch; // Default to UNIX epoch
$event->import_warning = "Could not parse date for $key: '" . $data[$key] . "' so default to UNIX Epoc\n";
} else {
$event->import_error = "Could not parse date for $key: '" . $data[$key] . "' so default to UNIX Epoc\n";
// TODO: Bail on this event or write to error table
die("Could not parse date for $key: '" . $data[$key] . "'\n");
}
}
// Assign DateTime object to
switch ($key) {
case 'dtstart':
$event->start_date = $dt;
if($timezone) {
$event->org_start_date_timezone = $timezone;
}
break;
case 'dtend':
$event->end_date = $dt;
if($timezone) {
$event->org_end_date_timezone = $timezone;
}
break;
case 'created':
$event->org_created_at = $dt;
break;
case 'last-modified':
$event->org_updated_at = $dt;
break;
}
}
// Check that we are witin the range
if ($event_source->from_date != null) {
$from_date = new DateTime($event_source->from_date, new DateTimeZone('UTC'));
if ($from_date > $event->end_date) {
// Skip this event
echo "Skiped: $codename: $title\n";
continue;
}
}
// Calculate number of hours
$di = $event->end_date->diff($event->start_date);
$event->hours = $di->h + $di->i / 60;
// Check for events we already have
if (isset($uids[$event->uid])) {
// Remove from deleted list
unset($deleted[$event->uid]);
// See if the event has been updated compared to the one in the database
$db_event_org_updated_at = new DateTime($uids[$event->uid], new DateTimeZone('UTC'));
// Check if same or older version of new event then skip
if($event->org_updated_at <= $db_event_org_updated_at) {
// SKIP
// Updated version of the event
} else {
// Get the old event from the database
/* @var $db_event TimesheetEvent */
$db_event = $event_source->events()->where('uid', $event->uid)->firstOrFail();
$changes = $db_event->toChangesArray($event);
// Make sure it's more than the org_updated_at that has been changed
if (count($changes) > 1) {
// Check if we have manually changed the event in the database or used it in a timesheet
if ($db_event->manualedit || $db_event->timesheet) {
$this->info("Updated Data");
$db_event->updated_data = $event->org_data;
$db_event->updated_data_at = $event->org_updated_at;
// Update the db_event with the changes
} else {
$this->info("Updated Event");
foreach ($changes as $key => $value) {
if($value == null) {
unset($db_event->$key);
} else {
$db_event->$key = $value;
}
}
}
} else {
$this->info("Nothing Changed");
// Nothing has been changed so update the org_updated_at
$db_event->org_updated_at = $changes['org_updated_at'];
}
$db_event->save();
}
} else {
try {
$this->info("New event: " . $event->summary);
$event->save();
} catch (Exception $ex) {
echo "'" . $event->summary . "'\n";
var_dump($data);
echo $ex->getMessage();
echo $ex->getTraceAsString();
exit(0);
}
}
// Add new uid to know uids
$uids[$event->uid] = $event->org_updated_at;
}
}
}
// Delete events in database that no longer exists in the source
foreach($deleted as $uid => $lastupdated_date) {
// Skip we already marked this a deleted
if(isset($org_deleted[$uid])) {
unset($deleted[$uid]);
continue;
}
// Delete or update event in db
$db_event = $event_source->events()->where('uid', $uid)->firstOrFail();
if($db_event->timesheet_id === null && !$db_event->manualedit) {
// Hard delete if this event has not been assigned to a timesheet or have been manually edited
$db_event->forceDelete();
} else {
// Mark as deleted in source
$db_event->org_deleted_at = new DateTime('now', new DateTimeZone('UTC'));
$db_event->save();
}
}
$this->info("Deleted ".count($deleted). " events");
} else {
// TODO: Parse error
}
} else {
// TODO: Curl Error
}
}
foreach($T->end()['laps'] as $lap) {
echo number_format($lap['total'], 3)." : {$lap['name']}\n";
}
$this->info('Done');
}
protected function getArguments() {
return array(
);
}
protected function getOptions() {
return array(
);
}
}

View File

@ -122,7 +122,8 @@ return array(
'Barryvdh\Debugbar\ServiceProvider',
'Chumper\Datatable\DatatableServiceProvider',
'Intervention\Image\ImageServiceProvider',
'Webpatser\Countries\CountriesServiceProvider'
'Webpatser\Countries\CountriesServiceProvider',
'Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider',
),
/*

View File

@ -818,6 +818,7 @@ class AccountController extends \BaseController {
{
$account = Auth::user()->account;
$account->name = trim(Input::get('name'));
$account->vat_number = trim(Input::get('vat_number'));
$account->work_email = trim(Input::get('work_email'));
$account->work_phone = trim(Input::get('work_phone'));
$account->address1 = trim(Input::get('address1'));

View File

@ -199,6 +199,7 @@ class ClientController extends \BaseController {
}
$client->name = trim(Input::get('name'));
$client->vat_number = trim(Input::get('vat_number'));
$client->work_phone = trim(Input::get('work_phone'));
$client->custom_value1 = trim(Input::get('custom_value1'));
$client->custom_value2 = trim(Input::get('custom_value2'));

View File

@ -0,0 +1,93 @@
<?php
class TimesheetController extends \BaseController {
/**
* Display a listing of the resource.
*
* @return Response
*/
public function index()
{
$data = [
'showBreadcrumbs' => false,
'timesheet' => [
'timesheet_number' => 1
]
];
return View::make('timesheets.edit', $data);
}
/**
* Show the form for creating a new resource.
*
* @return Response
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*
* @return Response
*/
public function store()
{
//
}
/**
* Display the specified resource.
*
* @param int $id
* @return Response
*/
public function show($id)
{
//
}
/**
* Show the form for editing the specified resource.
*
* @param int $id
* @return Response
*/
public function edit($id)
{
//
}
/**
* Update the specified resource in storage.
*
* @param int $id
* @return Response
*/
public function update($id)
{
//
}
/**
* Remove the specified resource from storage.
*
* @param int $id
* @return Response
*/
public function destroy($id)
{
//
}
}

View File

@ -0,0 +1,43 @@
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class AddCompanyVatNumber extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('accounts', function($table)
{
$table->string('vat_number')->nullable();
});
Schema::table('clients', function($table)
{
$table->string('vat_number')->nullable();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('accounts', function($table)
{
$table->dropColumn('vat_number');
});
Schema::table('clients', function($table)
{
$table->dropColumn('vat_number');
});
}
}

View File

@ -0,0 +1,152 @@
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class AddTimesheets extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('projects', function($t) {
$t->increments('id');
$t->unsignedInteger('user_id');
$t->unsignedInteger('account_id')->index();
$t->unsignedInteger('client_id')->nullable();
$t->timestamps();
$t->softDeletes();
$t->string('name');
$t->string('description');
$t->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$t->foreign('account_id')->references('id')->on('accounts');
$t->unique( array('account_id','name') );
});
Schema::create('project_codes', function($t) {
$t->increments('id');
$t->unsignedInteger('user_id');
$t->unsignedInteger('account_id')->index();
$t->unsignedInteger('project_id');
$t->timestamps();
$t->softDeletes();
$t->string('name');
$t->string('description');
$t->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$t->foreign('account_id')->references('id')->on('accounts');
$t->foreign('project_id')->references('id')->on('projects')->onDelete('cascade');
$t->unique( array('account_id','name') );
});
Schema::create('timesheets', function($t) {
$t->increments('id');
$t->unsignedInteger('user_id');
$t->unsignedInteger('account_id')->index();
$t->timestamps();
$t->softDeletes();
$t->dateTime('start_date');
$t->dateTime('end_date');
$t->float('discount');
$t->decimal('hours');
$t->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$t->foreign('account_id')->references('id')->on('accounts');
$t->unsignedInteger('public_id');
$t->unique( array('account_id','public_id') );
});
Schema::create('timesheet_event_sources', function($t) {
$t->increments('id');
$t->unsignedInteger('user_id');
$t->unsignedInteger('account_id')->index();
$t->timestamps();
$t->softDeletes();
$t->string('owner');
$t->string('name');
$t->string('url');
$t->enum('type', array('ical', 'googlejson'));
$t->dateTime('from_date')->nullable();
$t->dateTime('to_date')->nullable();
$t->foreign('account_id')->references('id')->on('accounts');
$t->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
});
Schema::create('timesheet_events', function($t) {
$t->increments('id');
$t->unsignedInteger('user_id');
$t->unsignedInteger('account_id')->index();
$t->unsignedInteger('timesheet_event_source_id');
$t->unsignedInteger('timesheet_id')->nullable()->index();
$t->unsignedInteger('project_id')->nullable()->index();
$t->unsignedInteger('project_code_id')->nullable()->index();
$t->timestamps();
$t->softDeletes();
// Basic fields
$t->string('uid');
$t->string('summary');
$t->text('description');
$t->string('location');
$t->string('owner');
$t->dateTime('start_date');
$t->dateTime('end_date');
# Calculated values
$t->decimal('hours');
$t->float('discount');
$t->boolean('manualedit');
// Original data
$t->string('org_code');
$t->timeStamp('org_created_at');
$t->timeStamp('org_updated_at');
$t->timeStamp('org_deleted_at')->default('0000-00-00T00:00:00');
$t->string('org_start_date_timezone')->nullable();
$t->string('org_end_date_timezone')->nullable();
$t->text('org_data');
// Error and merge handling
$t->string('import_error')->nullable();
$t->string('import_warning')->nullable();
$t->text('updated_data')->nullable();
$t->timeStamp('updated_data_at')->default('0000-00-00T00:00:00');
$t->foreign('account_id')->references('id')->on('accounts');
$t->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$t->foreign('timesheet_event_source_id')->references('id')->on('timesheet_event_sources')->onDelete('cascade');
$t->unique( array('timesheet_event_source_id', 'uid') );
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('timesheet_events');
Schema::drop('timesheet_event_sources');
Schema::drop('timesheets');
Schema::drop('project_codes');
Schema::drop('projects');
}
}

View File

@ -0,0 +1,29 @@
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class AddDanishTranslation extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
DB::table('languages')->insert(['name' => 'Danish', 'locale' => 'da']);
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
$language = Language::whereLocale('da')->first();
$language->delete();
}
}

View File

@ -0,0 +1,20 @@
<?php
return array(
/*
|--------------------------------------------------------------------------
| Pagination Language Lines
|--------------------------------------------------------------------------
|
| The following language lines are used by the paginator library to build
| the simple pagination links. You are free to change them to anything
| you want to customize your views to better match your application.
|
*/
'previous' => '&laquo; Forrige',
'next' => 'Næste &raquo;',
);

26
app/lang/da/reminders.php Normal file
View File

@ -0,0 +1,26 @@
<?php
return array(
/*
|--------------------------------------------------------------------------
| Password Reminder Language Lines
|--------------------------------------------------------------------------
|
| The following language lines are the default lines which match reasons
| that are given by the password broker for a password update attempt
| has failed, such as for an invalid token or invalid new password.
|
*/
"password" => "Passwords skal minimum være 6 tegn og matche sikkerhedstjekket.",
"user" => "Vi kan ikke finde en bruger med den email-adresse.",
"token" => "Password-nulstillingskoden er ugyldig.",
"sent" => "Password-påmindelse sendt!",
"reset" => "Password has been reset!",
);

447
app/lang/da/texts.php Normal file
View File

@ -0,0 +1,447 @@
<?php
return array(
// client
'organization' => 'Organisation',
'name' => 'Navn',
'vat_number' => 'CVR nummer',
'website' => 'Webside',
'work_phone' => 'Telefon',
'address' => 'Adresse',
'address1' => 'Gade',
'address2' => 'Nummer',
'city' => 'By',
'state' => 'Område',
'postal_code' => 'Postnummer',
'country_id' => 'Land',
'contacts' => 'Kontakter',
'first_name' => 'Fornavn',
'last_name' => 'Efternavn',
'phone' => 'Telefon',
'email' => 'E-mail',
'additional_info' => 'Ekstra information',
'payment_terms' => 'Betalingsvilkår',
'currency_id' => 'Valuta',
'size_id' => 'Størrelse',
'industry_id' => 'Sektor',
'private_notes' => 'Private notater',
// invoice
'invoice' => 'Faktura',
'client' => 'Klient',
'invoice_date' => 'Faktureringsdato',
'due_date' => 'Betalingsfrist',
'invoice_number' => 'Fakturanummer',
'invoice_number_short' => 'Faktura #',
'po_number' => 'Ordrenummer',
'po_number_short' => 'Ordre #',
'frequency_id' => 'Frekvens',
'discount' => 'Rabat',
'taxes' => 'Skatter',
'tax' => 'Skat',
'item' => 'Produkttype',
'description' => 'Beskrivelse',
'unit_cost' => 'Pris',
'quantity' => 'Stk.',
'line_total' => 'Sum',
'subtotal' => 'Subtotal',
'paid_to_date' => 'Betalt',
'balance_due' => 'Udestående beløb',
'invoice_design_id' => 'Design',
'terms' => 'Vilkår',
'your_invoice' => 'Din faktura',
'remove_contact' => 'Fjern kontakt',
'add_contact' => 'Tilføj til kontakt',
'create_new_client' => 'Opret ny klient',
'edit_client_details' => 'Ændre klientdetaljer',
'enable' => 'Aktiver',
'learn_more' => 'Lær mere',
'manage_rates' => 'Administrer priser',
'note_to_client' => 'Note til klient',
'invoice_terms' => 'Vilkår for fakturaen',
'save_as_default_terms' => 'Gem som standard vilkår',
'download_pdf' => 'Download PDF',
'pay_now' => 'Betal nu',
'save_invoice' => 'Gem faktura',
'clone_invoice' => 'Kopier faktura',
'archive_invoice' => 'Arkiver faktura',
'delete_invoice' => 'Slet faktura',
'email_invoice' => 'Send faktura som e-mail',
'enter_payment' => 'Tilføj betaling',
'tax_rates' => 'Skattesatser',
'rate' => 'Sats',
'settings' => 'Indstillinger',
'enable_invoice_tax' => 'Aktiver for at specificere en <b>faktura skat</b>',
'enable_line_item_tax' => 'Aktiver for at specificere per <b>faktura linje skat</b>',
// navigation
'dashboard' => 'Dashboard',
'clients' => 'Klienter',
'invoices' => 'Fakturaer',
'payments' => 'Betalinger',
'credits' => 'Kreditter',
'history' => 'Historie',
'search' => 'Søg',
'sign_up' => 'Registrer dig',
'guest' => 'Gjest',
'company_details' => 'Firmainformation',
'online_payments' => 'Online betaling',
'notifications' => 'Varsler',
'import_export' => 'Import/Export',
'done' => 'Færdig',
'save' => 'Gem',
'create' => 'Opret',
'upload' => 'Upload',
'import' => 'Importer',
'download' => 'Download',
'cancel' => 'Afbrud',
'close' => 'Luk',
'provide_email' => 'Venligst opgiv en gyldig e-mail',
'powered_by' => 'Drevet af',
'no_items' => 'Ingen elementer',
// recurring invoices
'recurring_invoices' => 'Gentagende fakturaer',
'recurring_help' => '<p>Automatisk send klienter de samme fakturaene ugentlig, bi-månedlig, månedlig, kvartalsvis eller årlig.</p>
<p>Brug :MONTH, :QUARTER eller :YEAR for dynamiske datoer. Grundliggende matematik fungerer også, for eksempel :MONTH-1.</p>
<p>Eksempler dynamiske faktura variabler:</p>
<ul>
<li>"Træningsmedlemskab for måneden :MONTH" => "Træningsmedlemskab for måneden Juli"</li>
<li>":YEAR+1 årlig abonnement" => "2015 årlig abonnement"</li>
<li>"Forudbetaling for :QUARTER+1" => "Forudbetaling for Q2"</li>
</ul>',
// dashboard
'in_total_revenue' => 'totale indtægter',
'billed_client' => 'faktureret klient',
'billed_clients' => 'fakturerede klienter',
'active_client' => 'aktiv klient',
'active_clients' => 'aktive klienter',
'invoices_past_due' => 'Forfaldte fakturaer',
'upcoming_invoices' => 'Forestående fakturaer',
'average_invoice' => 'Gennemsnitlige fakturaer',
// list pages
'archive' => 'Arkiv',
'delete' => 'Slet',
'archive_client' => 'Arkiver klient',
'delete_client' => 'Slet klient',
'archive_payment' => 'Arkiver betaling',
'delete_payment' => 'Slet betaling',
'archive_credit' => 'Arkiver kredit',
'delete_credit' => 'Slet kredit',
'show_archived_deleted' => 'Vis slettet/arkiverede',
'filter' => 'Filter',
'new_client' => 'Ny klient',
'new_invoice' => 'Ny faktura',
'new_payment' => 'Ny betaling',
'new_credit' => 'Ny kredit',
'contact' => 'Kontakt',
'date_created' => 'Dato oprette',
'last_login' => 'Sidst på-logging',
'balance' => 'Balanse',
'action' => 'Handling',
'status' => 'Status',
'invoice_total' => 'Faktura total',
'frequency' => 'Frekvens',
'start_date' => 'Startdato',
'end_date' => 'Slutdato',
'transaction_reference' => 'Transaktionsreference',
'method' => 'Betalingsmåde',
'payment_amount' => 'Beløb',
'payment_date' => 'Betalingsdato',
'credit_amount' => 'Kreditbeløb',
'credit_balance' => 'Kreditsaldo',
'credit_date' => 'Kreditdato',
'empty_table' => 'Ingen data er tilgængelige i tabellen',
'select' => 'Vælg',
'edit_client' => 'Rediger klient',
'edit_invoice' => 'Rediger faktura',
// client view page
'create_invoice' => 'Lag faktura',
'enter_credit' => 'Tilføj kredit',
'last_logged_in' => 'Sidst på-logget',
'details' => 'Detaljer',
'standing' => 'Stående',
'credit' => 'Kredit',
'activity' => 'Aktivitet',
'date' => 'Dato',
'message' => 'Besked',
'adjustment' => 'Justering',
'are_you_sure' => 'Er du sikker?',
// payment pages
'payment_type_id' => 'Betalingsmetode',
'amount' => 'Beløb',
// account/company pages
'work_email' => 'E-mail',
'language_id' => 'Sprog',
'timezone_id' => 'Tidszone',
'date_format_id' => 'Dato format',
'datetime_format_id' => 'Dato/Tidsformat',
'users' => 'Brugere',
'localization' => 'Lokalisering',
'remove_logo' => 'Fjern logo',
'logo_help' => 'Understøttede filtyper: JPEG, GIF og PNG. Anbefalet størrelse: 200px brede og 120px højde',
'payment_gateway' => 'Betalingsløsning',
'gateway_id' => 'Tilbyder',
'email_notifications' => 'Varsel via e-mail',
'email_sent' => 'Varsle når en faktura er <b>sendt</b>',
'email_viewed' => 'Varsle når en faktura er <b>set</b>',
'email_paid' => 'Varsle når en faktura er <b>betalt</b>',
'site_updates' => 'Webside opdateringer',
'custom_messages' => 'Tilpassede meldinger',
'default_invoice_terms' => 'Sæt standard fakturavilkår',
'default_email_footer' => 'Sæt standard e-mailsignatur',
'import_clients' => 'Importer klientdata',
'csv_file' => 'Vælg CSV-fil',
'export_clients' => 'Eksporter klientdata',
'select_file' => 'Venligst vælg en fil',
'first_row_headers' => 'Brug første række som overskrifter',
'column' => 'Kolonne',
'sample' => 'Eksempel',
'import_to' => 'Importer til',
'client_will_create' => 'Klient vil blive oprettet',
'clients_will_create' => 'Klienter vil blive oprettet',
// application messages
'created_client' => 'Klient oprettet succesfuldt',
'created_clients' => 'Klienter oprettet succesfuldt',
'updated_settings' => 'Indstiller opdateret',
'removed_logo' => 'Logo fjernet',
'sent_message' => 'Melding sendt',
'invoice_error' => 'Venligst vælge en klient og rette eventuelle fejl',
'limit_clients' => 'Desværre, dette vil overstige græsen på :count klienter',
'payment_error' => 'Det opstod en fejl under din betaling. Venligst prøv igen senere.',
'registration_required' => 'Venligst registrer dig for at sende e-mailfaktura',
'confirmation_required' => 'Venligst bekræft din e-mail',
'updated_client' => 'Klient opdateret',
'created_client' => 'Klient lagret',
'archived_client' => 'Klient arkiveret',
'archived_clients' => 'Arkiverede :count klienter',
'deleted_client' => 'Klient slettet',
'deleted_clients' => 'Slettet :count klienter',
'updated_invoice' => 'Faktura opdateret',
'created_invoice' => 'Faktura oprettet',
'cloned_invoice' => 'Faktura kopieret',
'emailed_invoice' => 'E-mailfaktura sendt',
'and_created_client' => 'og klient oprettet',
'archived_invoice' => 'Faktura arkiveret',
'archived_invoices' => 'Arkiverede :count fakturaer',
'deleted_invoice' => 'Faktura slettet',
'deleted_invoices' => 'Slettet :count fakturaer',
'created_payment' => 'Betaling oprettet',
'archived_payment' => 'Betaling arkiveret',
'archived_payments' => 'Arkiverede :count betalinger',
'deleted_payment' => 'Betaling slettet',
'deleted_payments' => 'Slettet :count betalinger',
'applied_payment' => 'Betaling lagret',
'created_credit' => 'Kredit oprettet',
'archived_credit' => 'Kredit arkiveret',
'archived_credits' => 'Arkiverede :count kreditter',
'deleted_credit' => 'Kredit slettet',
'deleted_credits' => 'Slettet :count kreditter',
// Emails
'confirmation_subject' => 'Invoice Ninja kontobekræftelse',
'confirmation_header' => 'Kontobekræftelse',
'confirmation_message' => 'Venligst åbne linket nedenfor for å bekræfte din konto.',
'invoice_subject' => 'Ny faktura fra :account',
'invoice_message' => 'For at se din faktura på :amount, klik på linket nedenfor.',
'payment_subject' => 'Betaling modtaget',
'payment_message' => 'Tak for din betaling pålydende :amount.',
'email_salutation' => 'Kære :name,',
'email_signature' => 'Med venlig hilsen,',
'email_from' => 'The InvoiceNinja Team',
'user_email_footer' => 'For at justere varslingsindstillingene venligst besøg '.SITE_URL.'/company/notifications',
'invoice_link_message' => 'Hvis du vil se din klientfaktura klik på linket under:',
'notification_invoice_paid_subject' => 'Faktura :invoice betalt af :client',
'notification_invoice_sent_subject' => 'Faktura :invoice sendt til :client',
'notification_invoice_viewed_subject' => 'Faktura :invoice set av :client',
'notification_invoice_paid' => 'En betaling pålydende :amount blev gjort af :client for faktura :invoice.',
'notification_invoice_sent' => 'E-mail er blevet sendt til :client - Faktura :invoice pålydende :amount.',
'notification_invoice_viewed' => ':client har set faktura :invoice pålydende :amount.',
'reset_password' => 'Du kan nulstille din adgangskode ved at besøge følgende link:',
'reset_password_footer' => 'Hvis du ikke bad om at få nulstillet din adgangskode venligst kontakt kundeservice: ' . CONTACT_EMAIL,
// Payment page
'secure_payment' => 'Sikker betaling',
'card_number' => 'Kortnummer',
'expiration_month' => 'Udløbsdato',
'expiration_year' => 'Udløbsår',
'cvv' => 'CVV',
// Security alerts
'confide' => [
'too_many_attempts' => 'For mange forsøg. Prøv igen om nogen få minutter.',
'wrong_credentials' => 'Fejl e-mail eller adgangskode.',
'confirmation' => 'Din konto har blevet bekræftet!',
'wrong_confirmation' => 'Fejl bekræftelseskode.',
'password_forgot' => 'Informationen om nulstilling af din adgangskode er blevet sendt til din e-mail.',
'password_reset' => 'Adgangskode ændret',
'wrong_password_reset' => 'Ugyldig adgangskode. Prøv på ny',
],
// Pro Plan
'pro_plan' => [
'remove_logo' => ':link for å fjerne Invoice Ninja-logoen, oppgrader til en Pro Plan',
'remove_logo_link' => 'Klik her',
],
'logout' => 'Log ud',
'sign_up_to_save' => 'Registrer dig for at gemme dit arbejde',
'agree_to_terms' =>'Jeg acceptere Invoice Ninja :terms',
'terms_of_service' => 'vilkår for brug',
'email_taken' => 'E-mailadressen er allerede registreret',
'working' => 'Arbejder',
'success' => 'Succes',
'success_message' => 'Du er blevet registreret. Venligst gå ind på linket som du har modtaget i e-mailbekræftelsen for at bekræfte e-mailaddressen.',
'erase_data' => 'Dette vil permanent slette dine data.',
'password' => 'Adgangskode',
'pro_plan_product' => 'Pro Plan',
'pro_plan_description' => 'Et års indmelding i Invoice Ninja Pro Plan.',
'pro_plan_success' => 'Tak for at du valgte Invoice Ninja\'s Pro plan!<p/>&nbsp;<br/>
<b>De næste skridt</b><p/>En betalbar faktura er send til e-emailaddesse
som er tilknyttet din konto. For at låse op for alle de utrolige
Pro-funktioner, kan du følge instruktionerne fakturaen til at
betale for et år med Pro-niveau funktionerer.<p/>
Kan du ikke finde fakturaen? Har behov for mere hjælp? Vi hjælper dig gerne om det skulle være noget
-- kontakt os contact@invoiceninja.com',
'unsaved_changes' => 'Du har ugemente ændringer',
'custom_fields' => 'Egendefineret felt',
'company_fields' => 'Selskabets felt',
'client_fields' => 'Klientens felt',
'field_label' => 'Felt etikette',
'field_value' => 'Feltets værdi',
'edit' => 'Rediger',
'set_name' => 'Sæt dit firmanavn',
'view_as_recipient' => 'Vis som modtager',
// product management
'product_library' => 'Produktbibliotek',
'product' => 'Produkt',
'products' => 'Produkter',
'fill_products' => 'Automatisk-udfyld produkter',
'fill_products_help' => 'Valg af produkt vil automatisk udfylde <b>beskrivelse og pris</b>',
'update_products' => 'Automatisk opdatering af produkter',
'update_products_help' => 'En opdatering af en faktura vil automatisk <b>opdaterer Produktbiblioteket</b>',
'create_product' => 'Opret nyt produkt',
'edit_product' => 'Rediger produkt',
'archive_product' => 'Arkiver produkt',
'updated_product' => 'Produkt opdateret',
'created_product' => 'Produkt lagret',
'archived_product' => 'Produkt arkiveret',
'pro_plan_custom_fields' => ':link for at aktivere egendefinerede felter ved at bliver medlem af Pro Plan',
'advanced_settings' => 'avancerede indstillinger',
'pro_plan_advanced_settings' => ':link for at aktivere avancerede indstillinger felter ved at bliver medlem af Pro Plan',
'invoice_design' => 'Fakturadesign',
'specify_colors' => 'Egendefineret farve',
'specify_colors_label' => 'Vælg farver som bruges i fakturaen',
'chart_builder' => 'Diagram bygger',
'ninja_email_footer' => 'Brug :sitet til at fakturere dine kunder og få betalt på nettet - gratis!',
'go_pro' => 'Vælg Pro',
// Quotes
'quote' => 'Pristilbud',
'quotes' => 'Pristilbud',
'quote_number' => 'Tilbuds nummer',
'quote_number_short' => 'Tilbuds #',
'quote_date' => 'Tilbudsdato',
'quote_total' => 'Tilbud total',
'your_quote' => 'Dit tilbud',
'total' => 'Total',
'clone' => 'Kopier',
'new_quote' => 'Nyt tilbud',
'create_quote' => 'Gem tilbud',
'edit_quote' => 'Rediger tilbud',
'archive_quote' => 'Arkiver tilbud',
'delete_quote' => 'Slet tilbud',
'save_quote' => 'Lagre tilbud',
'email_quote' => 'E-mail tilbudet',
'clone_quote' => 'Kopier tilbud',
'convert_to_invoice' => 'Konverter til en faktura',
'view_invoice' => 'Se faktura',
'view_client' => 'Vis klient',
'view_quote' => 'Se tilbud',
'updated_quote' => 'Tilbud opdatert',
'created_quote' => 'Tilbud oprettet',
'cloned_quote' => 'Tilbud kopieret',
'emailed_quote' => 'Tilbud sendt som e-mail',
'archived_quote' => 'Tilbud arkiveret',
'archived_quotes' => 'Arkiverede :count tilbud',
'deleted_quote' => 'Tilbud slettet',
'deleted_quotes' => 'Slettet :count tilbud',
'converted_to_invoice' => 'Tilbud konverteret til faktura',
'quote_subject' => 'Nyt tilbud fra :account',
'quote_message' => 'For å se dit tilbud pålydende :amount, klik på linket nedenfor.',
'quote_link_message' => 'Hvis du vil se din klients tilbud, klik på linket under:',
'notification_quote_sent_subject' => 'Tilbud :invoice sendt til :client',
'notification_quote_viewed_subject' => 'Tilbudet :invoice er set af :client',
'notification_quote_sent' => 'Følgende klient :client blev sendt tilbudsfaktura :invoice pålydende :amount.',
'notification_quote_viewed' => 'Følgende klient :client har set tilbudsfakturaen :invoice pålydende :amount.',
'session_expired' => 'Session er udløbet.',
'invoice_fields' => 'Faktura felt',
'invoice_options' => 'Faktura alternativer',
'hide_quantity' => 'Skjul antal',
'hide_quantity_help' => 'Hvis du altid kun har 1 som antal på dine fakturalinjer på fakturaen, kan du vælge ikke at vise antal på fakturaen.',
'hide_paid_to_date' => 'Skjul delbetalinger',
'hide_paid_to_date_help' => 'Vis kun delbetalinger hvis der er forekommet en delbetaling.',
'charge_taxes' => 'Inkluder skat',
'user_management' => 'Brugerhåndtering',
'add_user' => 'Tilføj bruger',
'send_invite' => 'Send invitation',
'sent_invite' => 'Invitation sendt',
'updated_user' => 'Bruger opdateret',
'invitation_message' => 'Du har blevet inviteret af :invitor. ',
'register_to_add_user' => 'Venligst registrer dig for at oprette en bruger',
'user_state' => 'Status',
'edit_user' => 'Rediger bruger',
'delete_user' => 'Slet bruger',
'active' => 'Aktiv',
'pending' => 'Afventer',
'deleted_user' => 'Bruger slettet',
'limit_users' => 'Desværre, vil dette overstiger grænsen på ' . MAX_NUM_USERS . ' brugere',
'confirm_email_invoice' => 'Er du sikker på at du vil sende denne faktura?',
'confirm_email_quote' => 'Er du sikker på du ville sende dette tilbud?',
'confirm_recurring_email_invoice' => 'Gentagende er slået til, er du sikker på du vil have denne faktura sendt?',
'cancel_account' => 'Annuller konto',
'cancel_account_message' => 'Advarsel: Dette ville slette alle dine data og kan ikke omgøres',
'go_back' => 'Go Back',
'data_visualizations' => 'Data visualisering',
'sample_data' => 'Eksempel data vis',
'hide' => 'Skjul',
'new_version_available' => 'En ny version af :releases_link er tilgængelig. Din nuværende version er v:user_version, den nyeste version er v:latest_version',
'invoice_settings' => 'Faktura indstillinger',
'invoice_number_prefix' => 'Faktura nummer præfiks',
'invoice_number_counter' => 'Faktura nummer tæller',
'quote_number_prefix' => 'Tilbuds nummer præfiks',
'quote_number_counter' => 'Tilbuds nummer tæller',
'share_invoice_counter' => 'Del faktura nummer tæller',
'invoice_issued_to' => 'Faktura udstedt til',
'invalid_counter' => 'For at undgå mulige overlap, sæt venligst et faktura eller tilbuds nummer præfiks',
'mark_sent' => 'Markering sendt',
);

106
app/lang/da/validation.php Normal file
View File

@ -0,0 +1,106 @@
<?php
return array(
/*
|--------------------------------------------------------------------------
| Validation Language Lines
|--------------------------------------------------------------------------
|
| The following language lines contain the default error messages used by
| the validator class. Some of these rules have multiple versions such
| such as the size rules. Feel free to tweak each of these messages.
|
*/
"accepted" => ":attribute skal accepteres.",
"active_url" => ":attribute er ikke en valid URL.",
"after" => ":attribute skal være en dato efter :date.",
"alpha" => ":attribute må kun bestå af bogstaver.",
"alpha_dash" => ":attribute må kun bestå af bogstaver, tal og bindestreger.",
"alpha_num" => ":attribute må kun bestå af bogstaver og tal.",
"array" => ":attribute skal være et array.",
"before" => ":attribute skal være en dato før :date.",
"between" => array(
"numeric" => ":attribute skal være imellem :min - :max.",
"file" => ":attribute skal være imellem :min - :max kilobytes.",
"string" => ":attribute skal være imellem :min - :max tegn.",
"array" => ":attribute skal indeholde mellem :min - :max elementer."
),
"boolean" => ":attribute skal være sandt eller falsk",
"confirmed" => ":attribute er ikke det samme som bekræftelsesfeltet.",
"date" => ":attribute er ikke en gyldig dato.",
"date_format" => ":attribute matcher ikke formatet :format.",
"different" => ":attribute og :other skal være forskellige.",
"digits" => ":attribute skal have :digits cifre.",
"digits_between" => ":attribute skal have mellem :min og :max cifre.",
"email" => ":attribute skal være en gyldig e-mailadresse.",
"exists" => "Det valgte :attribute er ugyldig.",
"image" => ":attribute skal være et billede.",
"in" => "Det valgte :attribute er ugyldig.",
"integer" => ":attribute skal være et heltal.",
"ip" => ":attribute skal være en gyldig IP adresse.",
"max" => array(
"numeric" => ":attribute skal være højest :max.",
"file" => ":attribute skal være højest :max kilobytes.",
"string" => ":attribute skal være højest :max tegn.",
"array" => ":attribute må ikke indeholde mere end :max elementer."
),
"mimes" => ":attribute skal være en fil af typen: :values.",
"min" => array(
"numeric" => ":attribute skal være mindst :min.",
"file" => ":attribute skal være mindst :min kilobytes.",
"string" => ":attribute skal være mindst :min tegn.",
"array" => ":attribute skal indeholde mindst :min elementer."
),
"not_in" => "Den valgte :attribute er ugyldig.",
"numeric" => ":attribute skal være et tal.",
"regex" => ":attribute formatet er ugyldigt.",
"required" => ":attribute skal udfyldes.",
"required_if" => ":attribute skal udfyldes når :other er :value.",
"required_with" => ":attribute skal udfyldes når :values er udfyldt.",
"required_with_all" => ":attribute skal udfyldes når :values er udfyldt.",
"required_without" => ":attribute skal udfyldes når :values ikke er udfyldt.",
"required_without_all" => ":attribute skal udfyldes når ingen af :values er udfyldt.",
"same" => ":attribute og :other skal være ens.",
"size" => array(
"numeric" => ":attribute skal være :size.",
"file" => ":attribute skal være :size kilobytes.",
"string" => ":attribute skal være :size tegn lang.",
"array" => ":attribute skal indeholde :size elementer."
),
"timezone" => "The :attribute must be a valid zone.",
"unique" => ":attribute er allerede taget.",
"url" => ":attribute formatet er ugyldigt.",
/*
|--------------------------------------------------------------------------
| Custom Validation Language Lines
|--------------------------------------------------------------------------
|
| Here you may specify custom validation messages for attributes using the
| convention "attribute.rule" to name the lines. This makes it quick to
| specify a specific custom language line for a given attribute rule.
|
*/
'custom' => array(
'attribute-name' => array(
'rule-name' => 'custom-message',
),
),
/*
|--------------------------------------------------------------------------
| Custom Validation Attributes
|--------------------------------------------------------------------------
|
| The following language lines are used to swap attribute place-holders
| with something more reader friendly such as E-Mail Address instead
| of "email". This simply helps us make messages a little cleaner.
|
*/
'attributes' => array(),
);

View File

@ -1,4 +1,4 @@
<?php
<?php
return array(
@ -117,11 +117,11 @@ return array(
'billed_client' => 'billed client',
'billed_clients' => 'billed clients',
'active_client' => 'active client',
'active_clients' => 'active clients',
'active_clients' => 'active clients',
'invoices_past_due' => 'Invoices Past Due',
'upcoming_invoices' => 'Upcoming invoices',
'average_invoice' => 'Average invoice',
// list pages
'archive' => 'Archive',
'delete' => 'Delete',
@ -267,7 +267,7 @@ return array(
'notification_invoice_viewed_subject' => 'Invoice :invoice was viewed by :client',
'notification_invoice_paid' => 'A payment of :amount was made by client :client towards Invoice :invoice.',
'notification_invoice_sent' => 'The following client :client was emailed Invoice :invoice for :amount.',
'notification_invoice_viewed' => 'The following client :client viewed Invoice :invoice for :amount.',
'notification_invoice_viewed' => 'The following client :client viewed Invoice :invoice for :amount.',
'reset_password' => 'You can reset your account password by clicking the following link:',
'reset_password_footer' => 'If you did not request this password reset please email our support: ' . CONTACT_EMAIL,
@ -275,10 +275,10 @@ return array(
// Payment page
'secure_payment' => 'Secure Payment',
'card_number' => 'Card number',
'expiration_month' => 'Expiration month',
'expiration_month' => 'Expiration month',
'expiration_year' => 'Expiration year',
'cvv' => 'CVV',
// Security alerts
'confide' => [
'too_many_attempts' => 'Too many attempts. Try again in few minutes.',
@ -289,7 +289,7 @@ return array(
'password_reset' => 'Your password has been changed successfully.',
'wrong_password_reset' => 'Invalid password. Try again',
],
// Pro Plan
'pro_plan' => [
'remove_logo' => ':link to remove the Invoice Ninja logo by joining the Pro Plan',
@ -310,11 +310,11 @@ return array(
'pro_plan_product' => 'Pro Plan',
'pro_plan_description' => 'One year enrollment in the Invoice Ninja Pro Plan.',
'pro_plan_success' => 'Thanks for choosing Invoice Ninja\'s Pro plan!<p/>&nbsp;<br/>
<b>Next Steps</b><p/>A payable invoice has been sent to the email
address associated with your account. To unlock all of the awesome
Pro features, please follow the instructions on the invoice to pay
<b>Next Steps</b><p/>A payable invoice has been sent to the email
address associated with your account. To unlock all of the awesome
Pro features, please follow the instructions on the invoice to pay
for a year of Pro-level invoicing.<p/>
Can\'t find the invoice? Need further assistance? We\'re happy to help
Can\'t find the invoice? Need further assistance? We\'re happy to help
-- email us at contact@invoiceninja.com',
'unsaved_changes' => 'You have unsaved changes',
@ -340,7 +340,7 @@ return array(
'archive_product' => 'Archive Product',
'updated_product' => 'Successfully updated product',
'created_product' => 'Successfully created product',
'archived_product' => 'Successfully archived product',
'archived_product' => 'Successfully archived product',
'pro_plan_custom_fields' => ':link to enable custom fields by joining the Pro Plan',
'advanced_settings' => 'Advanced Settings',
@ -393,8 +393,8 @@ return array(
'notification_quote_sent_subject' => 'Quote :invoice was sent to :client',
'notification_quote_viewed_subject' => 'Quote :invoice was viewed by :client',
'notification_quote_sent' => 'The following client :client was emailed Quote :invoice for :amount.',
'notification_quote_viewed' => 'The following client :client viewed Quote :invoice for :amount.',
'notification_quote_viewed' => 'The following client :client viewed Quote :invoice for :amount.',
'session_expired' => 'Your session has expired.',
'invoice_fields' => 'Invoice Fields',
@ -440,7 +440,7 @@ return array(
'quote_number_counter' => 'Quote Number Counter',
'share_invoice_counter' => 'Share invoice counter',
'invoice_issued_to' => 'Invoice issued to',
'invalid_counter' => 'To prevent a possible conflict please set either an invoice or quote number prefix',
'invalid_counter' => 'To prevent a possible conflict please set either an invoice or quote number prefix',
'mark_sent' => 'Mark sent',
'gateway_help_1' => ':link to sign up for Authorize.net.',
@ -457,7 +457,7 @@ return array(
'more_designs_self_host_text' => '',
'buy' => 'Buy',
'bought_designs' => 'Successfully added additional invoice designs',
'sent' => 'sent',
);
'timesheets' => 'Timesheets',
);

View File

@ -0,0 +1,119 @@
<?php
class TimesheetUtils
{
public static function parseEventSummary($summary) {
if (preg_match('/^\s*([^\s:\/]+)(?:\/([^:]+))?\s*:\s*([^)].*$|$)$/s', $summary, $matches)) {
return [strtoupper($matches[1]), strtolower($matches[2]), $matches[3]];
} else {
return false;
}
}
public static function parseICALEvent($eventstr) {
if (preg_match_all('/(?:^|\r?\n)([^;:]+)[;:]([^\r\n]+)/s', $eventstr, $matches)) {
// Build ICAL event array
$data = ['summary' => ''];
foreach ($matches[1] as $i => $key) {
# Convert escaped linebreakes to linebreak
$value = preg_replace("/\r?\n\s/", "", $matches[2][$i]);
# Unescape , and ;
$value = preg_replace('/\\\\([,;])/s', '$1', $value);
$data[strtolower($key)] = $value;
}
return $data;
} else {
return false;
}
}
public static function parseICALDate($datestr) {
$dt = null;
$timezone = null;
if (preg_match('/^TZID=(.+?):([12]\d\d\d)(\d\d)(\d\d)T(\d\d)(\d\d)(\d\d)$/', $datestr, $m)) {
$timezone = $m[1];
$dt = new DateTime("{$m[2]}-{$m[3]}-{$m[4]}T{$m[5]}:{$m[6]}:{$m[7]}", new DateTimeZone($m[1]));
} else if (preg_match('/^VALUE=DATE:([12]\d\d\d)(\d\d)(\d\d)$/', $datestr, $m)) {
$dt = new DateTime("{$m[1]}-{$m[2]}-{$m[3]}T00:00:00", new DateTimeZone("UTC"));
} else if (preg_match('/^([12]\d\d\d)(\d\d)(\d\d)T(\d\d)(\d\d)(\d\d)Z$/', $datestr, $m)) {
$dt = new DateTime("{$m[1]}-{$m[2]}-{$m[3]}T{$m[4]}:{$m[5]}:{$m[6]}", new DateTimeZone("UTC"));
} else {
return false;
}
// Convert all to UTC
if($dt->getTimezone()->getName() != 'UTC') {
$dt->setTimezone(new DateTimeZone('UTC'));
}
return [$dt, $timezone];
}
public static function curlGetUrls($urls = [], $timeout = 30) {
// Create muxer
$results = [];
$multi = curl_multi_init();
$handles = [];
$ch2idx = [];
try {
foreach ($urls as $i => $url) {
// Create new handle and add to muxer
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_ENCODING, "gzip");
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); //timeout in seconds
curl_multi_add_handle($multi, $ch);
$handles[(int) $ch] = $ch;
$ch2idx[(int) $ch] = $i;
}
// Do initial connect
$still_running = true;
while ($still_running) {
// Do curl stuff
while (($mrc = curl_multi_exec($multi, $still_running)) === CURLM_CALL_MULTI_PERFORM);
if ($mrc !== CURLM_OK) {
break;
}
// Try to read from handles that are ready
while ($info = curl_multi_info_read($multi)) {
if ($info["result"] == CURLE_OK) {
$results[$ch2idx[(int) $info["handle"]]] = curl_multi_getcontent($info["handle"]);
} else {
if (CURLE_UNSUPPORTED_PROTOCOL == $info["result"]) {
$results[$ch2idx[(int) $info["handle"]]] = [$info["result"], "Unsupported protocol"];
} else if (CURLE_URL_MALFORMAT == $info["result"]) {
$results[$ch2idx[(int) $info["handle"]]] = [$info["result"], "Malform url"];
} else if (CURLE_COULDNT_RESOLVE_HOST == $info["result"]) {
$results[$ch2idx[(int) $info["handle"]]] = [$info["result"], "Could not resolve host"];
} else if (CURLE_OPERATION_TIMEDOUT == $info["result"]) {
$results[$ch2idx[(int) $info["handle"]]] = [$info["result"], "Timed out waiting for operations to finish"];
} else {
$results[$ch2idx[(int) $info["handle"]]] = [$info["result"], "Unknown curl error code"];
}
}
}
// Sleep until
if (($rs = curl_multi_select($multi)) === -1) {
usleep(20); // select failed for some reason, so we sleep for 20ms and run some more curl stuff
}
}
} finally {
foreach ($handles as $chi => $ch) {
curl_multi_remove_handle($multi, $ch);
}
curl_multi_close($multi);
}
return $results;
}
}

View File

@ -31,6 +31,11 @@ class Client extends EntityModel
{
return $this->hasMany('Contact');
}
public function projects()
{
return $this->hasMany('Project');
}
public function country()
{
@ -125,6 +130,19 @@ class Client extends EntityModel
return $str;
}
public function getVatNumber()
{
$str = '';
if ($this->work_phone)
{
$str .= '<i class="fa fa-vat-number" style="width: 20px"></i>' . $this->vat_number;
}
return $str;
}
public function getNotes()
{

View File

@ -94,7 +94,8 @@ class Invoice extends EntityModel
'custom_taxes2']);
$this->client->setVisible([
'name',
'name',
'vat_number',
'address1',
'address2',
'city',
@ -109,7 +110,8 @@ class Invoice extends EntityModel
'custom_value2']);
$this->account->setVisible([
'name',
'name',
'vat_number',
'address1',
'address2',
'city',

51
app/models/Project.php Normal file
View File

@ -0,0 +1,51 @@
<?php
class Project extends Eloquent
{
public $timestamps = true;
protected $softDelete = true;
public function account()
{
return $this->belongsTo('Account');
}
public function user()
{
return $this->belongsTo('User');
}
public function client()
{
return $this->belongsTo('Client');
}
public function codes()
{
return $this->hasMany('ProjectCode');
}
public static function createNew($parent = false)
{
$className = get_called_class();
$entity = new $className();
if ($parent)
{
$entity->user_id = $parent instanceof User ? $parent->id : $parent->user_id;
$entity->account_id = $parent->account_id;
}
else if (Auth::check())
{
$entity->user_id = Auth::user()->id;
$entity->account_id = Auth::user()->account_id;
}
else
{
Utils::fatalError();
}
return $entity;
}
}

View File

@ -0,0 +1,50 @@
<?php
class ProjectCode extends Eloquent
{
public $timestamps = true;
protected $softDelete = true;
public function account()
{
return $this->belongsTo('Account');
}
public function user()
{
return $this->belongsTo('User');
}
public function project()
{
return $this->belongsTo('Project');
}
public function events()
{
return $this->hasMany('TimesheetEvent');
}
public static function createNew($parent = false)
{
$className = get_called_class();
$entity = new $className();
if ($parent)
{
$entity->user_id = $parent instanceof User ? $parent->id : $parent->user_id;
$entity->account_id = $parent->account_id;
}
else if (Auth::check())
{
$entity->user_id = Auth::user()->id;
$entity->account_id = Auth::user()->account_id;
}
else
{
Utils::fatalError();
}
return $entity;
}
}

22
app/models/Timesheet.php Executable file
View File

@ -0,0 +1,22 @@
<?php
class Timesheet extends Eloquent
{
public $timestamps = true;
protected $softDelete = true;
public function account()
{
return $this->belongsTo('Account');
}
public function user()
{
return $this->belongsTo('User');
}
public function timesheet_events()
{
return $this->hasMany('TimeSheetEvent');
}
}

View File

@ -0,0 +1,129 @@
<?php
class TimesheetEvent extends Eloquent
{
public $timestamps = true;
protected $softDelete = true;
/* protected $dates = array('org_updated_at');
public function getDates() {
return array('created_at', 'updated_at', 'deleted_at');
} */
/* public function setOrgUpdatedAtAttribute($value)
{
var_dump($value);
$this->attributes['org_updated_at'] = $value->getTimestamp();
}*/
public function account()
{
return $this->belongsTo('Account');
}
public function user()
{
return $this->belongsTo('User');
}
public function source()
{
return $this->belongsTo('TimesheetEventSource');
}
public function timesheet()
{
return $this->belongsTo('Timesheet');
}
public function project()
{
return $this->belongsTo('Project');
}
public function project_code()
{
return $this->belongsTo('ProjectCode');
}
/**
* @return TimesheetEvent
*/
public static function createNew($parent = false)
{
$className = get_called_class();
$entity = new $className();
if ($parent)
{
$entity->user_id = $parent instanceof User ? $parent->id : $parent->user_id;
$entity->account_id = $parent->account_id;
}
else if (Auth::check())
{
$entity->user_id = Auth::user()->id;
$entity->account_id = Auth::user()->account_id;
}
else
{
Utils::fatalError();
}
return $entity;
}
public function toChangesArray(TimesheetEvent $other)
{
$attributes_old = parent::toArray();
$attributes_new = $other->toArray();
$skip_keys = ['id' => 1, 'created_at' => 1, 'updated_at' => 1, 'deleted_at' => 1, 'org_data' => 1, 'update_data' => 1];
$zeroisempty_keys = ['discount' => 1];
$result = [];
// Find all the values that where changed or deleted
foreach ($attributes_old as $key => $value) {
// Skip null values, keys we don't care about and 0 value keys that means they are not used
if(empty($value) || isset($skip_keys[$key])|| (isset($zeroisempty_keys[$key]) && $value) ) {
continue;
}
// Compare values if it exists in the new array
if(isset($attributes_new[$key]) || array_key_exists($key, $attributes_new)) {
if($value instanceof \DateTime && $attributes_new[$key] instanceof \DateTime) {
if($value != $attributes_new[$key]) {
$result[$key] = $attributes_new[$key]->format("Y-m-d H:i:s");
}
} elseif($value instanceof \DateTime && is_string($attributes_new[$key])) {
if($value->format("Y-m-d H:i:s") != $attributes_new[$key]) {
$result[$key] = $attributes_new[$key];
}
} elseif(is_string($value) && $attributes_new[$key] instanceof \DateTime) {
if($attributes_new[$key]->format("Y-m-d H:i:s") != $value) {
$result[$key] = $attributes_new[$key]->format("Y-m-d H:i:s");
}
} elseif($value != $attributes_new[$key]) {
$result[$key] = $attributes_new[$key];
}
} else {
$result[$key] = null;
}
}
// Find all the values that where deleted
foreach ($attributes_new as $key => $value) {
if(isset($skip_keys[$key])) {
continue;
}
if(!isset($attributes_old[$key])) {
$result[$key] = $value;
}
}
return $result;
}
}

View File

@ -0,0 +1,46 @@
<?php
class TimesheetEventSource extends Eloquent
{
public $timestamps = true;
protected $softDelete = true;
public function account()
{
return $this->belongsTo('Account');
}
public function user()
{
return $this->belongsTo('User');
}
public function events()
{
return $this->hasMany('TimesheetEvent');
}
public static function createNew($parent = false)
{
$className = get_called_class();
$entity = new $className();
if ($parent)
{
$entity->user_id = $parent instanceof User ? $parent->id : $parent->user_id;
$entity->account_id = $parent->account_id;
}
else if (Auth::check())
{
$entity->user_id = Auth::user()->id;
$entity->account_id = Auth::user()->account_id;
}
else
{
Utils::fatalError();
}
return $entity;
}
}

View File

@ -62,6 +62,9 @@ class ClientRepository
if (isset($data['name'])) {
$client->name = trim($data['name']);
}
if (isset($data['vat_number'])) {
$client->vat_number = trim($data['vat_number']);
}
if (isset($data['work_phone'])) {
$client->work_phone = trim($data['work_phone']);
}

View File

@ -131,6 +131,8 @@ Route::group(array('before' => 'auth'), function()
Route::get('credits/create/{client_id?}/{invoice_id?}', 'CreditController@create');
Route::get('api/credits/{client_id?}', array('as'=>'api.credits', 'uses'=>'CreditController@getDatatable'));
Route::post('credits/bulk', 'CreditController@bulk');
//Route::resource('timesheets', 'TimesheetController');
});
// Route group for API

View File

@ -14,3 +14,4 @@
Artisan::resolve('SendRecurringInvoices');
Artisan::resolve('CreateRandomData');
Artisan::resolve('ResetData');
Artisan::resolve('ImportTimesheetData');

View File

@ -10,8 +10,7 @@ class ExampleTest extends TestCase {
public function testBasicExample()
{
$crawler = $this->client->request('GET', '/');
$this->assertTrue($this->client->getResponse()->isOk());
$this->assertTrue($this->client->getResponse()->isRedirect());
}
}

View File

@ -0,0 +1,26 @@
<?php
class TimesheetUtilTest extends \PHPUnit_Framework_TestCase {
public function testParseEventSummary() {
list($code, $codes, $title) = TimesheetUtils::parseEventSummary('Riga :)');
$this->assertSame(null, $code);
list($code, $tags, $title) = TimesheetUtils::parseEventSummary('Test:');
$this->assertSame("TEST", $code);
list($code, $tags, $title) = TimesheetUtils::parseEventSummary('Test: ');
$this->assertSame("TEST", $code);
list($code, $tags, $title) = TimesheetUtils::parseEventSummary('Test::');
$this->assertSame("TEST", $code);
list($code, $tags, $title) = TimesheetUtils::parseEventSummary('TEST: Hello :)');
$this->assertSame("TEST", $code);
list($code, $tags, $title) = TimesheetUtils::parseEventSummary('Test/tags: ');
$this->assertSame('TEST', $code);
$this->assertSame('tags', $tags);
}
}

View File

@ -29,6 +29,7 @@
{{ Former::legend('details') }}
{{ Former::text('name') }}
{{ Former::text('vat_number') }}
{{ Former::text('work_email') }}
{{ Former::text('work_phone') }}
{{ Former::file('logo')->max(2, 'MB')->accept('image')->inlineHelp(trans('texts.logo_help')) }}

View File

@ -4,6 +4,7 @@
{{ Former::legend('Organization') }}
{{ Former::text('name') }}
{{ Former::text('vat_number') }}
{{ Former::text('work_phone')->label('Phone') }}
{{ Former::textarea('notes') }}

View File

@ -23,7 +23,8 @@
{{ Former::legend('organization') }}
{{ Former::text('name')->data_bind("attr { placeholder: placeholderName }") }}
{{ Former::text('website') }}
{{ Former::text('vat_number') }}
{{ Former::text('website') }}
{{ Former::text('work_phone') }}
@if (Auth::user()->isPro())

View File

@ -39,7 +39,8 @@
<div class="col-md-3">
<h3>{{ trans('texts.details') }}</h3>
<p>{{ $client->getAddress() }}</p>
<p>{{ $client->getVatNumber() }}</p>
<p>{{ $client->getAddress() }}</p>
<p>{{ $client->getCustomFields() }}</p>
<p>{{ $client->getPhone() }}</p>
<p>{{ $client->getNotes() }}</p>

View File

@ -5,7 +5,7 @@
<style type="text/css">
html, body {
height: 100%
height: 100%;
vertical-align: middle;
}

View File

@ -341,6 +341,8 @@
{{ Former::legend('organization') }}
{{ Former::text('name')->data_bind("value: name, valueUpdate: 'afterkeydown', attr { placeholder: name.placeholder }") }}
{{ Former::text('vat_number')->data_bind("value: vat_number, valueUpdate: 'afterkeydown'") }}
{{ Former::text('website')->data_bind("value: website, valueUpdate: 'afterkeydown'") }}
{{ Former::text('work_phone')->data_bind("value: work_phone, valueUpdate: 'afterkeydown'") }}
@ -1220,6 +1222,7 @@
var self = this;
self.public_id = ko.observable(0);
self.name = ko.observable('');
self.vat_number = ko.observable('');
self.work_phone = ko.observable('');
self.custom_value1 = ko.observable('');
self.custom_value2 = ko.observable('');
@ -1551,15 +1554,15 @@
}
model.invoice().addItem();
//model.addTaxRate();
@else
@else
// TODO: Add the first tax rate for new invoices by adding a new db field to the tax codes types to set the default
//if(model.invoice_taxes() && model.tax_rates().length > 2) {
// var tax = model.tax_rates()[1];
// model.invoice().tax(tax);
//}
model.invoice().custom_taxes1({{ $account->custom_invoice_taxes1 ? 'true' : 'false' }});
model.invoice().custom_taxes2({{ $account->custom_invoice_taxes2 ? 'true' : 'false' }});
@endif
// Add the first tax rate for new invoices
//if(model.invoice_taxes() && model.tax_rates().length > 0) {
// var tax = model.tax_rates()[0];
// model.invoice().tax(tax);
//}
@endif
model.invoice().tax(model.getTaxRate(model.invoice().tax_name(), model.invoice().tax_rate()));

View File

@ -21,13 +21,13 @@
<center id="designThumbs">
<p>&nbsp;</p>
<a href="/images/designs/business.png" data-lightbox="more-designs" data-title="Business"><img src="/images/designs/business_thumb.png"/></a>&nbsp;&nbsp;&nbsp;&nbsp;
<a href="/images/designs/creative.png" data-lightbox="more-designs" data-title="Creative"><img src="/images/designs/creative_thumb.png"/></a>&nbsp;&nbsp;&nbsp;&nbsp;
<a href="/images/designs/elegant.png" data-lightbox="more-designs" data-title="Elegant"><img src="/images/designs/elegant_thumb.png"/></a>
<a href="{{ asset('/images/designs/business.png') }}" data-lightbox="more-designs" data-title="Business"><img src="{{ asset('/images/designs/business_thumb.png') }}"/></a>&nbsp;&nbsp;&nbsp;&nbsp;
<a href="{{ asset('/images/designs/creative.png') }}" data-lightbox="more-designs" data-title="Creative"><img src="{{ asset('/images/designs/creative_thumb.png') }}"/></a>&nbsp;&nbsp;&nbsp;&nbsp;
<a href="{{ asset('/images/designs/elegant.png') }}" data-lightbox="more-designs" data-title="Elegant"><img src="{{ asset('/images/designs/elegant_thumb.png') }}"/></a>
<p>&nbsp;</p>
<a href="/images/designs/hipster.png" data-lightbox="more-designs" data-title="Hipster"><img src="/images/designs/hipster_thumb.png"/></a>&nbsp;&nbsp;&nbsp;&nbsp;
<a href="/images/designs/playful.png" data-lightbox="more-designs" data-title="Playful"><img src="/images/designs/playful_thumb.png"/></a>&nbsp;&nbsp;&nbsp;&nbsp;
<a href="/images/designs/photo.png" data-lightbox="more-designs" data-title="Photo"><img src="/images/designs/photo_thumb.png"/></a>
<a href="{{ asset('/images/designs/hipster.png') }}" data-lightbox="more-designs" data-title="Hipster"><img src="{{ asset('/images/designs/hipster_thumb.png') }}"/></a>&nbsp;&nbsp;&nbsp;&nbsp;
<a href="{{ asset('/images/designs/playful.png') }}" data-lightbox="more-designs" data-title="Playful"><img src="{{ asset('/images/designs/playful_thumb.png') }}"/></a>&nbsp;&nbsp;&nbsp;&nbsp;
<a href="{{ asset('/images/designs/photo.png') }}" data-lightbox="more-designs" data-title="Photo"><img src="{{ asset('/images/designs/photo_thumb.png') }}"/></a>
<p>&nbsp;</p>
</center>

View File

@ -0,0 +1,76 @@
@extends('header')
@section('head')
@parent
@stop
@section('content')
<ol class="breadcrumb">
<li>{{ link_to('timesheets', trans('texts.timesheets')) }}</li>
<li class='active'>{{ 1 }}</li>
</ol>
<div id="timesheet_table" style="height: 800px; overflow: auto">
</div>
<script type="text/javascript">
var table = $("#timesheet_table");
table.handsontable({
colHeaders: false,//['','Date', 'From', 'To', 'Hours', 'Desciption', 'Owner', 'Project', 'Code', 'Tags' ],
startRows: 6,
startCols: 4,
minSpareRows: 1,
stretchH: 'last',
colWidths: [25, 97, 50, 50, 50, 400, 150, 150, 50,50],
columns: [
{ data: "selected", type: 'checkbox' },
{ data: "date", type: 'date', dateFormat: 'yy-mm-dd' },
{ data: "from" },
{ data: "to" },
{ data: "hours", type: 'numeric', format: '0.00' },
{ data: "summary" },
{ data: "owner" },
{ data: "project", type: 'dropdown', source: ["Hobby", "Large Development"], strict: true },
{ data: "code", type: 'dropdown', source: ["DAMCO", "MYDAMCO", "SAXO", "STUDIE"], strict: true },
{ data: "tags", type: 'dropdown', source: ["design", "development", "bug"], strict: false },
],
//cells: function(row, col, prop) {
// return {
// type : {
// renderer : function (instance, td, row, col, prop, value, cellProperties) {
// }
// }
// };
//}
});
var data = [
{ selected: true, date:'2014-01-01', from:'10:00', to:'11:00', hours:'1', summary: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut vitae aliquet risus. Etiam posuere urna dictum ipsum imperdiet ullamcorper. Integer suscipit arcu sed iaculis tristique', owner:'Troels Liebe Bentsen', project:'Hobby', code:'TEST', tags:'design'},
{ selected: true, date:'2014-01-02', from:'00:00', to:'23:00', hours:'11', summary: 'Suspendisse tellus nunc, vehicula eget purus eu, tempor volutpat ligula. ', owner:'Troels Liebe Bentsen', project:'Hobby', code:'TEST', tags:'design'},
{ selected: true, date:'2014-01-03', from:'10:00', to:'11:00', hours:'1', summary: 'Quisque id facilisis tellus. Curabitur diam ante, venenatis sit amet lectus vitae, viverra bibendum lectus.', owner:'Troels Liebe Bentsen', project:'Hobby', code:'TEST', tags:'design'},
{ selected: true, date:'2014-01-04', from:'23:00', to:'24:00', hours:'1', summary: 'Do some stuff', owner:'Troels Liebe Bentsen', project:'Hobby', code:'TEST', tags:'design'},
{ selected: true, date:'2014-01-01', from:'10:00', to:'11:00', hours:'1', summary: 'Morbi commodo tellus justo, id elementum est blandit sit amet.', owner:'Troels Liebe Bentsen', project:'Hobby', code:'TEST', tags:'design'},
{ selected: true, date:'2014-01-02', from:'00:00', to:'23:00', hours:'11', summary: 'Do some stuff', owner:'Troels Liebe Bentsen', project:'Hobby', code:'TEST', tags:'design'},
{ selected: true, date:'2014-01-03', from:'10:00', to:'11:00', hours:'1', summary: 'Do some stuff', owner:'Troels Liebe Bentsen', project:'Hobby', code:'TEST', tags:'design'},
{ selected: true, date:'2014-01-04', from:'23:00', to:'24:00', hours:'1', summary: 'Mauris elementum mi non tincidunt facilisisin', owner:'Troels Liebe Bentsen', project:'Hobby', code:'TEST', tags:'design'},
{ selected: true, date:'2014-01-01', from:'10:00', to:'11:00', hours:'1', summary: 'Redo inital load as the CDC cleanup task is failing and it might be due to DDL changes that the CDC service did not handle', owner:'Troels Liebe Bentsen', project:'Hobby', code:'TEST', tags:'design'},
];
table.handsontable("loadData", data);
/*
$.ajax({
url: "/api/events",
dataType: 'json',
type: 'GET',
success: function (res) {
$("#dataTable").data("handsontable").loadData(res);
}
});
*/
</script>
@stop

View File

@ -18,7 +18,8 @@
"accounting": "~0.*",
"spectrum": "~1.3.4",
"d3": "~3.4.11",
"lightbox2": "~2.7.1"
"lightbox2": "~2.7.1",
"handsontable": "*"
},
"resolutions": {
"jquery": "~1.11"

View File

@ -8,17 +8,20 @@
"name": "Hillel Coren",
"email": "hillelcoren@gmail.com"
}
],
],
"require": {
"laravel/framework": "4.1.25",
"patricktalmadge/bootstrapper": "dev-develop",
"patricktalmadge/bootstrapper": "4.1.x",
"zizaco/confide": "3.1.x",
"anahkiasen/former": "3.4.x",
"barryvdh/laravel-debugbar": "dev-master",
"chumper/datatable": "2.x",
"omnipay/omnipay": "~2.0",
"intervention/image": "1.x",
"webpatser/laravel-countries": "dev-master"
"webpatser/laravel-countries": "dev-master",
"barryvdh/laravel-ide-helper": "1.*",
"doctrine/dbal": "~2.3",
"jsanc623/phpbenchtime": "2.x"
},
"require-dev": {
"codeception/codeception": "dev-master"
@ -45,8 +48,9 @@
],
"post-update-cmd": [
"php artisan clear-compiled",
"php artisan optimize",
"php artisan debugbar:publish"
"php artisan ide-helper:generate",
"php artisan debugbar:publish",
"php artisan optimize"
],
"post-create-project-cmd": [
"php artisan key:generate"

810
composer.lock generated

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -6,7 +6,7 @@ body {
color: #2e2b2b;
}
.center-block {
margin: 0 auto!;
margin: 0 auto !important;
float: none;
}
.valgin {padding: 0; margin: 0;}
@ -254,7 +254,7 @@ a .cta:hover span {
.hero1.background {
background-repeat: no-repeat;
background-position: center center;
background-attachment: static;
background-attachment: fixed;
background-size: cover;
min-height: 500px;
}
@ -280,7 +280,7 @@ a .cta:hover span {
padding-right: 15px;
position: relative;
padding: 10px 35px 20px 35px;
xheight: 212px;
height: 212px;
border-left: 1px dotted #ccc;
margin-top: 100px;
text-align: left;
@ -785,7 +785,7 @@ footer.footer form#mad_mimi_signup_form .form-group {
position: relative;
}
footer.footer form#mad_mimi_signup_form input {
padding: right: 60px;
padding-right: 60px;
background: #393636;
border: none;
color: #b7b7b6;

View File

@ -637,6 +637,7 @@ function displayAccount(doc, invoice, x, y, layout) {
var data1 = [
account.name,
account.vat_number,
account.work_email,
account.work_phone
];
@ -674,6 +675,7 @@ function displayClient(doc, invoice, x, y, layout) {
}
var data = [
getClientDisplayName(client),
client.vat_number,
concatStrings(client.address1, client.address2),
concatStrings(client.city, client.state, client.postal_code),
client.country ? client.country.name : false,
@ -1503,4 +1505,4 @@ function roundToTwo(num, toString) {
function truncate(str, length) {
return (str && str.length > length) ? (str.substr(0, length-1) + '...') : str;
}
}