';
popupData.content += '
";
popupData.content += '
';
popupData.content +=
'';
popupData.content += "
";
popupData.content +=
"
";
popupData.message = app.lang.task_stop_timer;
var $popupHTML = system_popup(popupData);
$popupHTML.attr("id", "timer-select-task");
init_ajax_search(
"tasks",
"#timer_add_task_id",
undefined,
admin_url + "tasks/ajax_search_assign_task_to_timer"
);
return false;
}
$(e).addClass("disabled");
var data = {};
data.task_id = task_id;
data.timer_id = timer_id;
data.note = $("body").find("#timesheet_note").val();
if (!data.note) {
data.note = "";
}
var taskModalVisible = $("#task-modal").is(":visible");
var reqUrl =
admin_url + "tasks/timer_tracking?single_task=" + taskModalVisible;
if (adminStop) {
reqUrl += "&admin_stop=" + adminStop;
}
$.post(reqUrl, data).done(function (response) {
response = JSON.parse(response);
// Timer action, stopping from staff/member/id
if ($("body").hasClass("member")) {
window.location.reload();
}
if (taskModalVisible) {
_task_append_html(response.taskHtml);
}
if ($timerSelectTask.is(":visible")) {
$timerSelectTask.find(".system-popup-close").click();
}
_init_timers_top_html(JSON.parse(response.timers));
$(".popover-top-timer-note").popover("hide");
reload_tasks_tables();
});
}
// Init task modal and get data from server
function init_task_modal(task_id, comment_id) {
var queryStr = "";
var $leadModal = $("#lead-modal");
var $taskAddEditModal = $("#_task_modal");
if ($leadModal.is(":visible")) {
queryStr +=
"?opened_from_lead_id=" + $leadModal.find('input[name="leadid"]').val();
$leadModal.modal("hide");
} else if ($taskAddEditModal.attr("data-lead-id") != undefined) {
queryStr +=
"?opened_from_lead_id=" + $taskAddEditModal.attr("data-lead-id");
}
requestGet("tasks/get_task_data/" + task_id + queryStr)
.done(function (response) {
_task_append_html(response);
if (typeof comment_id != "undefined") {
setTimeout(function () {
$('[data-task-comment-href-id="' + comment_id + '"]').click();
}, 1000);
}
})
.fail(function (data) {
$("#task-modal").modal("hide");
alert_float("danger", data.responseText);
});
}
// General function to append task html returned from request
function _task_append_html(html) {
var $taskModal = $("#task-modal");
$taskModal.find(".data").html(html);
//init_tasks_checklist_items(false, task_id);
recalculate_checklist_items_progress();
do_task_checklist_items_height();
setTimeout(function () {
$taskModal.modal("show");
// Init_tags_input is trigged too when task modal is shown
// This line prevents triggering twice.
if ($taskModal.is(":visible")) {
init_tags_inputs();
}
init_form_reminder("task");
fix_task_modal_left_col_height();
// Show the comment area on mobile when task modal is opened
// Because the user may want only to upload file, but if the comment textarea is not focused the dropzone won't be shown
if (is_mobile()) {
init_new_task_comment(true);
}
}, 150);
}
// Tracking stats modal from task single
function task_tracking_stats(id) {
requestGet("tasks/task_tracking_stats/" + id).done(function (response) {
$("
", {
id: "tracking-stats",
})
.appendTo("body")
.html(response);
$("#task-tracking-stats-modal").modal("toggle");
});
}
// Fetches all staff timers and append to DOM
function init_timers() {
requestGetJSON("tasks/get_staff_started_timers").done(function (response) {
_init_timers_top_html(response);
});
}
// Top started timers dropdown init html data with class
function _init_timers_top_html(data) {
var $tt = $("#top-timers");
var $ttIcon = $("#top-timers").find(".icon-started-timers");
data.total_timers > 0
? $ttIcon.removeClass("hide").html(data.total_timers)
: $ttIcon.addClass("hide");
data.total_timers > 0
? $("#top-timers").find("i").addClass("tw-animate-spin")
: $("#top-timers").find("i").removeClass("tw-animate-spin");
$("#started-timers-top").html(data.html);
}
// Init task edit comment
function edit_task_comment(id) {
var edit_wrapper = $('[data-edit-comment="' + id + '"]');
edit_wrapper.next().addClass("hide");
edit_wrapper.removeClass("hide");
tinymce.remove("#task_comment_" + id);
var editorConfig = _simple_editor_config();
editorConfig.auto_focus = "task_comment_" + id;
editorConfig.toolbar_sticky = true;
init_editor("#task_comment_" + id, editorConfig);
tinymce.triggerSave();
}
// Cancel editing commment after clicked on edit href
function cancel_edit_comment(id) {
var edit_wrapper = $('[data-edit-comment="' + id + '"]');
tinymce.remove('[data-edit-comment="' + id + '"] textarea');
edit_wrapper.addClass("hide");
edit_wrapper.next().removeClass("hide");
}
// Save task edited comment
function save_edited_comment(id, task_id) {
tinymce.triggerSave();
var data = {};
data.id = id;
data.task_id = task_id;
data.content = $('[data-edit-comment="' + id + '"]')
.find("textarea")
.val();
if (is_ios()) {
data.no_editor = true;
}
$.post(admin_url + "tasks/edit_comment", data).done(function (response) {
response = JSON.parse(response);
if (response.success === true || response.success == "true") {
alert_float("success", response.message);
_task_append_html(response.taskHtml);
} else {
cancel_edit_comment(id);
}
tinymce.remove('[data-edit-comment="' + id + '"] textarea');
});
}
// Fix task single modal height to be on both sides the same
function fix_task_modal_left_col_height() {
if (!is_mobile()) {
$("body")
.find(".task-single-col-left")
.css(
"min-height",
$("body").find(".task-single-col-right").outerHeight(true) + "px"
);
}
}
// Updates task when action performed form kan ban area eq status changed.
function tasks_kanban_update(ui, object) {
if (object === ui.item.parent()[0]) {
var status = $(ui.item.parent()[0]).attr("data-task-status-id");
var data = {
order: [],
status: status,
};
$.each($(ui.item.parent()[0]).find("[data-task-id]"), function (idx, el) {
var id = $(el).attr("data-task-id");
if (id) {
data.order.push([id, idx + 1]);
}
});
task_mark_as(status, $(ui.item).attr("data-task-id"));
check_kanban_empty_col("[data-task-id]");
setTimeout(function () {
$.post(admin_url + "tasks/update_order", data).done(function (response) {
update_kan_ban_total_when_moving(ui, data.status);
tasks_kanban();
});
}, 200);
}
}
// Init tasks kan ban
function tasks_kanban() {
init_kanban("tasks/kanban", tasks_kanban_update, ".tasks-status", 240, 360);
}
// Task single edit description with inline editor, used from task single modal
function edit_task_inline_description(e, id) {
tinyMCE.remove("#task_view_description");
if ($(e).hasClass("editor-initiated")) {
$(e).removeClass("editor-initiated");
return;
}
$(e).addClass("editor-initiated");
$.Shortcuts.stop();
tinymce.init({
branding: false,
toolbar: false,
menubar: false,
inline: true,
cache_suffix: '?v='+app.version,
selector: "#task_view_description",
theme: "silver",
directionality: isRTL == "true" ? "rtl" : "",
auto_focus: "task_view_description",
plugins: ['quickbars', 'link', 'table', (isRTL == "true" ? " directionality" : "")],
contextmenu: "link table paste pastetext",
quickbars_insert_toolbar: "quicktable",
quickbars_selection_toolbar: "bold italic | quicklink h2 h3 blockquote",
table_default_styles: {
width: "100%",
},
setup: function (editor) {
editor.on("blur", function (e) {
if (editor.isDirty()) {
$.post(admin_url + "tasks/update_task_description/" + id, {
description: editor.getContent(),
});
}
setTimeout(function () {
editor.remove();
$.Shortcuts.start();
}, 500);
});
},
});
}
// Tasks bulk actions action
function tasks_bulk_action(event) {
if (confirm_delete()) {
var ids = [],
data = {},
mass_delete = $("#mass_delete").prop("checked");
if (mass_delete == false || typeof mass_delete == "undefined") {
data.status = $("#move_to_status_tasks_bulk_action").val();
var assignees = $("#task_bulk_assignees");
data.assignees = assignees.length ? assignees.selectpicker("val") : "";
var tags_bulk = $("#tags_bulk");
data.tags = tags_bulk.length ? tags_bulk.tagit("assignedTags") : "";
var milestone = $("#task_bulk_milestone");
data.milestone = milestone.length ? milestone.selectpicker("val") : "";
data.billable = $("#task_bulk_billable").val();
data.billable = typeof data.billable == "undefined" ? "" : data.billable;
data.priority = $("#task_bulk_priority").val();
data.priority = typeof data.priority == "undefined" ? "" : data.priority;
if (
data.status === "" &&
data.priority === "" &&
data.tags === "" &&
data.assignees === "" &&
data.milestone === "" &&
data.billable === ""
) {
return;
}
} else {
data.mass_delete = true;
}
var rows = $($("#tasks_bulk_actions").attr("data-table")).find("tbody tr");
$.each(rows, function () {
var checkbox = $($(this).find("td").eq(0)).find("input");
if (checkbox.prop("checked") === true) {
ids.push(checkbox.val());
}
});
data.ids = ids;
$(event).addClass("disabled");
setTimeout(function () {
$.post(admin_url + "tasks/bulk_action", data).done(function () {
window.location.reload();
});
}, 200);
}
}
function load_small_table_item(id, selector, input_name, url, table) {
var _tmpID = $('input[name="' + input_name + '"]').val();
// Check if id passed from url, hash is prioritized becuase is last
if (_tmpID !== "" && !window.location.hash) {
id = _tmpID;
// Clear the current id value in case user click on the left sidebar credit_note_ids
$('input[name="' + input_name + '"]').val("");
} else {
// check first if hash exists and not id is passed, becuase id is prioritized
if (window.location.hash && !id) {
id = window.location.hash.substring(1); //Puts hash in variable, and removes the # character
}
}
if (typeof id == "undefined" || id === "") {
return;
}
destroy_dynamic_scripts_in_element($(selector));
if (!$("body").hasClass("small-table")) {
toggle_small_view(table, selector);
}
$('input[name="' + input_name + '"]').val(id);
do_hash_helper(id);
$(selector).load(admin_url + url + "/" + id)
$("html, body").animate(
{
scrollTop: $(selector).offset().top + (is_mobile() ? 150 : 0),
},
600
);
}
// Init single invoice
function init_invoice(id) {
load_small_table_item(
id,
"#invoice",
"invoiceid",
"invoices/get_invoice_data_ajax",
".table-invoices"
);
}
// Init single credit note
function init_credit_note(id) {
load_small_table_item(
id,
"#credit_note",
"credit_note_id",
"credit_notes/get_credit_note_data_ajax",
".table-credit-notes"
);
}
// Init single estimate
function init_estimate(id) {
load_small_table_item(
id,
"#estimate",
"estimateid",
"estimates/get_estimate_data_ajax",
".table-estimates"
);
}
// Init single proposal
function init_proposal(id) {
load_small_table_item(
id,
"#proposal",
"proposal_id",
"proposals/get_proposal_data_ajax",
".table-proposals"
);
}
function init_expense(id) {
load_small_table_item(
id,
"#expense",
"expenseid",
"expenses/get_expense_data_ajax",
".table-expenses"
);
}
// Clear billing and shipping inputs for invoice,estimate etc...
function clear_billing_and_shipping_details() {
for (var f in billingAndShippingFields) {
if (billingAndShippingFields[f].indexOf("country") > -1) {
$('select[name="' + billingAndShippingFields[f] + '"]').selectpicker(
"val",
""
);
} else {
$('input[name="' + billingAndShippingFields[f] + '"]').val("");
$('textarea[name="' + billingAndShippingFields[f] + '"]').val("");
}
if (billingAndShippingFields[f] == "billing_country") {
$('input[name="include_shipping"]').prop("checked", false);
$('input[name="include_shipping"]').change();
}
}
init_billing_and_shipping_details();
}
// Init billing and shipping details for invoice, estimate etc...
function init_billing_and_shipping_details() {
var _f;
var include_shipping = $('input[name="include_shipping"]').prop("checked");
for (var f in billingAndShippingFields) {
_f = "";
if (billingAndShippingFields[f].indexOf("country") > -1) {
_f = $("#" + billingAndShippingFields[f] + " option:selected").data(
"subtext"
);
} else if (
billingAndShippingFields[f].indexOf("shipping_street") > -1 ||
billingAndShippingFields[f].indexOf("billing_street") > -1
) {
if ($('textarea[name="' + billingAndShippingFields[f] + '"]').length) {
_f = $('textarea[name="' + billingAndShippingFields[f] + '"]')
.val()
.replace(/(?:\r\n|\r|\n)/g, "
");
}
} else {
_f = $('input[name="' + billingAndShippingFields[f] + '"]').val();
}
if (billingAndShippingFields[f].indexOf("shipping") > -1) {
if (!include_shipping) {
_f = "";
}
}
if (typeof _f == "undefined") {
_f = "";
}
_f = _f !== "" ? _f : "--";
$("." + billingAndShippingFields[f]).html(_f);
}
$("#billing_and_shipping_details").modal("hide");
}
// Record payment function
function record_payment(id) {
if (typeof id == "undefined" || id === "") {
return;
}
$("#invoice").load(admin_url + "invoices/record_invoice_payment_ajax/" + id);
}
function schedule_invoice_send(id) {
$("#invoice").load(admin_url + "email_schedule_invoice/create/" + id);
}
function schedule_estimate_send(id) {
$("#estimate").load(admin_url + "email_schedule_estimate/create/" + id);
}
function edit_invoice_scheduled_email(schedule_id) {
$("#invoice").load(admin_url + "email_schedule_invoice/edit/" + schedule_id);
}
function edit_estimate_scheduled_email(schedule_id) {
$("#estimate").load(
admin_url + "email_schedule_estimate/edit/" + schedule_id
);
}
// Add item to preview
function add_item_to_preview(id) {
requestGetJSON("invoice_items/get_item_by_id/" + id).done(function (
response
) {
clear_item_preview_values();
$('.main textarea[name="description"]').val(response.description);
$('.main textarea[name="long_description"]').val(
response.long_description.replace(/(<|<)br\s*\/*(>|>)/g, " ")
);
_set_item_preview_custom_fields_array(response.custom_fields);
$('.main input[name="quantity"]').val(1);
var taxSelectedArray = [];
if (response.taxname && response.taxrate) {
taxSelectedArray.push(response.taxname + "|" + response.taxrate);
}
if (response.taxname_2 && response.taxrate_2) {
taxSelectedArray.push(response.taxname_2 + "|" + response.taxrate_2);
}
$(".main select.tax").selectpicker("val", taxSelectedArray);
$('.main input[name="unit"]').val(response.unit);
var $currency = $("body").find(
'.accounting-template select[name="currency"]'
);
var baseCurency = $currency.attr("data-base");
var selectedCurrency = $currency.find("option:selected").val();
var $rateInputPreview = $('.main input[name="rate"]');
if (baseCurency == selectedCurrency) {
$rateInputPreview.val(response.rate);
} else {
var itemCurrencyRate = response["rate_currency_" + selectedCurrency];
if (!itemCurrencyRate || parseFloat(itemCurrencyRate) === 0) {
$rateInputPreview.val(response.rate);
} else {
$rateInputPreview.val(itemCurrencyRate);
}
}
$(document).trigger({
type: "item-added-to-preview",
item: response,
item_type: "item",
});
});
}
function _set_item_preview_custom_fields_array(custom_fields) {
var cf_act_as_inputs = [
"input",
"number",
"date_picker",
"date_picker_time",
"colorpicker",
];
for (var i = 0; i < custom_fields.length; i++) {
var cf = custom_fields[i];
if ($.inArray(cf.type, cf_act_as_inputs) > -1) {
var f = $('tr.main td[data-id="' + cf.id + '"] input');
// trigger change eq. for colorpicker
f.val(cf.value).trigger("change");
} else if (cf.type == "textarea") {
$('tr.main td[data-id="' + cf.id + '"] textarea').val(cf.value);
} else if (cf.type == "select" || cf.type == "multiselect") {
if (!empty(cf.value)) {
var selected = cf.value.split(",");
selected = selected.map(function (e) {
return e.trim();
});
$('tr.main td[data-id="' + cf.id + '"] select').selectpicker(
"val",
selected
);
}
} else if (cf.type == "checkbox") {
if (!empty(cf.value)) {
var selected = cf.value.split(",");
selected = selected.map(function (e) {
return e.trim();
});
$.each(selected, function (i, e) {
$(
'tr.main td[data-id="' +
cf.id +
'"] input[type="checkbox"][value="' +
e +
'"]'
).prop("checked", true);
});
}
}
}
}
// Add task to preview
function add_task_to_preview_as_item(id) {
requestGetJSON("tasks/get_billable_task_data/" + id).done(function (
response
) {
response.taxname = $("select.main-tax").selectpicker("val");
var previewArea = $(".main");
previewArea.find('textarea[name="description"]').val(response.name);
previewArea
.find('textarea[name="long_description"]')
.val(response.description);
previewArea.find('input[name="quantity"]').val(response.total_hours);
previewArea.find('input[name="rate"]').val(response.hourly_rate);
previewArea.find('input[name="unit"]').val("");
$('input[name="task_id"]').val(id);
$(document).trigger({
type: "item-added-to-preview",
item: response,
item_type: "task",
});
});
}
// Clear the items added to preview
function clear_item_preview_values(default_taxes) {
// Get the last taxes applied to be available for the next item
var last_taxes_applied = $("table.items tbody")
.find("tr:last-child")
.find("select")
.selectpicker("val");
var previewArea = $(".main");
previewArea.find("textarea").val(""); // includes cf
previewArea
.find('td.custom_field input[type="checkbox"]')
.prop("checked", false); // cf
previewArea.find("td.custom_field input:not(:checkbox):not(:hidden)").val(""); // cf // not hidden for chkbox hidden helpers
previewArea.find("td.custom_field select").selectpicker("val", ""); // cf
previewArea.find('input[name="quantity"]').val(1);
previewArea.find("select.tax").selectpicker("val", last_taxes_applied);
previewArea.find('input[name="rate"]').val("");
previewArea.find('input[name="unit"]').val("");
$('input[name="task_id"]').val("");
$('input[name="expense_id"]').val("");
}
// Append the added items to the preview to the table as items
function add_item_to_table(data, itemid, merge_invoice, bill_expense) {
// If not custom data passed get from the preview
data =
typeof data == "undefined" || data == "undefined"
? get_item_preview_values()
: data;
if (
data.description === "" &&
data.long_description === "" &&
data.rate === ""
) {
return;
}
var table_row = "";
var item_key = lastAddedItemKey
? (lastAddedItemKey += 1)
: $("body").find("tbody .item").length + 1;
lastAddedItemKey = item_key;
table_row +=
'
';
table_row += '';
// Check if quantity is number
if (isNaN(data.qty)) {
data.qty = 1;
}
// Check if rate is number
if (data.rate === "" || isNaN(data.rate)) {
data.rate = 0;
}
var amount = data.rate * data.qty;
var tax_name = "newitems[" + item_key + "][taxname][]";
$("body").append('');
var regex = / ]*>/gi;
get_taxes_dropdown_template(tax_name, data.taxname).done(function (
tax_dropdown
) {
// order input
table_row +=
'';
table_row += " | ";
table_row +=
' | ";
table_row +=
' | ";
var custom_fields = $("tr.main td.custom_field");
var cf_has_required = false;
if (custom_fields.length > 0) {
$.each(custom_fields, function () {
var cf = $(this).clone();
var cf_html = "";
var cf_field = $(this).find("[data-fieldid]");
var cf_name =
"newitems[" +
item_key +
"][custom_fields][items][" +
cf_field.attr("data-fieldid") +
"]";
if (cf_field.is(":checkbox")) {
var checked = $(this).find('input[type="checkbox"]:checked');
var checkboxes = cf.find('input[type="checkbox"]');
$.each(checkboxes, function (i, e) {
var random_key = Math.random().toString(20).slice(2);
$(this)
.attr("id", random_key)
.attr("name", cf_name)
.next("label")
.attr("for", random_key);
if ($(this).attr("data-custom-field-required") == "1") {
cf_has_required = true;
}
});
$.each(checked, function (i, e) {
cf.find('input[value="' + $(e).val() + '"]').attr("checked", true);
});
cf_html = cf.html();
} else if (cf_field.is("input") || cf_field.is("textarea")) {
if (cf_field.is("input")) {
cf.find("[data-fieldid]").attr("value", cf_field.val());
} else {
cf.find("[data-fieldid]").html(cf_field.val());
}
cf.find("[data-fieldid]").attr("name", cf_name);
if (
cf.find("[data-fieldid]").attr("data-custom-field-required") == "1"
) {
cf_has_required = true;
}
cf_html = cf.html();
} else if (cf_field.is("select")) {
if ($(this).attr("data-custom-field-required") == "1") {
cf_has_required = true;
}
var selected = $(this)
.find("select[data-fieldid]")
.selectpicker("val");
selected = typeof (selected != "array")
? new Array(selected)
: selected;
// Check if is multidimensional by multi-select customfield
selected = selected[0].constructor === Array ? selected[0] : selected;
var selectNow = cf.find("select");
var $wrapper = $("");
selectNow.attr("name", cf_name);
var $select = selectNow.clone();
$wrapper.append($select);
$.each(selected, function (i, e) {
$wrapper
.find('select option[value="' + e + '"]')
.attr("selected", true);
});
cf_html = $wrapper.html();
}
table_row += '' + cf_html + " | ";
});
}
table_row +=
'';
if (!data.unit || typeof data.unit == "undefined") {
data.unit = "";
}
table_row +=
'';
table_row += " | ";
table_row +=
' | ';
table_row += '' + tax_dropdown + " | ";
table_row +=
'' +
format_money(amount, true) +
" | ";
table_row +=
' | ';
table_row += "
";
$("table.items tbody").append(table_row);
$(document).trigger({
type: "item-added-to-table",
data: data,
row: table_row,
});
setTimeout(function () {
calculate_total();
}, 15);
var billed_task = $('input[name="task_id"]').val();
var billed_expense = $('input[name="expense_id"]').val();
if (billed_task !== "" && typeof billed_task != "undefined") {
billed_tasks = billed_task.split(",");
$.each(billed_tasks, function (i, obj) {
$("#billed-tasks").append(
hidden_input("billed_tasks[" + item_key + "][]", obj)
);
});
}
if (billed_expense !== "" && typeof billed_expense != "undefined") {
billed_expenses = billed_expense.split(",");
$.each(billed_expenses, function (i, obj) {
$("#billed-expenses").append(
hidden_input("billed_expenses[" + item_key + "][]", obj)
);
});
}
if (
$("#item_select").hasClass("ajax-search") &&
$("#item_select").selectpicker("val") !== ""
) {
$("#item_select").prepend("
");
}
init_selectpicker();
init_datepicker();
init_color_pickers();
clear_item_preview_values();
reorder_items();
$("body").find("#items-warning").remove();
$("body").find(".dt-loader").remove();
$("#item_select").selectpicker("val", "");
if (cf_has_required && $(".invoice-form").length) {
validate_invoice_form();
} else if (cf_has_required && $(".estimate-form").length) {
validate_estimate_form();
} else if (cf_has_required && $(".proposal-form").length) {
validate_proposal_form();
} else if (cf_has_required && $(".credit-note-form").length) {
validate_credit_note_form();
}
if (bill_expense == "undefined" || !bill_expense) {
$('select[name="task_select"]')
.find('[value="' + billed_task + '"]')
.remove();
$('select[name="task_select"]').selectpicker("refresh");
}
return true;
});
return false;
}
// Get taxes dropdown selectpicker template
function get_taxes_dropdown_template(name, taxname) {
return $.post(admin_url + "misc/get_taxes_dropdown_template", {
name: name,
taxname: taxname,
});
}
// Custom function for deselecting selected value from ajax dropdown
function deselect_ajax_search(e) {
var $elm = $("select#" + $(e).attr("data-id"));
$elm.data("AjaxBootstrapSelect").list.cache = {};
var $elmWrapper = $elm.parents(".bootstrap-select");
$elm
.html("")
.append('
')
.selectpicker("val", $elm.attr("multiple") == "multiple" ? [] : "");
$elmWrapper
.removeClass("ajax-remove-values-option")
.find(".ajax-clear-values")
.remove();
setTimeout(function () {
$elm.trigger("selected.cleared.ajax.bootstrap.select", e);
$elm.trigger("change").data("AjaxBootstrapSelect").list.cache = {};
}, 50);
}
// Ajax project search but only for specific customer
function init_ajax_project_search_by_customer_id(selector) {
selector =
typeof selector == "undefined" ? "#project_id.ajax-search" : selector;
init_ajax_search("project", selector, {
customer_id: function () {
return $("#clientid").val();
},
});
}
// Ajax project search select
function init_ajax_projects_search(selector) {
selector =
typeof selector == "undefined" ? "#project_id.ajax-search" : selector;
init_ajax_search("project", selector);
}
// Make items sortable with jquery sort plugin
function init_items_sortable(preview_table) {
var _items_sortable = $("#wrapper").find(".items tbody");
if (_items_sortable.length === 0) {
return;
}
_items_sortable.sortable({
helper: fixHelperTableHelperSortable,
handle: ".dragger",
placeholder: "ui-placeholder",
itemPath: "> tbody",
itemSelector: "tr.sortable",
items: "tr.sortable",
update: function () {
if (typeof preview_table == "undefined") {
reorder_items();
} else {
// If passed from the admin preview there is other function for re-ordering
save_ei_items_order();
}
},
sort: function (event, ui) {
// Firefox fixer when dragging
var $target = $(event.target);
if (!/html|body/i.test($target.offsetParent()[0].tagName)) {
var top =
event.pageY -
$target.offsetParent().offset().top -
ui.helper.outerHeight(true) / 2;
ui.helper.css({
top: top + "px",
});
}
},
});
}
// Save the items from order from the admin preview
function save_ei_items_order() {
var table = $(".table.items-preview");
var rows = table.find("tbody tr");
var i = 1,
type = table.attr("data-type"),
order = [],
_order_id,
item_id;
if (!type) {
return false;
}
$.each(rows, function () {
order.push([$(this).data("item-id"), i]);
// update item number when reordering
$(this).find("td.item_no").html(i);
i++;
});
setTimeout(function () {
$.post(admin_url + "misc/update_ei_items_order/" + type, {
data: order,
});
}, 200);
}
// Reoder the items in table edit for estimate and invoices
function reorder_items() {
var rows = $(".table.has-calculations tbody tr.item");
var i = 1;
$.each(rows, function () {
$(this).find("input.order").val(i);
i++;
});
}
// Get the preview main values
function get_item_preview_values() {
var response = {};
response.description = $('.main textarea[name="description"]').val();
response.long_description = $(
'.main textarea[name="long_description"]'
).val();
response.qty = $('.main input[name="quantity"]').val();
response.taxname = $(".main select.tax").selectpicker("val");
response.rate = $('.main input[name="rate"]').val();
response.unit = $('.main input[name="unit"]').val();
return response;
}
// Calculate invoice total - NOT RECOMENDING EDIT THIS FUNCTION BECUASE IS VERY SENSITIVE
function calculate_total() {
if ($("body").hasClass("no-calculate-total")) {
return false;
}
var calculated_tax,
taxrate,
item_taxes,
row,
_amount,
_tax_name,
taxes = {},
taxes_rows = [],
subtotal = 0,
total = 0,
quantity = 1,
total_discount_calculated = 0,
rows = $(".table.has-calculations tbody tr.item"),
discount_area = $("#discount_area"),
adjustment = $('input[name="adjustment"]').val(),
discount_percent = $('input[name="discount_percent"]').val(),
discount_fixed = $('input[name="discount_total"]').val(),
discount_total_type = $(".discount-total-type.selected"),
discount_type = $('select[name="discount_type"]').val();
$(".tax-area").remove();
$.each(rows, function () {
quantity = $(this).find("[data-quantity]").val();
if (quantity === "") {
quantity = 1;
$(this).find("[data-quantity]").val(1);
}
_amount = accounting.toFixed(
$(this).find("td.rate input").val() * quantity,
app.options.decimal_places
);
_amount = parseFloat(_amount);
$(this).find("td.amount").html(format_money(_amount, true));
subtotal += _amount;
row = $(this);
item_taxes = $(this).find("select.tax").selectpicker("val");
if (item_taxes) {
$.each(item_taxes, function (i, taxname) {
taxrate = row
.find('select.tax [value="' + taxname + '"]')
.data("taxrate");
calculated_tax = (_amount / 100) * taxrate;
if (!taxes.hasOwnProperty(taxname)) {
if (taxrate != 0) {
_tax_name = taxname.split("|");
tax_row =
'
| ' +
_tax_name[0] +
"(" +
taxrate +
'%) | |
';
$(discount_area).after(tax_row);
taxes[taxname] = calculated_tax;
}
} else {
// Increment total from this tax
taxes[taxname] = taxes[taxname] += calculated_tax;
}
});
}
});
// Discount by percent
if (
discount_percent !== "" &&
discount_percent != 0 &&
discount_type == "before_tax" &&
discount_total_type.hasClass("discount-type-percent")
) {
total_discount_calculated = (subtotal * discount_percent) / 100;
} else if (
discount_fixed !== "" &&
discount_fixed != 0 &&
discount_type == "before_tax" &&
discount_total_type.hasClass("discount-type-fixed")
) {
total_discount_calculated = discount_fixed;
}
$.each(taxes, function (taxname, total_tax) {
if (
discount_percent !== "" &&
discount_percent != 0 &&
discount_type == "before_tax" &&
discount_total_type.hasClass("discount-type-percent")
) {
total_tax_calculated = (total_tax * discount_percent) / 100;
total_tax = total_tax - total_tax_calculated;
} else if (
discount_fixed !== "" &&
discount_fixed != 0 &&
discount_type == "before_tax" &&
discount_total_type.hasClass("discount-type-fixed")
) {
var t = (discount_fixed / subtotal) * 100;
total_tax = total_tax - (total_tax * t) / 100;
}
total += total_tax;
total_tax = format_money(total_tax);
$("#tax_id_" + slugify(taxname)).html(total_tax);
});
total = total + subtotal;
// Discount by percent
if (
discount_percent !== "" &&
discount_percent != 0 &&
discount_type == "after_tax" &&
discount_total_type.hasClass("discount-type-percent")
) {
total_discount_calculated = (total * discount_percent) / 100;
} else if (
discount_fixed !== "" &&
discount_fixed != 0 &&
discount_type == "after_tax" &&
discount_total_type.hasClass("discount-type-fixed")
) {
total_discount_calculated = discount_fixed;
}
total = total - total_discount_calculated;
adjustment = parseFloat(adjustment);
// Check if adjustment not empty
if (!isNaN(adjustment)) {
total = total + adjustment;
}
var discount_html = "-" + format_money(total_discount_calculated);
$('input[name="discount_total"]').val(
accounting.toFixed(total_discount_calculated, app.options.decimal_places)
);
// Append, format to html and display
$(".discount-total").html(discount_html);
$(".adjustment").html(format_money(adjustment));
$(".subtotal").html(
format_money(subtotal) +
hidden_input(
"subtotal",
accounting.toFixed(subtotal, app.options.decimal_places)
)
);
$(".total").html(
format_money(total) +
hidden_input(
"total",
accounting.toFixed(total, app.options.decimal_places)
)
);
$(document).trigger("sales-total-calculated");
}
function exclude_tax_from_amount(tax_percent, total_amount) {
totalTax = accounting.toFixed(
(total_amount * tax_percent) / (100 + tax_percent),
app.options.decimal_places
);
return accounting.toFixed(
total_amount - totalTax,
app.options.decimal_places
);
}
// Deletes invoice items
function delete_item(row, itemid) {
$(row)
.parents("tr")
.addClass("animated fadeOut", function () {
setTimeout(function () {
$(row).parents("tr").remove();
calculate_total();
}, 50);
});
// If is edit we need to add to input removed_items to track activity
if (itemid && $('input[name="isedit"]').length > 0) {
$("#removed-items").append(hidden_input("removed_items[]", itemid));
}
}
// Format money function
function format_money(total, excludeSymbol) {
if (typeof excludeSymbol != "undefined" && excludeSymbol) {
return accounting.formatMoney(total, {
symbol: "",
});
}
return accounting.formatMoney(total);
}
// Set the currency for accounting
function init_currency(id, callback) {
var $accountingTemplate = $("body").find(".accounting-template");
if ($accountingTemplate.length || id) {
var selectedCurrencyId = !id
? $accountingTemplate.find('select[name="currency"]').val()
: id;
requestGetJSON("misc/get_currency/" + selectedCurrencyId).done(function (
currency
) {
// Used for formatting money
accounting.settings.currency.decimal = currency.decimal_separator;
accounting.settings.currency.thousand = currency.thousand_separator;
accounting.settings.currency.symbol = currency.symbol;
accounting.settings.currency.format =
currency.placement == "after" ? "%v %s" : "%s%v";
calculate_total();
if (callback) {
callback();
}
});
}
}
// Delete invoice attachment
function delete_invoice_attachment(id) {
if (confirm_delete()) {
requestGet("invoices/delete_attachment/" + id)
.done(function (success) {
if (success == 1) {
$("body")
.find('[data-attachment-id="' + id + '"]')
.remove();
init_invoice(
$("body").find('input[name="_attachment_sale_id"]').val()
);
}
})
.fail(function (error) {
alert_float("danger", error.responseText);
});
}
}
// Delete credit note attachment
function delete_credit_note_attachment(id) {
if (confirm_delete()) {
requestGet("credit_notes/delete_attachment/" + id)
.done(function (success) {
if (success == 1) {
$("body")
.find('[data-attachment-id="' + id + '"]')
.remove();
init_credit_note(
$("body").find('input[name="_attachment_sale_id"]').val()
);
}
})
.fail(function (error) {
alert_float("danger", error.responseText);
});
}
}
// Delete estimate attachment
function delete_estimate_attachment(id) {
if (confirm_delete()) {
requestGet("estimates/delete_attachment/" + id)
.done(function (success) {
if (success == 1) {
$("body")
.find('[data-attachment-id="' + id + '"]')
.remove();
var rel_id = $("body")
.find('input[name="_attachment_sale_id"]')
.val();
$("body").hasClass("estimates-pipeline")
? estimate_pipeline_open(rel_id)
: init_estimate(rel_id);
}
})
.fail(function (error) {
alert_float("danger", error.responseText);
});
}
}
// Delete proposal attachment
function delete_proposal_attachment(id) {
if (confirm_delete()) {
requestGet("proposals/delete_attachment/" + id)
.done(function (success) {
if (success == 1) {
var rel_id = $("body")
.find('input[name="_attachment_sale_id"]')
.val();
$("body")
.find('[data-attachment-id="' + id + '"]')
.remove();
$("body").hasClass("proposals-pipeline")
? proposal_pipeline_open(rel_id)
: init_proposal(rel_id);
}
})
.fail(function (error) {
alert_float("danger", error.responseText);
});
}
}
// Invoices quick total stats
function init_invoices_total(manual) {
if ($("#invoices_total").length === 0) {
return;
}
var _inv_total_inline = $(".invoices-total-inline");
var _inv_total_href_manual = $(".invoices-total");
if (
$("body").hasClass("invoices-total-manual") &&
typeof manual == "undefined" &&
!_inv_total_href_manual.hasClass("initialized")
) {
return;
}
if (
_inv_total_inline.length > 0 &&
_inv_total_href_manual.hasClass("initialized")
) {
// On the next request won't be inline in case of currency change
// Used on dashboard
_inv_total_inline.removeClass("invoices-total-inline");
return;
}
_inv_total_href_manual.addClass("initialized");
var _years = $("body")
.find('select[name="invoices_total_years"]')
.selectpicker("val");
var years = [];
$.each(_years, function (i, _y) {
if (_y !== "") {
years.push(_y);
}
});
var currency = $("body").find('select[name="total_currency"]').val();
var data = {
currency: currency,
years: years,
init_total: true,
};
var project_id = $('input[name="project_id"]').val();
var customer_id = $('.customer_profile input[name="userid"]').val();
if (typeof project_id != "undefined") {
data.project_id = project_id;
} else if (typeof customer_id != "undefined") {
data.customer_id = customer_id;
}
$.post(admin_url + "invoices/get_invoices_total", data).done(function (
response
) {
$("#invoices_total").html(response);
});
}
// Estimates quick total stats
function init_estimates_total(manual) {
if ($("#estimates_total").length === 0) {
return;
}
var _est_total_href_manual = $(".estimates-total");
if (
$("body").hasClass("estimates-total-manual") &&
typeof manual == "undefined" &&
!_est_total_href_manual.hasClass("initialized")
) {
return;
}
_est_total_href_manual.addClass("initialized");
var currency = $("body").find('select[name="total_currency"]').val();
var _years = $("body")
.find('select[name="estimates_total_years"]')
.selectpicker("val");
var years = [];
$.each(_years, function (i, _y) {
if (_y !== "") {
years.push(_y);
}
});
var customer_id = "";
var project_id = "";
var _customer_id = $('.customer_profile input[name="userid"]').val();
var _project_id = $('input[name="project_id"]').val();
if (typeof _customer_id != "undefined") {
customer_id = _customer_id;
} else if (typeof _project_id != "undefined") {
project_id = _project_id;
}
$.post(admin_url + "estimates/get_estimates_total", {
currency: currency,
init_total: true,
years: years,
customer_id: customer_id,
project_id: project_id,
}).done(function (response) {
$("#estimates_total").html(response);
});
}
// Expenses quick total stats
function init_expenses_total() {
if ($("#expenses_total").length === 0) {
return;
}
var currency = $("body").find('select[name="expenses_total_currency"]').val();
var _years = $("body")
.find('select[name="expenses_total_years"]')
.selectpicker("val");
var years = [];
$.each(_years, function (i, _y) {
if (_y !== "") {
years.push(_y);
}
});
var customer_id = "";
var _customer_id = $('.customer_profile input[name="userid"]').val();
if (typeof customer_id != "undefined") {
customer_id = _customer_id;
}
var project_id = "";
var _project_id = $('input[name="project_id"]').val();
if (typeof project_id != "undefined") {
project_id = _project_id;
}
$.post(admin_url + "expenses/get_expenses_total", {
currency: currency,
init_total: true,
years: years,
customer_id: customer_id,
project_id: project_id,
}).done(function (response) {
$("#expenses_total").html(response);
});
}
// Validate invoice add/edit form
function validate_invoice_form(selector) {
selector = typeof selector == "undefined" ? "#invoice-form" : selector;
appValidateForm($(selector), {
clientid: {
required: {
depends: function () {
var customerRemoved =
$("select#clientid").hasClass("customer-removed");
return !customerRemoved;
},
},
},
date: "required",
currency: "required",
repeat_every_custom: {
min: 1,
},
number: {
required: true,
},
});
$("body")
.find('input[name="number"]')
.rules("add", {
remote: {
url: admin_url + "invoices/validate_invoice_number",
type: "post",
data: {
number: function () {
return $('input[name="number"]').val();
},
isedit: function () {
return $('input[name="number"]').data("isedit");
},
original_number: function () {
return $('input[name="number"]').data("original-number");
},
date: function () {
return $('input[name="date"]').val();
},
},
},
messages: {
remote: app.lang.invoice_number_exists,
},
});
}
function validate_credit_note_form(selector) {
selector = typeof selector == "undefined" ? "#credit-note-form" : selector;
appValidateForm($(selector), {
clientid: {
required: {
depends: function () {
var customerRemoved =
$("select#clientid").hasClass("customer-removed");
return !customerRemoved;
},
},
},
date: "required",
currency: "required",
number: {
required: true,
},
});
$("body")
.find('input[name="number"]')
.rules("add", {
remote: {
url: admin_url + "credit_notes/validate_number",
type: "post",
data: {
number: function () {
return $('input[name="number"]').val();
},
isedit: function () {
return $('input[name="number"]').data("isedit");
},
original_number: function () {
return $('input[name="number"]').data("original-number");
},
date: function () {
return $(".credit_note input[name='date']").val();
},
},
},
messages: {
remote: app.lang.credit_note_number_exists,
},
});
}
// Validates estimate add/edit form
function validate_estimate_form(selector) {
selector = typeof selector == "undefined" ? "#estimate-form" : selector;
appValidateForm($(selector), {
clientid: {
required: {
depends: function () {
var customerRemoved =
$("select#clientid").hasClass("customer-removed");
return !customerRemoved;
},
},
},
date: "required",
currency: "required",
number: {
required: true,
},
});
$("body")
.find('input[name="number"]')
.rules("add", {
remote: {
url: admin_url + "estimates/validate_estimate_number",
type: "post",
data: {
number: function () {
return $('input[name="number"]').val();
},
isedit: function () {
return $('input[name="number"]').data("isedit");
},
original_number: function () {
return $('input[name="number"]').data("original-number");
},
date: function () {
return $("body").find('.estimate input[name="date"]').val();
},
},
},
messages: {
remote: app.lang.estimate_number_exists,
},
});
}
// Sort estimates in the pipeline view / switching sort type by click
function estimates_pipeline_sort(type) {
kan_ban_sort(type, estimate_pipeline);
}
// Sort proposals in the pipeline view / switching sort type by click
function proposal_pipeline_sort(type) {
kan_ban_sort(type, proposals_pipeline);
}
// Init estimates pipeline
function estimate_pipeline() {
init_kanban(
"estimates/get_pipeline",
estimates_pipeline_update,
".pipeline-status",
290,
360
);
}
// Used when estimate is updated from pipeline. eq changed order or moved to another status
function estimates_pipeline_update(ui, object) {
if (object === ui.item.parent()[0]) {
var data = {
estimateid: $(ui.item).attr("data-estimate-id"),
status: $(ui.item.parent()[0]).attr("data-status-id"),
order: [],
};
$.each(
$(ui.item).parents(".pipeline-status").find("li"),
function (idx, el) {
var id = $(el).attr("data-estimate-id");
if (id) {
data.order.push([id, idx + 1]);
}
}
);
check_kanban_empty_col("[data-estimate-id]");
setTimeout(function () {
$.post(admin_url + "estimates/update_pipeline", data).done(function (
response
) {
update_kan_ban_total_when_moving(ui, data.status);
estimate_pipeline();
});
}, 200);
}
}
// Used when proposal is updated from pipeline. eq changed order or moved to another status
function proposals_pipeline_update(ui, object) {
if (object === ui.item.parent()[0]) {
var data = {
order: [],
status: $(ui.item.parent()[0]).attr("data-status-id"),
proposalid: $(ui.item).attr("data-proposal-id"),
};
$.each(
$(ui.item).parents(".pipeline-status").find("li"),
function (idx, el) {
var id = $(el).attr("data-proposal-id");
if (id) {
data.order.push([id, idx + 1]);
}
}
);
check_kanban_empty_col("[data-proposal-id]");
setTimeout(function () {
$.post(admin_url + "proposals/update_pipeline", data).done(function (
response
) {
update_kan_ban_total_when_moving(ui, data.status);
proposals_pipeline();
});
}, 200);
}
}
// Init proposals pipeline
function proposals_pipeline() {
init_kanban(
"proposals/get_pipeline",
proposals_pipeline_update,
".pipeline-status",
290,
360
);
}
// Open single proposal in pipeline
function proposal_pipeline_open(id) {
if (id === "") {
return;
}
requestGet("proposals/pipeline_open/" + id).done(function (response) {
var visible = $(".proposal-pipeline-modal:visible").length > 0;
$("#proposal").html(response);
if (!visible) {
$(".proposal-pipeline-modal").modal({
show: true,
backdrop: "static",
keyboard: false,
});
} else {
$("#proposal")
.find(".modal.proposal-pipeline-modal")
.removeClass("fade")
.addClass("in")
.css("display", "block");
}
});
}
// Estimate single open in pipeline
function estimate_pipeline_open(id) {
if (id === "") {
return;
}
requestGet("estimates/pipeline_open/" + id).done(function (response) {
var visible = $(".estimate-pipeline:visible").length > 0;
$("#estimate").html(response);
if (!visible) {
$(".estimate-pipeline").modal({
show: true,
backdrop: "static",
keyboard: false,
});
} else {
$("#estimate")
.find(".modal.estimate-pipeline")
.removeClass("fade")
.addClass("in")
.css("display", "block");
}
});
}
// Delete estimate note
function delete_sales_note(wrapper, id) {
if (confirm_delete()) {
requestGetJSON("misc/delete_note/" + id).done(function (response) {
if (response.success === true || response.success == "true") {
$(wrapper).parents(".sales-note-wrapper").remove();
var salesNotesWrapper = $("#sales-notes-wrapper");
var totalNotesNow = salesNotesWrapper.attr("data-total") - 1;
var notesTotal = $(".notes-total");
salesNotesWrapper.attr("data-total", totalNotesNow);
if (totalNotesNow <= 0) {
notesTotal.addClass("hide");
} else {
notesTotal.html('
' + totalNotesNow + "");
}
}
});
}
}
// Get all estimate notes
function get_sales_notes(id, controller) {
requestGet(controller + "/get_notes/" + id).done(function (response) {
$("#sales_notes_area").html(response);
var totalNotesNow = $("#sales-notes-wrapper").attr("data-total");
if (totalNotesNow > 0) {
$(".notes-total")
.html('
' + totalNotesNow + "")
.removeClass("hide");
}
});
}
// Proposal merge field into the editor
function insert_proposal_merge_field(field) {
tinymce.activeEditor.execCommand("mceInsertContent", false, $(field).text());
}
// Toggle full view for small tables like proposals
function small_table_full_view() {
$("#small-table").toggleClass("hide");
$(".small-table-right-col").toggleClass("col-md-12 col-md-7");
$(window).trigger("resize");
}
// Used to update manually the prefix after invoice/estimate is created.
function save_sales_number_settings(e) {
var data = {};
data.prefix = $("body").find('input[name="s_prefix"]').val();
$.post($(e).data("url"), data).done(function (response) {
response = JSON.parse(response);
if (response.success && response.message) {
alert_float("success", response.message);
$("#prefix").html(data.prefix);
}
});
}
// Prefix for invoices/estimates in case there is year.
function do_prefix_year(date) {
var date_array = _split_formatted_date_by_separator(date);
if (typeof date_array != "undefined") {
$.each(date_array, function (i, string) {
if (string.length == 4) {
var $pYear = $("#prefix_year");
if ($pYear.hasClass("format-n-yy")) {
string = string.substr(-2);
} else if ($pYear.hasClass("format-mm-yyyy")) {
var month_index;
if (
app.options.date_format == "d-m-Y" ||
app.options.date_format == "d/m/Y" ||
app.options.date_format == "Y-m-d" ||
app.options.date_format == "d.m.Y"
) {
month_index = 1;
} else if (
app.options.date_format == "m-d-Y" ||
app.options.date_format == "m.d.Y" ||
app.options.date_format == "m/d/Y"
) {
month_index = 0;
}
$("#prefix_month").html(date_array[month_index]);
}
$pYear.html(string);
}
});
}
}
function unformat_date(date) {
var date_array = _split_formatted_date_by_separator(date),
// Y-m-d is default, see below commented code
month_index = 1,
year_index = 0,
day_index = 2;
if (
app.options.date_format == "d-m-Y" ||
app.options.date_format == "d/m/Y" ||
app.options.date_format == "d.m.Y"
) {
day_index = 0;
month_index = 1;
year_index = 2;
} else if (
/* else if (app.options.date_format == 'Y-m-d') {
day_index = 2;
month_index = 1;
year_index = 0;
}*/
app.options.date_format == "m-d-Y" ||
app.options.date_format == "m.d.Y" ||
app.options.date_format == "m/d/Y"
) {
day_index = 1;
month_index = 0;
year_index = 2;
}
return (
date_array[year_index] +
"-" +
date_array[month_index] +
"-" +
date_array[day_index]
);
}
function _split_formatted_date_by_separator(date) {
var date_array;
if (date.indexOf(".") > -1) {
date_array = date.split(".");
} else if (date.indexOf("-") > -1) {
date_array = date.split("-");
} else if (date.indexOf("/") > -1) {
date_array = date.split("/");
}
return date_array;
}
function init_tabs_scrollable() {
// Not working fine on RTL
if (isRTL != "true") {
if ($(window).width() <= 768) {
$("body").find(".toggle_view").remove();
}
// Horinzontal tabs
$(".horizontal-scrollable-tabs").horizontalTabs();
} else {
$(".arrow-left, .arrow-right").css("display", "none");
$(".horizontal-scrollable-tabs").removeClass("horizontal-scrollable-tabs");
$(".nav-tabs-horizontal").removeClass("nav-tabs-horizontal");
}
}
function view_contact_consent(id) {
requestGet("clients/consents/" + id).done(function (response) {
$("#consent_data").html(response);
initDataTableInline($("#consentHistoryTable"));
$("#consentModal").modal("show");
});
}
function view_lead_consent(id) {
window.location.hash = "gdpr";
init_lead(id);
}
// Set single notification as read INLINE
function set_notification_read_inline(id) {
requestGet("misc/set_notification_read_inline/" + id).done(function () {
var notification = $("body").find(
'.notification-wrapper[data-notification-id="' + id + '"]'
);
notification
.find(".notification-handler")
.removeClass("unread-notification");
notification.find(".not-mark-as-read-inline").tooltip("destroy").remove();
});
}
// Marks all notifications as read INLINE
function mark_all_notifications_as_read_inline() {
requestGet("misc/mark_all_notifications_as_read_inline/").done(function () {
var notification = $("body").find(".notification-wrapper");
notification
.find(".notification-handler")
.removeClass("unread-notification");
notification.find(".not-mark-as-read-inline").tooltip("destroy").remove();
});
}
// Deletes activity for sales eq. invoices, estimates.
function delete_sale_activity(id) {
if (confirm_delete()) {
requestGet("misc/delete_sale_activity/" + id).done(function () {
$("body")
.find('[data-sale-activity-id="' + id + '"]')
.remove();
});
}
}
// View calendar custom single event
function view_event(id) {
if (typeof id == "undefined") {
return;
}
$.post(admin_url + "utilities/view_event/" + id).done(function (response) {
$("#event").html(response);
$("#viewEvent").modal("show");
init_datepicker();
init_selectpicker();
validate_calendar_form();
});
}
// Delete calendar event form
function delete_event(id) {
if (confirm_delete()) {
requestGetJSON("utilities/delete_event/" + id).done(function (response) {
if (response.success === true || response.success == "true") {
window.location.reload();
}
});
}
}
// Validate calendar event form
function validate_calendar_form() {
appValidateForm(
$("body").find("._event form"),
{
title: "required",
start: "required",
reminder_before: "required",
},
calendar_form_handler
);
appValidateForm(
$("body").find("#viewEvent form"),
{
title: "required",
start: "required",
reminder_before: "required",
},
calendar_form_handler
);
}
// Handles calendar event saving
function calendar_form_handler(form) {
$.post(form.action, $(form).serialize()).done(function (response) {
response = JSON.parse(response);
if (response.success === true || response.success == "true") {
alert_float("success", response.message);
setTimeout(function () {
var location = window.location.href;
location = location.split("?");
window.location.href = location[0];
}, 500);
}
});
return false;
}
// Fetches notifications
function fetch_notifications(callback) {
requestGetJSON("misc/notifications_check").done(function (response) {
var nw = notifications_wrapper;
nw.html(response.html);
var total = nw.find("ul.notifications").attr("data-total-unread");
document.title =
total > 0 ? "(" + total + ") " + doc_initial_title : doc_initial_title;
var nIds = response.notificationsIds;
if (app.browser == "firefox" && nIds.length > 1) {
var lastNotification = nIds[0];
nIds = [];
nIds.push(lastNotification);
}
setTimeout(function () {
if (nIds.length > 0) {
$.each(nIds, function (i, notId) {
var nSelector = 'li[data-notification-id="' + notId + '"]';
var $not = nw.find(nSelector);
$.notify("", {
title: app.lang.new_notification,
body: $not.find(".notification-title").text(),
requireInteraction: true,
icon: $not.find(".notification-image").attr("src"),
tag: notId,
closeTime:
app.options.dismiss_desktop_not_after != "0"
? app.options.dismiss_desktop_not_after * 1000
: null,
})
.close(function () {
requestGet("misc/set_desktop_notification_read/" + notId).done(
function (response) {
var $totalIndicator = nw.find(".icon-total-indicator");
nw.find(
'li[data-notification-id="' + notId + '"] .notification-box'
).removeClass("unread");
var currentTotalNotifications = $totalIndicator.text();
currentTotalNotifications = currentTotalNotifications.trim();
currentTotalNotifications = currentTotalNotifications - 1;
if (currentTotalNotifications > 0) {
document.title =
"(" +
currentTotalNotifications +
") " +
doc_initial_title;
$totalIndicator.html(currentTotalNotifications);
} else {
document.title = doc_initial_title;
$totalIndicator.addClass("hide");
}
}
);
})
.click(function (e) {
parent.focus();
window.focus();
setTimeout(function () {
nw.find(nSelector + " .notification-link")
.addClass("desktopClick")
.click();
e.target.close();
}, 70);
});
});
}
}, 10);
});
}
function init_new_task_comment(manual) {
if (tinymce.get('task_comment')) {
tinymce.remove("#task_comment");
}
if (typeof taskCommentAttachmentDropzone != "undefined") {
taskCommentAttachmentDropzone.destroy();
}
$("#dropzoneTaskComment").removeClass("hide");
$("#addTaskCommentBtn").removeClass("hide");
taskCommentAttachmentDropzone = new Dropzone(
"#task-comment-form",
appCreateDropzoneOptions({
uploadMultiple: true,
clickable: "#dropzoneTaskComment",
previewsContainer: ".dropzone-task-comment-previews",
autoProcessQueue: false,
addRemoveLinks: true,
parallelUploads: 20,
maxFiles: 20,
paramName: "file",
sending: function (file, xhr, formData) {
formData.append(
"taskid",
$("#addTaskCommentBtn").attr("data-comment-task-id")
);
if (tinyMCE.activeEditor) {
formData.append("content", tinyMCE.activeEditor.getContent());
} else {
formData.append("content", $("#task_comment").val());
}
},
success: function (files, response) {
response = JSON.parse(response);
if (
this.getUploadingFiles().length === 0 &&
this.getQueuedFiles().length === 0
) {
_task_append_html(response.taskHtml);
tinymce.remove("#task_comment");
}
},
})
);
var editorConfig = _simple_editor_config();
editorConfig.toolbar_sticky = true
if (typeof manual == "undefined" || manual === false) {
editorConfig.auto_focus = true;
}
var taskid = $("#task-modal #taskid").val();
editorConfig.content_style =
"span.mention {\
background-color: #eeeeee;\
padding: 3px;\
}";
editorConfig.setup = function (editor) {
initializeTinyMceMentions(editor, function () {
return $.getJSON(
admin_url + "tasks/get_staff_names_for_mentions/" + taskid
)
})
};
init_editor("#task_comment", editorConfig)
}
function initializeTinyMceMentions(editor, usersCallback) {
if(!Object.hasOwn(editor, 'perfexCommands')) {
editor.perfexCommands = {}
}
let cachedUsers = null;
editor.perfexCommands.getUsersForMention = async function() {
if(Array.isArray(cachedUsers)) {
return cachedUsers
}
let users = await usersCallback()
cachedUsers = users.map(u=>({
value: u.id.toString(),
text: u.name,
}));
return cachedUsers
},
editor.perfexCommands.insertMentionUser = function (id, name, rng) {
// Insert in to the editor
editor.selection.setRng(rng || 0)
editor.insertContent((
'
@' +
name +
" "
))
}
editor.ui.registry.addAutocompleter('mentions', {
trigger: '@', // the trigger character to open the autocompleter
minChars: 0, // 0 to open the dropdown immediately after the @ is typed
columns: 1, // must be 1 for text-based results
// Retrieve the available users
fetch: function (pattern) {
return new Promise(resolve =>
resolve(editor.perfexCommands.getUsersForMention())
)
},
// Executed when user is selected from the dropdown
onAction: function (autocompleteApi, rng, value) {
editor.perfexCommands.getUsersForMention().then(users=> {
let user = users.find(user=>user.value == value)
editor.perfexCommands.insertMentionUser(value, user.text, rng)
autocompleteApi.hide()
})
},
})
}
function init_ajax_search(type, selector, server_data, url) {
var ajaxSelector = $("body").find(selector);
if (ajaxSelector.length) {
var options = {
ajax: {
url:
typeof url == "undefined"
? admin_url + "misc/get_relation_data"
: url,
data: function () {
var data = {};
data.type = type;
data.rel_id = "";
data.q = "{{{q}}}";
if (typeof server_data != "undefined") {
jQuery.extend(data, server_data);
}
return data;
},
},
locale: {
emptyTitle: app.lang.search_ajax_empty,
statusInitialized: app.lang.search_ajax_initialized,
statusSearching: app.lang.search_ajax_searching,
statusNoResults: app.lang.not_results_found,
searchPlaceholder: app.lang.search_ajax_placeholder,
currentlySelected: app.lang.currently_selected,
},
requestDelay: 500,
cache: false,
preprocessData: function (processData) {
var bs_data = [];
var len = processData.length;
for (var i = 0; i < len; i++) {
var tmp_data = {
value: processData[i].id,
text: processData[i].name,
};
if (processData[i].subtext) {
tmp_data.data = {
subtext: processData[i].subtext,
};
}
bs_data.push(tmp_data);
}
return bs_data;
},
preserveSelectedPosition: "after",
preserveSelected: true,
};
if (ajaxSelector.data("empty-title")) {
options.locale.emptyTitle = ajaxSelector.data("empty-title");
}
ajaxSelector.selectpicker().ajaxSelectPicker(options);
}
}
// Used for email template URL
function merge_field_format_url(url, node, on_save, name) {
// Merge fields url
if (url && url.indexOf("%7B") > -1 && url.indexOf("%7D") > -1) {
url = url.replaceAll("%7B", "{").replaceAll("%7D", "}");
}
return url;
}
function salesGoogleDriveSave(pickData) {
salesExtenalFileUpload(pickData, "gdrive");
}
function leadExternalFileUpload(files, externalType, leadId) {
$.post(admin_url + "leads/add_external_attachment", {
files: files,
lead_id: leadId,
external: externalType,
}).done(function () {
init_lead_modal_data(leadId);
});
}
function taskExternalFileUpload(files, externalType, taskId) {
$.post(admin_url + "tasks/add_external_attachment", {
files: files,
task_id: taskId,
external: externalType,
}).done(function () {
init_task_modal(taskId);
});
}
function salesExtenalFileUpload(files, externalType) {
var _data = {};
_data.rel_id = $("body").find('input[name="_attachment_sale_id"]').val();
_data.type = $("body").find('input[name="_attachment_sale_type"]').val();
_data.files = files;
_data.external = externalType;
$.post(admin_url + "misc/add_sales_external_attachment", _data).done(
function () {
if (_data.type == "estimate") {
if ($("body").hasClass("estimates-pipeline")) {
estimate_pipeline_open(_data.rel_id);
} else {
init_estimate(_data.rel_id);
}
} else if (_data.type == "proposal") {
if ($("body").hasClass("proposals-pipeline")) {
proposal_pipeline_open(_data.rel_id);
} else {
init_proposal(_data.rel_id);
}
} else {
if (typeof window["init_" + _data.type] == "function") {
window["init_" + _data.type](_data.rel_id);
}
}
$("#sales_attach_file").modal("hide");
}
);
}
function set_search_history(history) {
var $searchHistory = $("#search-history");
var historyHtml = "";
for (var i = 0; i < history.length; i++) {
historyHtml +=
'
' +
history[i] +
' ';
}
$searchHistory.html(historyHtml);
}
// General helper function for $.get ajax requests
function requestGet(uri, params) {
params = typeof params == "undefined" ? {} : params;
var options = {
type: "GET",
url: uri.indexOf(admin_url) > -1 ? uri : admin_url + uri,
};
return $.ajax($.extend({}, options, params));
}
// General helper function for $.get ajax requests with dataType JSON
function requestGetJSON(uri, params) {
params = typeof params == "undefined" ? {} : params;
params.dataType = "json";
return requestGet(uri, params);
}
// Templates Js
function update_templates_count(type) {
var count = $(".templates-wrapper").length;
$(".total_templates").text(count);
if (count === 0) {
$(".total_templates").addClass("hide");
} else {
$(".total_templates").removeClass("hide");
}
}
function get_templates(rel_type, rel_id) {
if (rel_type === "proposals") {
$("#proposal-templates").load(
admin_url + "templates",
{
rel_type: rel_type,
rel_id: rel_id,
},
function () {
update_templates_count();
}
);
} else if (rel_type === "contracts") {
$("#contract-templates").load(
admin_url + "templates",
{
rel_type: rel_type,
rel_id: rel_id,
},
function () {
update_templates_count();
}
);
}
}
function add_template(rel_type, rel_id) {
$("#modal-wrapper").load(
admin_url + "templates/modal",
{
slug: "new",
rel_type: rel_type,
rel_id: rel_id,
},
function () {
if ($("#TemplateModal").is(":hidden")) {
$("#TemplateModal").modal({
backdrop: "static",
show: true,
});
}
appValidateForm($("#template-form"), {
name: "required",
});
tinymce.remove("#content");
init_editor("#content", {
pagebreak_split_block: true,
contextmenu:
"link image inserttable | cell row column deletetable | paste copy | pagebreak",
append_plugins: "pagebreak",
});
}
);
}
function edit_template(rel_type, id, rel_id) {
$("#modal-wrapper").load(
admin_url + "templates/modal",
{
slug: "edit",
id: id,
rel_type: rel_type,
rel_id: rel_id,
},
function () {
if ($("#TemplateModal").is(":hidden")) {
$("#TemplateModal").modal({
backdrop: "static",
show: true,
});
}
appValidateForm($("#template-form"), {
name: "required",
});
tinymce.remove("#content");
init_editor("#content", {
pagebreak_split_block: true,
contextmenu:
"link image inserttable | cell row column deletetable | paste copy | pagebreak",
append_plugins: "pagebreak",
});
}
);
}
function delete_template(wrapper, rel_type, id) {
if (confirm_delete()) {
$.post(admin_url + "templates/delete/" + id).done(function (response) {
response = JSON.parse(response);
if (response.success === true || response.success == "true") {
if (rel_type === "proposals") {
$(wrapper).parents(".proposal-templates-wrapper").html("");
} else if (rel_type === "contracts") {
$(wrapper).parents(".contract-templates-wrapper").html("");
}
get_templates(rel_type);
}
});
}
}
function insert_template(wrapper, rel_type, id) {
requestGetJSON(admin_url + "templates/index/" + id).done(function (response) {
var data = response.data;
tinymce.activeEditor.execCommand("mceInsertContent", false, data.content);
if (rel_type == "proposals") {
$('a[aria-controls="tab_proposal"]').click();
} else if (rel_type == "contracts") {
$('a[aria-controls="tab_content"]').click();
}
tinymce.activeEditor.focus();
});
}
function retrieve_imap_folders(url, params) {
var dfd = $.Deferred();
$("#folders-loader").addClass("spinning").removeClass("hidden");
$.post(url, params)
.done(function (response) {
response = JSON.parse(response);
if (response.hasOwnProperty("alert_type")) {
alert_float(response.alert_type, response.message);
} else {
var output = "";
var $folder = $("#folder");
var currentFolder = $folder.selectpicker("val");
response.forEach(function (folderName) {
output +=
'
";
});
$folder.html(output);
$folder.selectpicker("refresh");
if (!currentFolder) {
$folder.selectpicker("val", $folder.find("option:eq(0)")[0].value);
}
}
dfd.resolve(response);
})
.fail(function (error) {
dfd.reject(error);
})
.always(function () {
$("#folders-loader").removeClass("spinning").addClass("hidden");
});
return dfd.promise();
}
function add_batch_payment() {
$("#modal-wrapper").load(
admin_url + "payments/batch_payment_modal",
function () {
if ($("#batch-payment-modal").is(":hidden")) {
$("#batch-payment-modal").modal({
backdrop: "static",
show: true,
});
}
init_selectpicker();
init_datepicker();
var $filterByClientSelect = $("#batch-payment-filter");
$filterByClientSelect.on("changed.bs.select", function () {
if ($filterByClientSelect.val() !== "") {
$(".batch_payment_item").each(function () {
if ($(this).data("clientid") == $filterByClientSelect.val()) {
$(this).find("input, select").prop("disabled", false);
$(this).removeClass("hide");
} else {
$(this).addClass("hide");
$(this).find("input, select").prop("disabled", true);
}
});
} else {
$(".batch_payment_item").each(function () {
$(this).removeClass("hide");
$(this).find("input, select").prop("disabled", false);
});
}
});
appValidateForm($("#batch-payment-form"), {});
$(".batch_payment_item").each(function () {
var invoiceLine = $(this).find('[name^="invoice"]');
invoiceLine
.filter('select[name$="[paymentmode]"],input[name$="[amount]"]')
.each(function () {
var field = $(this);
field.rules("add", {
required: function () {
var isRequired = false;
var rowFields = field
.closest(".batch_payment_item")
.find("input, select");
rowFields
.filter(
'select[name$="[paymentmode]"],input[name$="[transactionid]"],input[name$="[amount]"]'
)
.each(function () {
if ($(this).val() != "") {
isRequired = true;
}
if ($(this).hasClass("selectpicker") && isRequired) {
field.prop("required", true);
$(this).selectpicker("refresh");
}
});
return isRequired;
},
});
});
});
}
);
}
/**
* @DEPRECATED FUNCTIONS
*/
/**
* @deprecated
*/
function initDatatableOffline(dt_table) {
console.warn(
'"initDatatableOffline" is deprecated, use "initDataTableInline" instead.'
);
initDataTableInline(dt_table);
}
/**
* @deprecated
* @since 2.3.2
*/
function init_currency_symbol() {
console.warn(
'"init_currency_symbol" is deprecated, use "init_currency" instead'
);
init_currency();
}