|
|
|
@@ -289,22 +289,44 @@ |
|
|
|
do_edit_job(id); |
|
|
|
}); |
|
|
|
|
|
|
|
// $(document).on('click', 'div.bts_editor div.ult-overlay-close', function(){ |
|
|
|
// $('.Editing').addClass('blink_me'); |
|
|
|
// setTimeout(function(){ |
|
|
|
// $(".Editing").removeClass('Editing blink_me'); |
|
|
|
// }, 1000); |
|
|
|
// |
|
|
|
// }); |
|
|
|
|
|
|
|
$(document).on('click', 'div.bts_editor div.ult-overlay-close', function(){ |
|
|
|
$('.Editing').addClass('blink_me'); |
|
|
|
var el = $('.Editing'); |
|
|
|
el.addClass("blink_me").removeClass('Editing'); |
|
|
|
setTimeout(function(){ |
|
|
|
$(".Editing").removeClass('Editing blink_me'); |
|
|
|
}, 1000); |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
$(document).on('click', 'div.bts_editor div.bsave span.ticon-save', function(){ |
|
|
|
close_editor(); |
|
|
|
el.removeClass('blink_me'); |
|
|
|
}, 600); |
|
|
|
}); |
|
|
|
function close_editor(){ |
|
|
|
$('div.bts_editor div.ult-overlay-close').trigger('click'); |
|
|
|
} |
|
|
|
$(document).on('jobEditor:close', 'div.divTable.Editor', function(e,job){ |
|
|
|
console.log('close_editor event %o', job); |
|
|
|
$(document).on('jobEditor:close', 'div.divTable.Editor', function(e,data){ |
|
|
|
var editor = data.editor; |
|
|
|
var job = data.job; |
|
|
|
//console.log('close_editor event %o, %o', editor, job); |
|
|
|
|
|
|
|
var templ = $("#jobv1_item").html(); |
|
|
|
var html = Mustache.render(templ, {jobs:job}); //job id should be available; |
|
|
|
if (job.is_new){ |
|
|
|
$('div.workspace').append(html); |
|
|
|
$('#job_'+job.id).get(0).scrollIntoView(); |
|
|
|
}else{ |
|
|
|
//create new job underneath it |
|
|
|
var templ = $("#jobv1_item").html(); |
|
|
|
var html = Mustache.render(templ, {jobs:job}); //job id should be available; |
|
|
|
$('div.jobTable.Editing').after(html).remove(); |
|
|
|
} |
|
|
|
var el = $('#job_' + job.id).addClass("blink_me"); |
|
|
|
setTimeout(function(){ |
|
|
|
el.removeClass('blink_me'); |
|
|
|
}, 1500); |
|
|
|
}); |
|
|
|
|
|
|
|
$(document).on('click', 'div.divTableHead.bdelete span.ticon-trash', function(){ |
|
|
|
@@ -354,10 +376,21 @@ |
|
|
|
} |
|
|
|
|
|
|
|
function add_new_empty_job(){ |
|
|
|
var o = new Job({empty:true}); |
|
|
|
$('div.workspace').append(o.el); |
|
|
|
o.el.get(0).scrollIntoView(); |
|
|
|
dtp_init(); |
|
|
|
var job = new Job({empty:true}); |
|
|
|
job.is_new = true; |
|
|
|
job.editorid = Math.floor(Math.random() * Math.floor(99999)); // a random number; |
|
|
|
|
|
|
|
//show editor |
|
|
|
var html = $('#jobv1_editor').html(); |
|
|
|
html = Mustache.render(html, job); |
|
|
|
set_modal_content('editor', html); |
|
|
|
//update GUI |
|
|
|
open_modal('editor'); |
|
|
|
set_modal_title('editor', "Create New Job "); |
|
|
|
dtp_init(); |
|
|
|
//init editor |
|
|
|
var e = new JobEditor('#editor_' + job.editorid, job); |
|
|
|
console.log("%o is instance of JobEditor %o", e, e instanceof JobEditor); |
|
|
|
} |
|
|
|
$(document).on('click', 'div[name="copyschedule"]', function(e){ |
|
|
|
e.stopPropagation(); |
|
|
|
@@ -426,11 +459,172 @@ |
|
|
|
newj.mark_highlight_me(1000);//for 1 second; |
|
|
|
dtp_init(); |
|
|
|
}); |
|
|
|
class Job{}; |
|
|
|
class Job{ |
|
|
|
constructor(record) |
|
|
|
{ |
|
|
|
this.original_attr = Object.keys(record); |
|
|
|
$.extend(this, record); |
|
|
|
this.derive_attr(); |
|
|
|
} |
|
|
|
|
|
|
|
get_record() |
|
|
|
{//without extended attributes; |
|
|
|
var self = this; |
|
|
|
var r = {}; |
|
|
|
this.original_attr.forEach(function(attr){ |
|
|
|
r[attr] = self[attr]; |
|
|
|
}); |
|
|
|
return r; |
|
|
|
} |
|
|
|
|
|
|
|
derive_attr() |
|
|
|
{ |
|
|
|
this.tos_name(this); |
|
|
|
this.staff_name(this); |
|
|
|
this.client_name(this); |
|
|
|
this.rate_name(this); |
|
|
|
this.rating_range(this); |
|
|
|
this.identify_job_week(this); |
|
|
|
this.job_acked(this); |
|
|
|
} |
|
|
|
|
|
|
|
tos_name(e){ |
|
|
|
if (typeof bts().tos[e.tos] != 'undefined'){ |
|
|
|
e.tos_name = bts().tos[e.tos].tos_full_str; |
|
|
|
}else{ |
|
|
|
e.tos_name = "(deleted) "; |
|
|
|
e.tos_err="Missing: " + e.tos; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
staff_name(e){ |
|
|
|
if (typeof bts().staff_map[e.staff] != 'undefined'){ |
|
|
|
e.staff_name = bts().staff_map[e.staff].display_name; |
|
|
|
}else{ |
|
|
|
e.staff_name = "(deleted) "; |
|
|
|
e.staff_err="Missing: " + e.staff; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
client_name(e){ |
|
|
|
if (typeof bts().client_map[e.client] != 'undefined'){ |
|
|
|
e.client_name = bts().client_map[e.client].display_name; |
|
|
|
}else{ |
|
|
|
e.client_name ="(deleted)"; |
|
|
|
e.client_err ="Missing: " + e.client; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
rate_name(e){ |
|
|
|
if (typeof bts().earnings_rate[e.rate] != 'undefined') { |
|
|
|
e.rate_name = bts().earnings_rate[e.rate].RatePerUnit + "-" + bts().earnings_rate[e.rate].Name; |
|
|
|
if (! has_txt_hour( bts().earnings_rate[e.rate].TypeOfUnits )){ |
|
|
|
e.rate_non_hour = true; |
|
|
|
e.rate_err = `Rate unit must be ⟦ Hours ⟧ |
|
|
|
Possible solution: |
|
|
|
1. Change it in Xero |
|
|
|
2. Delete this job`; |
|
|
|
} |
|
|
|
}else{ |
|
|
|
e.rate_name = "(deleted)"; |
|
|
|
e.rate_err = "Missing: " + e.rate; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
rating_range(e){ |
|
|
|
if (e.rating <0 || e.rating >5){ |
|
|
|
e.rating_err = "Rating 0-5 Only"; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
identify_job_week(e){ |
|
|
|
if (job_is_week1(e.start)){ |
|
|
|
e.is_week1=true; |
|
|
|
}else if (job_is_week2(e.start)){ |
|
|
|
e.is_week2=true; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
job_acked(e){ |
|
|
|
if (typeof e == 'undefined'){ |
|
|
|
console.log('break;'); |
|
|
|
} |
|
|
|
if (e.ack != 0){ |
|
|
|
e.is_confirmed = true; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
is_job_valid(){ |
|
|
|
|
|
|
|
var error_found = false; |
|
|
|
var self = this; |
|
|
|
this.original_attr.forEach(function(attr){ |
|
|
|
var col = attr + "_err"; |
|
|
|
if (typeof self[col] == 'undefined') |
|
|
|
return; |
|
|
|
if (self[attr+"_err"] != "") |
|
|
|
error_found = true; |
|
|
|
}); |
|
|
|
|
|
|
|
return !error_found; |
|
|
|
} |
|
|
|
|
|
|
|
is_high_pay() |
|
|
|
{ |
|
|
|
var job = this; |
|
|
|
var rate_info = bts().earnings_rate[job.rate]; |
|
|
|
|
|
|
|
var keywords =bts().high_pay_keywords; |
|
|
|
var found = false; |
|
|
|
keywords.forEach(function(e){ |
|
|
|
if (-1 != rate_info.Name.toLowerCase().indexOf(e.toLowerCase()) ) |
|
|
|
found = true; |
|
|
|
}); |
|
|
|
return found; |
|
|
|
} |
|
|
|
|
|
|
|
get_payment_summary() |
|
|
|
{ |
|
|
|
var job = this; |
|
|
|
var result ={}; |
|
|
|
result.ot = job.is_high_pay(); |
|
|
|
result.hour = job.get_working_duration(); |
|
|
|
result.money = job.get_wages(); |
|
|
|
return result; |
|
|
|
} |
|
|
|
|
|
|
|
get_working_duration() |
|
|
|
{ |
|
|
|
var job = this; |
|
|
|
//finish - start |
|
|
|
var f = new Date(job.finish); |
|
|
|
var s = new Date(job.start); |
|
|
|
var diff = f.getTime() - s.getTime(); |
|
|
|
var hours = Math.floor(diff / 1000 / 60 / 60); |
|
|
|
diff -= hours * 1000 * 60 * 60; |
|
|
|
var minutes = Math.floor(diff / 1000 / 60); |
|
|
|
var minute_to_hour = minutes/60; |
|
|
|
return (hours + minute_to_hour); |
|
|
|
} |
|
|
|
|
|
|
|
get_wages() |
|
|
|
{ |
|
|
|
var job = this; |
|
|
|
var hour = job.get_working_duration(job); |
|
|
|
var rate_info = bts().earnings_rate[job.rate]; |
|
|
|
if ( has_txt_hour( bts().earnings_rate[job.rate].TypeOfUnits ) ) |
|
|
|
{ |
|
|
|
return hour * rate_info.RatePerUnit; |
|
|
|
}else{ |
|
|
|
return 1 * rate_info.RatePerUnit; |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
class JobEditor{ //save data for the record, and display it as GUI |
|
|
|
constructor(selector, data){ |
|
|
|
constructor(selector, job){ |
|
|
|
this.el = $(selector); |
|
|
|
this.load_data(data); |
|
|
|
this.load_data(job); |
|
|
|
console.assert(job instanceof Job); |
|
|
|
this.init_event_handler(); |
|
|
|
} |
|
|
|
|
|
|
|
@@ -455,39 +649,47 @@ |
|
|
|
//draw GUI by other |
|
|
|
this.mark_dirty_on_new_record(data); |
|
|
|
this.mark_week_color(); |
|
|
|
this.validate(); //also triggers mark errors |
|
|
|
//this.validate(); //also triggers mark errors |
|
|
|
} |
|
|
|
init_event_handler(){ |
|
|
|
var self = this; |
|
|
|
this.el.find("div.btos select").change(function(){ |
|
|
|
if ( self.validate_tos() ) { |
|
|
|
self.data.tos = self.get_tos(); |
|
|
|
self.set_err_msg_tos(''); |
|
|
|
} |
|
|
|
}); |
|
|
|
this.el.find("div.bstart input").change(function(){ |
|
|
|
if (self.validate_start()){ |
|
|
|
self.data.start = self.get_start(); |
|
|
|
self.set_err_msg_start(''); |
|
|
|
self.validate_start_and_finish(); |
|
|
|
} |
|
|
|
}); |
|
|
|
this.el.find("div.bfinish input").change(function(){ |
|
|
|
if (self.validate_finish()){ |
|
|
|
self.data.finish = self.get_finish(); |
|
|
|
self.set_err_msg_finish(''); |
|
|
|
self.validate_start_and_finish(); |
|
|
|
} |
|
|
|
}); |
|
|
|
this.el.find("div.bstaff select").change(function(){ |
|
|
|
if (self.validate_staff()){ |
|
|
|
self.data.staff = self.get_staff(); |
|
|
|
self.set_err_msg_staff(''); |
|
|
|
} |
|
|
|
}); |
|
|
|
this.el.find("div.bclient select").change(function(){ |
|
|
|
if (self.validate_client()){ |
|
|
|
self.data.client = self.get_client(); |
|
|
|
self.set_err_msg_client(''); |
|
|
|
} |
|
|
|
}); |
|
|
|
|
|
|
|
this.el.find("div.brate select").change(function(){ |
|
|
|
if (self.validate_rate()){ |
|
|
|
self.data.rate = self.get_rate(); |
|
|
|
self.set_err_msg_rate(''); |
|
|
|
} |
|
|
|
}); |
|
|
|
this.el.find("div.bconfirmed input").change(function(){ |
|
|
|
@@ -502,8 +704,9 @@ |
|
|
|
}); |
|
|
|
this.el.find("div.bsave span.ticon-save").click(function(e){ |
|
|
|
if ( self.validate() ){ |
|
|
|
job_derive_attr(self.data); |
|
|
|
self.el.trigger('jobEditor:close', self.data); |
|
|
|
self.do_save_record(); |
|
|
|
}else{ |
|
|
|
self.set_err_msg_save('Data Error'); |
|
|
|
} |
|
|
|
}); |
|
|
|
} |
|
|
|
@@ -531,9 +734,11 @@ |
|
|
|
return; |
|
|
|
this.el.find('div.btos select option[value="'+val+'"]').prop('selected',true); |
|
|
|
if ( this.get_tos() != val ){ |
|
|
|
var o = new Option("(missing:" + this.data.tos +")", this.data.tos); |
|
|
|
this.set_err_msg_tos("Missing:" + this.data.tos) |
|
|
|
var o = new Option("(deleted)", this.data.tos); |
|
|
|
this.el.find('div.btos select').prepend(o); |
|
|
|
this.el.find('div.btos select option[value="'+this.data.tos+'"]').prop('selected',true); |
|
|
|
this.el.find('div.btos select option[value="'+this.data.tos+'"]').prop('selected',true); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
get_start(){ |
|
|
|
@@ -541,8 +746,10 @@ |
|
|
|
} |
|
|
|
set_start(val) |
|
|
|
{ |
|
|
|
if (typeof(val) =="undefined") |
|
|
|
if (typeof(val) =="undefined"){ |
|
|
|
this.set_err_msg_start("need start"); |
|
|
|
return; |
|
|
|
} |
|
|
|
this.el.find('div.bstart input').attr('value', val); |
|
|
|
} |
|
|
|
get_finish() |
|
|
|
@@ -551,10 +758,10 @@ |
|
|
|
} |
|
|
|
set_finish(val) |
|
|
|
{ |
|
|
|
if (typeof(val) == "undefined") |
|
|
|
return; |
|
|
|
if (typeof(val) == "undefined") |
|
|
|
if (typeof(val) == "undefined"){ |
|
|
|
this.set_err_msg_finish("need finish"); |
|
|
|
return; |
|
|
|
} |
|
|
|
this.el.find('div.bfinish input').attr('value', val); |
|
|
|
} |
|
|
|
get_rate() |
|
|
|
@@ -566,6 +773,13 @@ |
|
|
|
if (typeof(val) =="undefined") |
|
|
|
return; |
|
|
|
this.el.find('div.brate select option[value="'+val+'"]').prop('selected',true); |
|
|
|
|
|
|
|
if ( this.get_rate() != val ){ |
|
|
|
var o = new Option("(deleted)", this.data.rate); |
|
|
|
this.el.find('div.brate select').prepend(o); |
|
|
|
this.set_err_msg_rate('Missing:' + this.data.rate); |
|
|
|
this.el.find('div.brate select option[value="'+this.data.rate+'"]').prop('selected',true); |
|
|
|
} |
|
|
|
} |
|
|
|
get_staff() |
|
|
|
{ |
|
|
|
@@ -577,9 +791,9 @@ |
|
|
|
return; |
|
|
|
this.el.find('div.bstaff select option[value="'+val+'"]').prop('selected',true); |
|
|
|
if ( this.get_staff() != val ){ |
|
|
|
var o = new Option("(missing:" + this.data.staff +")", this.data.staff); |
|
|
|
var o = new Option("(deleted)", this.data.staff); |
|
|
|
this.el.find('div.bstaff select').prepend(o); |
|
|
|
this.set_err_msg_tos('missing' + this.data.staff); |
|
|
|
this.set_err_msg_staff('Missing:' + this.data.staff); |
|
|
|
this.el.find('div.bstaff select option[value="'+this.data.staff+'"]').prop('selected',true); |
|
|
|
} |
|
|
|
} |
|
|
|
@@ -592,6 +806,12 @@ |
|
|
|
if (typeof(val) =="undefined") |
|
|
|
return; |
|
|
|
this.el.find('div.bclient select option[value="'+val+'"]').prop('selected',true); |
|
|
|
if ( this.get_client() != val ){ |
|
|
|
var o = new Option("(deleted)", this.data.client); |
|
|
|
this.el.find('div.bclient select').prepend(o); |
|
|
|
this.set_err_msg_client('Missing:' + this.data.client); |
|
|
|
this.el.find('div.bclient select option[value="'+this.data.client+'"]').prop('selected',true); |
|
|
|
} |
|
|
|
} |
|
|
|
get_ack() |
|
|
|
{ |
|
|
|
@@ -631,13 +851,19 @@ |
|
|
|
$.post(bts().ajax_url, { // POST request |
|
|
|
_ajax_nonce: bts().nonce, // nonce |
|
|
|
action: "save_job", // action |
|
|
|
record: this.get_record_from_ui(), |
|
|
|
record: self.get_record_from_ui(), |
|
|
|
}, function(response, status, xhr){ |
|
|
|
if (response.status=='success'){ |
|
|
|
self.load_data(response.newdata); |
|
|
|
self.mark_saved(); |
|
|
|
self.mark_old(); |
|
|
|
var job = new Job(response.newdata); |
|
|
|
job.saved = true; |
|
|
|
job.is_new = response.isNew; |
|
|
|
bts().job_map[job.id] = job; |
|
|
|
var data = {editor:self, job:job}; |
|
|
|
self.el.trigger('jobEditor:close', data); |
|
|
|
close_editor(); |
|
|
|
}else{ |
|
|
|
self.self.set_err_msg_save('Not saved'); |
|
|
|
alert( 'error saving data, please check your network'); |
|
|
|
} |
|
|
|
}); |
|
|
|
@@ -723,7 +949,7 @@ |
|
|
|
validate() |
|
|
|
{ |
|
|
|
var ok_tos = this.validate_tos(); |
|
|
|
var ok_time = this.validate_start() && this.validate_finish(); //finish might not be executed, if start is wrong |
|
|
|
var ok_time = this.validate_start() && this.validate_finish() && this.validate_start_and_finish(); |
|
|
|
var ok_staff = this.validate_staff(); |
|
|
|
var ok_client = this.validate_client(); |
|
|
|
var ok_rate = this.validate_rate() ; //make sure this validate is executed |
|
|
|
@@ -739,33 +965,49 @@ |
|
|
|
|
|
|
|
validate_start(){ |
|
|
|
var str = this.get_start(); |
|
|
|
if ( is_valid_date_str(str) ){ |
|
|
|
this.mark_start_valid(); |
|
|
|
this.set_err_msg_start(''); |
|
|
|
return true; |
|
|
|
}else{ |
|
|
|
if (str == ""){ |
|
|
|
this.set_err_msg_start('need start'); |
|
|
|
return false; |
|
|
|
} |
|
|
|
if (!is_valid_date_str(str) ){ |
|
|
|
this.mark_start_invalid(); |
|
|
|
this.set_err_msg_start('wrong date'); |
|
|
|
return false; |
|
|
|
} |
|
|
|
return true; |
|
|
|
} |
|
|
|
validate_finish() |
|
|
|
{ |
|
|
|
var str = this.get_finish(); |
|
|
|
if (str == ""){ |
|
|
|
this.set_err_msg_finish('need finish'); |
|
|
|
return false; |
|
|
|
} |
|
|
|
if (! is_valid_date_str(str)){ |
|
|
|
this.set_err_msg_finish('wrong date'); |
|
|
|
this.mark_finish_invalid(); |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
validate_start_and_finish() |
|
|
|
{ |
|
|
|
if (! this.validate_start() || ! this.validate_finish()) |
|
|
|
return; |
|
|
|
if (!this.is_finish_resonable()){ |
|
|
|
this.set_err_msg_finish("older than start") |
|
|
|
this.set_err_msg_finish("older than start"); |
|
|
|
this.set_err_msg_start("after finish"); |
|
|
|
this.mark_start_invalid(); |
|
|
|
this.mark_finish_invalid(); |
|
|
|
return false; |
|
|
|
}else{ |
|
|
|
this.mark_start_valid(); |
|
|
|
this.mark_finish_valid(); |
|
|
|
this.set_err_msg_finish(""); |
|
|
|
this.set_err_msg_start(""); |
|
|
|
return true; |
|
|
|
} |
|
|
|
this.mark_finish_valid(); |
|
|
|
this.set_err_msg_finish(''); |
|
|
|
return true; |
|
|
|
} |
|
|
|
validate_rate() |
|
|
|
{ |
|
|
|
@@ -775,12 +1017,6 @@ |
|
|
|
this.mark_rate_invalid(); |
|
|
|
return false; |
|
|
|
} |
|
|
|
// if (this.get_rate() != this.data.rate){ |
|
|
|
// this.set_err_msg_rate('rate@Xero inactive ' + this.data.rate); |
|
|
|
// this.mark_rate_invalid(); |
|
|
|
// this.mark_dirty(); |
|
|
|
// return false; |
|
|
|
// } |
|
|
|
this.set_err_msg_rate(''); |
|
|
|
this.mark_rate_valid(); |
|
|
|
return true; |
|
|
|
@@ -806,10 +1042,16 @@ |
|
|
|
} |
|
|
|
} |
|
|
|
validate_client(){ |
|
|
|
return true; |
|
|
|
if ( typeof bts().client_map[this.get_client()] == 'undefined') { |
|
|
|
this.set_err_msg_client('missing ' + this.get_client()); |
|
|
|
return false; |
|
|
|
}else{ |
|
|
|
this.set_err_msg_client(''); |
|
|
|
return true; |
|
|
|
} |
|
|
|
} |
|
|
|
validate_rating(){ |
|
|
|
return true; |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
clear_err_msg(){ |
|
|
|
@@ -1334,16 +1576,20 @@ |
|
|
|
(typeof b.earnings_rate != "undefined" && Object.keys(b.earnings_rate).length > 0 )) |
|
|
|
{ |
|
|
|
var job_map={}; |
|
|
|
var jobs = []; |
|
|
|
//map data for each jobTable |
|
|
|
response.jobs.forEach(function(e){ |
|
|
|
job_derive_attr(e); |
|
|
|
job_map[e.id] = e; |
|
|
|
//job_derive_attr(e); |
|
|
|
var j = new Job(e) |
|
|
|
j.saved=true; //this is a record from database; |
|
|
|
job_map[e.id] = j; |
|
|
|
jobs.push(j); |
|
|
|
}); |
|
|
|
bts().job_map = job_map; |
|
|
|
|
|
|
|
//we do works, load timesheets |
|
|
|
var template = $("#jobv1_item").html(); |
|
|
|
var html = Mustache.render(template, response); |
|
|
|
var html = Mustache.render(template, {jobs:jobs}); |
|
|
|
$('div.workspace').append(html); |
|
|
|
hide_loading_jobs(); |
|
|
|
//filter it if reqired |
|
|
|
@@ -1356,63 +1602,8 @@ |
|
|
|
}, 500); //try it half seconds later |
|
|
|
} |
|
|
|
|
|
|
|
function job_derive_attr(e) |
|
|
|
{ |
|
|
|
clear_non_derive_attr(e); |
|
|
|
e.saved = true; |
|
|
|
|
|
|
|
if (typeof bts().tos[e.tos] != 'undefined'){ |
|
|
|
e.tos_name = bts().tos[e.tos].tos_full_str; |
|
|
|
}else{ |
|
|
|
e.tos_name = "(deleted) "; |
|
|
|
e.tos_err="Missing: " + e.tos; |
|
|
|
} |
|
|
|
|
|
|
|
if (typeof bts().staff_map[e.staff] != 'undefined'){ |
|
|
|
e.staff_name = bts().staff_map[e.staff].display_name; |
|
|
|
}else{ |
|
|
|
e.staff_name = "(deleted) "; |
|
|
|
e.staff_err="Missing: " + e.staff; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
e.client_name = bts().client_map[e.client].display_name; |
|
|
|
e.rate_name = bts().earnings_rate[e.rate].RatePerUnit + "-" + bts().earnings_rate[e.rate].Name; |
|
|
|
|
|
|
|
if (! has_txt_hour( bts().earnings_rate[e.rate].TypeOfUnits )){ |
|
|
|
e.rate_non_hour = true; |
|
|
|
e.rate_err = `Rate unit must be ⟦ Hours ⟧ |
|
|
|
Possible solution: |
|
|
|
1. Change it in Xero |
|
|
|
2. Delete this job`; |
|
|
|
} |
|
|
|
|
|
|
|
if (job_is_week1(e.start)){ |
|
|
|
e.is_week1=true; |
|
|
|
}else if (job_is_week2(e.start)){ |
|
|
|
e.is_week2=true; |
|
|
|
} |
|
|
|
|
|
|
|
if (e.ack != 0){ |
|
|
|
e.is_confirmed = true; |
|
|
|
} |
|
|
|
} |
|
|
|
function clear_non_derive_attr(e) |
|
|
|
{ |
|
|
|
delete e.saved; |
|
|
|
delete e.tos_name; |
|
|
|
delete e.tos_err; |
|
|
|
delete e.staff_name; |
|
|
|
delete e.staff_err; |
|
|
|
delete e.client_name; |
|
|
|
delete e.client_err; |
|
|
|
delete e.rate_name; |
|
|
|
delete e.rate_err; |
|
|
|
delete e.rate_non_hour; |
|
|
|
delete e.is_week1; |
|
|
|
delete e.is_week2; |
|
|
|
delete e.is_confirmed; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function has_txt_hour(str){ |
|
|
|
var s = str.toLowerCase(); |
|
|
|
@@ -1618,9 +1809,9 @@ |
|
|
|
|
|
|
|
var id = $(e).attr('data-id'); |
|
|
|
var job = bts().job_map[id]; |
|
|
|
if (typeof job === 'undefined') |
|
|
|
if (typeof job === 'undefined' || !job.is_job_valid() ) |
|
|
|
return; |
|
|
|
var ps = job_get_payment_summary(job); |
|
|
|
var ps = job.get_payment_summary(); |
|
|
|
|
|
|
|
pays.total += ps.money; |
|
|
|
pays.hours += ps.hour; |
|
|
|
@@ -1634,54 +1825,6 @@ |
|
|
|
set_working_hours(pays.hours.toFixed(2)); |
|
|
|
} |
|
|
|
|
|
|
|
function job_get_payment_summary(job) |
|
|
|
{ |
|
|
|
var result ={}; |
|
|
|
result.ot = job_get_is_high_pay(job); |
|
|
|
result.hour = job_get_working_duration(job); |
|
|
|
result.money = job_get_wages(job); |
|
|
|
return result; |
|
|
|
} |
|
|
|
|
|
|
|
function job_get_is_high_pay(job) |
|
|
|
{ |
|
|
|
var rate_info = bts().earnings_rate[job.rate]; |
|
|
|
|
|
|
|
var keywords =bts().high_pay_keywords; |
|
|
|
var found = false; |
|
|
|
keywords.forEach(function(e){ |
|
|
|
if (-1 != rate_info.Name.toLowerCase().indexOf(e.toLowerCase()) ) |
|
|
|
found = true; |
|
|
|
}); |
|
|
|
return found; |
|
|
|
} |
|
|
|
|
|
|
|
function job_get_working_duration(job) |
|
|
|
{ |
|
|
|
//finish - start |
|
|
|
var f = new Date(job.finish); |
|
|
|
var s = new Date(job.start); |
|
|
|
var diff = f.getTime() - s.getTime(); |
|
|
|
var hours = Math.floor(diff / 1000 / 60 / 60); |
|
|
|
diff -= hours * 1000 * 60 * 60; |
|
|
|
var minutes = Math.floor(diff / 1000 / 60); |
|
|
|
var minute_to_hour = minutes/60; |
|
|
|
return (hours + minute_to_hour); |
|
|
|
} |
|
|
|
|
|
|
|
function job_get_wages(job) |
|
|
|
{ |
|
|
|
var hour = job_get_working_duration(job); |
|
|
|
var rate_info = bts().earnings_rate[job.rate]; |
|
|
|
if ( has_txt_hour( bts().earnings_rate[job.rate].TypeOfUnits ) ) |
|
|
|
{ |
|
|
|
return hour * rate_info.RatePerUnit; |
|
|
|
}else{ |
|
|
|
return 1 * rate_info.RatePerUnit; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function find_staff(login) |
|
|
|
@@ -1884,7 +2027,8 @@ |
|
|
|
set_modal_title('editor', "Editing Job: " + id); |
|
|
|
|
|
|
|
//make a copy of the job |
|
|
|
var job_copy = $.extend({}, bts().job_map[id]); |
|
|
|
var child = bts().job_map[id]; |
|
|
|
var job_copy = Object.assign(Object.create(Object.getPrototypeOf(child)), child); //a shallow copy only; |
|
|
|
job_copy.editorid = Math.floor(Math.random() * Math.floor(99999)); // a random number; |
|
|
|
//set_modal_data('editor', {jobid: id, job_copy:job_copy}); |
|
|
|
var html = $('#jobv1_editor').html(); |
|
|
|
@@ -1893,7 +2037,8 @@ |
|
|
|
//update GUI |
|
|
|
dtp_init(); |
|
|
|
//init editor |
|
|
|
new JobEditor('#editor_' + job_copy.editorid, job_copy); |
|
|
|
var e = new JobEditor('#editor_' + job_copy.editorid, job_copy); |
|
|
|
//console.log("e is instance of JobEditor %o", e instanceof JobEditor); |
|
|
|
} |
|
|
|
|
|
|
|
|