mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-22 14:39:19 +00:00
Merge branch 'alyf-de-si_total_billing_hours_v13' into version-13-hotfix
This commit is contained in:
@@ -719,19 +719,6 @@ frappe.ui.form.on('Sales Invoice', {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
project: function(frm){
|
|
||||||
if (!frm.doc.is_return) {
|
|
||||||
frm.call({
|
|
||||||
method: "add_timesheet_data",
|
|
||||||
doc: frm.doc,
|
|
||||||
callback: function(r, rt) {
|
|
||||||
refresh_field(['timesheets'])
|
|
||||||
}
|
|
||||||
})
|
|
||||||
frm.refresh();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
onload: function(frm) {
|
onload: function(frm) {
|
||||||
frm.redemption_conversion_factor = null;
|
frm.redemption_conversion_factor = null;
|
||||||
},
|
},
|
||||||
@@ -842,25 +829,92 @@ frappe.ui.form.on('Sales Invoice', {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
add_timesheet_row: function(frm, row, exchange_rate) {
|
project: function(frm) {
|
||||||
frm.add_child('timesheets', {
|
if (frm.doc.project) {
|
||||||
'activity_type': row.activity_type,
|
frm.events.add_timesheet_data(frm, {
|
||||||
'description': row.description,
|
project: frm.doc.project
|
||||||
'time_sheet': row.parent,
|
});
|
||||||
'billing_hours': row.billing_hours,
|
}
|
||||||
'billing_amount': flt(row.billing_amount) * flt(exchange_rate),
|
},
|
||||||
'timesheet_detail': row.name,
|
|
||||||
'project_name': row.project_name
|
async add_timesheet_data(frm, kwargs) {
|
||||||
|
if (kwargs === "Sales Invoice") {
|
||||||
|
// called via frm.trigger()
|
||||||
|
kwargs = Object();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!kwargs.hasOwnProperty("project") && frm.doc.project) {
|
||||||
|
kwargs.project = frm.doc.project;
|
||||||
|
}
|
||||||
|
|
||||||
|
const timesheets = await frm.events.get_timesheet_data(frm, kwargs);
|
||||||
|
return frm.events.set_timesheet_data(frm, timesheets);
|
||||||
|
},
|
||||||
|
|
||||||
|
async get_timesheet_data(frm, kwargs) {
|
||||||
|
return frappe.call({
|
||||||
|
method: "erpnext.projects.doctype.timesheet.timesheet.get_projectwise_timesheet_data",
|
||||||
|
args: kwargs
|
||||||
|
}).then(r => {
|
||||||
|
if (!r.exc && r.message.length > 0) {
|
||||||
|
return r.message
|
||||||
|
} else {
|
||||||
|
return []
|
||||||
|
}
|
||||||
});
|
});
|
||||||
frm.refresh_field('timesheets');
|
},
|
||||||
calculate_total_billing_amount(frm);
|
|
||||||
|
set_timesheet_data: function(frm, timesheets) {
|
||||||
|
frm.clear_table("timesheets")
|
||||||
|
timesheets.forEach(timesheet => {
|
||||||
|
if (frm.doc.currency != timesheet.currency) {
|
||||||
|
frappe.call({
|
||||||
|
method: "erpnext.setup.utils.get_exchange_rate",
|
||||||
|
args: {
|
||||||
|
from_currency: timesheet.currency,
|
||||||
|
to_currency: frm.doc.currency
|
||||||
|
},
|
||||||
|
callback: function(r) {
|
||||||
|
if (r.message) {
|
||||||
|
exchange_rate = r.message;
|
||||||
|
frm.events.append_time_log(frm, timesheet, exchange_rate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
frm.events.append_time_log(frm, timesheet, 1.0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
append_time_log: function(frm, time_log, exchange_rate) {
|
||||||
|
const row = frm.add_child("timesheets");
|
||||||
|
row.activity_type = time_log.activity_type;
|
||||||
|
row.description = time_log.description;
|
||||||
|
row.time_sheet = time_log.time_sheet;
|
||||||
|
row.from_time = time_log.from_time;
|
||||||
|
row.to_time = time_log.to_time;
|
||||||
|
row.billing_hours = time_log.billing_hours;
|
||||||
|
row.billing_amount = flt(time_log.billing_amount) * flt(exchange_rate);
|
||||||
|
row.timesheet_detail = time_log.name;
|
||||||
|
row.project_name = time_log.project_name;
|
||||||
|
|
||||||
|
frm.refresh_field("timesheets");
|
||||||
|
frm.trigger("calculate_timesheet_totals");
|
||||||
|
},
|
||||||
|
|
||||||
|
calculate_timesheet_totals: function(frm) {
|
||||||
|
frm.set_value("total_billing_amount",
|
||||||
|
frm.doc.timesheets.reduce((a, b) => a + (b["billing_amount"] || 0.0), 0.0));
|
||||||
|
frm.set_value("total_billing_hours",
|
||||||
|
frm.doc.timesheets.reduce((a, b) => a + (b["billing_hours"] || 0.0), 0.0));
|
||||||
},
|
},
|
||||||
|
|
||||||
refresh: function(frm) {
|
refresh: function(frm) {
|
||||||
if (frm.doc.docstatus===0 && !frm.doc.is_return) {
|
if (frm.doc.docstatus===0 && !frm.doc.is_return) {
|
||||||
frm.add_custom_button(__('Fetch Timesheet'), function() {
|
frm.add_custom_button(__("Fetch Timesheet"), function() {
|
||||||
let d = new frappe.ui.Dialog({
|
let d = new frappe.ui.Dialog({
|
||||||
title: __('Fetch Timesheet'),
|
title: __("Fetch Timesheet"),
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
"label" : __("From"),
|
"label" : __("From"),
|
||||||
@@ -869,8 +923,8 @@ frappe.ui.form.on('Sales Invoice', {
|
|||||||
"reqd": 1,
|
"reqd": 1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fieldtype: 'Column Break',
|
fieldtype: "Column Break",
|
||||||
fieldname: 'col_break_1',
|
fieldname: "col_break_1",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label" : __("To"),
|
"label" : __("To"),
|
||||||
@@ -887,48 +941,18 @@ frappe.ui.form.on('Sales Invoice', {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
primary_action: function() {
|
primary_action: function() {
|
||||||
let data = d.get_values();
|
const data = d.get_values();
|
||||||
frappe.call({
|
frm.events.add_timesheet_data(frm, {
|
||||||
method: "erpnext.projects.doctype.timesheet.timesheet.get_projectwise_timesheet_data",
|
from_time: data.from_time,
|
||||||
args: {
|
to_time: data.to_time,
|
||||||
from_time: data.from_time,
|
project: data.project
|
||||||
to_time: data.to_time,
|
|
||||||
project: data.project
|
|
||||||
},
|
|
||||||
callback: function(r) {
|
|
||||||
if (!r.exc && r.message.length > 0) {
|
|
||||||
frm.clear_table('timesheets')
|
|
||||||
r.message.forEach((d) => {
|
|
||||||
let exchange_rate = 1.0;
|
|
||||||
if (frm.doc.currency != d.currency) {
|
|
||||||
frappe.call({
|
|
||||||
method: 'erpnext.setup.utils.get_exchange_rate',
|
|
||||||
args: {
|
|
||||||
from_currency: d.currency,
|
|
||||||
to_currency: frm.doc.currency
|
|
||||||
},
|
|
||||||
callback: function(r) {
|
|
||||||
if (r.message) {
|
|
||||||
exchange_rate = r.message;
|
|
||||||
frm.events.add_timesheet_row(frm, d, exchange_rate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
frm.events.add_timesheet_row(frm, d, exchange_rate);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
frappe.msgprint(__('No Timesheets found with the selected filters.'))
|
|
||||||
}
|
|
||||||
d.hide();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
d.hide();
|
||||||
},
|
},
|
||||||
primary_action_label: __('Get Timesheets')
|
primary_action_label: __("Get Timesheets")
|
||||||
});
|
});
|
||||||
d.show();
|
d.show();
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (frm.doc.is_debit_note) {
|
if (frm.doc.is_debit_note) {
|
||||||
@@ -961,26 +985,22 @@ frappe.ui.form.on('Sales Invoice', {
|
|||||||
frm: frm
|
frm: frm
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
create_dunning: function(frm) {
|
create_dunning: function(frm) {
|
||||||
frappe.model.open_mapped_doc({
|
frappe.model.open_mapped_doc({
|
||||||
method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.create_dunning",
|
method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.create_dunning",
|
||||||
frm: frm
|
frm: frm
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
var calculate_total_billing_amount = function(frm) {
|
|
||||||
var doc = frm.doc;
|
|
||||||
|
|
||||||
doc.total_billing_amount = 0.0
|
frappe.ui.form.on("Sales Invoice Timesheet", {
|
||||||
if (doc.timesheets) {
|
timesheets_remove(frm, cdt, cdn) {
|
||||||
$.each(doc.timesheets, function(index, data){
|
frm.trigger("calculate_timesheet_totals");
|
||||||
doc.total_billing_amount += flt(data.billing_amount)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
refresh_field('total_billing_amount')
|
|
||||||
}
|
|
||||||
|
|
||||||
var set_timesheet_detail_rate = function(cdt, cdn, currency, timelog) {
|
var set_timesheet_detail_rate = function(cdt, cdn, currency, timelog) {
|
||||||
frappe.call({
|
frappe.call({
|
||||||
|
|||||||
@@ -74,6 +74,7 @@
|
|||||||
"time_sheet_list",
|
"time_sheet_list",
|
||||||
"timesheets",
|
"timesheets",
|
||||||
"total_billing_amount",
|
"total_billing_amount",
|
||||||
|
"total_billing_hours",
|
||||||
"section_break_30",
|
"section_break_30",
|
||||||
"total_qty",
|
"total_qty",
|
||||||
"base_total",
|
"base_total",
|
||||||
@@ -2011,6 +2012,13 @@
|
|||||||
"fieldtype": "Small Text",
|
"fieldtype": "Small Text",
|
||||||
"label": "Dispatch Address",
|
"label": "Dispatch Address",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "total_billing_hours",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"label": "Total Billing Hours",
|
||||||
|
"print_hide": 1,
|
||||||
|
"read_only": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"icon": "fa fa-file-text",
|
"icon": "fa fa-file-text",
|
||||||
@@ -2023,7 +2031,7 @@
|
|||||||
"link_fieldname": "consolidated_invoice"
|
"link_fieldname": "consolidated_invoice"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"modified": "2021-08-25 14:46:05.279588",
|
"modified": "2021-08-25 15:46:05.279588",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Sales Invoice",
|
"name": "Sales Invoice",
|
||||||
|
|||||||
@@ -757,7 +757,7 @@ class SalesInvoice(SellingController):
|
|||||||
if self.project:
|
if self.project:
|
||||||
for data in get_projectwise_timesheet_data(self.project):
|
for data in get_projectwise_timesheet_data(self.project):
|
||||||
self.append('timesheets', {
|
self.append('timesheets', {
|
||||||
'time_sheet': data.parent,
|
'time_sheet': data.time_sheet,
|
||||||
'billing_hours': data.billing_hours,
|
'billing_hours': data.billing_hours,
|
||||||
'billing_amount': data.billing_amount,
|
'billing_amount': data.billing_amount,
|
||||||
'timesheet_detail': data.name,
|
'timesheet_detail': data.name,
|
||||||
@@ -768,12 +768,11 @@ class SalesInvoice(SellingController):
|
|||||||
self.calculate_billing_amount_for_timesheet()
|
self.calculate_billing_amount_for_timesheet()
|
||||||
|
|
||||||
def calculate_billing_amount_for_timesheet(self):
|
def calculate_billing_amount_for_timesheet(self):
|
||||||
total_billing_amount = 0.0
|
def timesheet_sum(field):
|
||||||
for data in self.timesheets:
|
return sum((ts.get(field) or 0.0) for ts in self.timesheets)
|
||||||
if data.billing_amount:
|
|
||||||
total_billing_amount += data.billing_amount
|
|
||||||
|
|
||||||
self.total_billing_amount = total_billing_amount
|
self.total_billing_amount = timesheet_sum("billing_amount")
|
||||||
|
self.total_billing_hours = timesheet_sum("billing_hours")
|
||||||
|
|
||||||
def get_warehouse(self):
|
def get_warehouse(self):
|
||||||
user_pos_profile = frappe.db.sql("""select name, warehouse from `tabPOS Profile`
|
user_pos_profile = frappe.db.sql("""select name, warehouse from `tabPOS Profile`
|
||||||
|
|||||||
@@ -7,12 +7,19 @@
|
|||||||
"field_order": [
|
"field_order": [
|
||||||
"activity_type",
|
"activity_type",
|
||||||
"description",
|
"description",
|
||||||
|
"section_break_3",
|
||||||
|
"from_time",
|
||||||
|
"column_break_5",
|
||||||
|
"to_time",
|
||||||
|
"section_break_7",
|
||||||
"billing_hours",
|
"billing_hours",
|
||||||
|
"column_break_9",
|
||||||
"billing_amount",
|
"billing_amount",
|
||||||
|
"section_break_11",
|
||||||
|
"timesheet_detail",
|
||||||
"column_break_5",
|
"column_break_5",
|
||||||
"time_sheet",
|
"time_sheet",
|
||||||
"project_name",
|
"project_name"
|
||||||
"timesheet_detail"
|
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
@@ -64,10 +71,40 @@
|
|||||||
"label": "Description",
|
"label": "Description",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "from_time",
|
||||||
|
"fieldtype": "Datetime",
|
||||||
|
"label": "From Time"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "to_time",
|
||||||
|
"fieldtype": "Datetime",
|
||||||
|
"label": "To Time"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "section_break_3",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"label": "Time"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "column_break_5",
|
"fieldname": "column_break_5",
|
||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
|
||||||
|
"fieldname": "section_break_7",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"label": "Totals"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_9",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "section_break_11",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"label": "Reference"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "project_name",
|
"fieldname": "project_name",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
@@ -77,7 +114,7 @@
|
|||||||
],
|
],
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-06-08 14:43:02.748981",
|
"modified": "2021-08-15 18:37:08.084930",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Sales Invoice Timesheet",
|
"name": "Sales Invoice Timesheet",
|
||||||
|
|||||||
@@ -216,25 +216,53 @@ class Timesheet(Document):
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_projectwise_timesheet_data(project=None, parent=None, from_time=None, to_time=None):
|
def get_projectwise_timesheet_data(project=None, parent=None, from_time=None, to_time=None):
|
||||||
condition = ''
|
condition = ""
|
||||||
if project:
|
if project:
|
||||||
condition += "and tsd.project = %(project)s"
|
condition += "AND tsd.project = %(project)s "
|
||||||
if parent:
|
if parent:
|
||||||
condition += "AND tsd.parent = %(parent)s"
|
condition += "AND tsd.parent = %(parent)s "
|
||||||
if from_time and to_time:
|
if from_time and to_time:
|
||||||
condition += "AND CAST(tsd.from_time as DATE) BETWEEN %(from_time)s AND %(to_time)s"
|
condition += "AND CAST(tsd.from_time as DATE) BETWEEN %(from_time)s AND %(to_time)s"
|
||||||
|
|
||||||
return frappe.db.sql("""SELECT tsd.name as name,
|
query = f"""
|
||||||
tsd.parent as parent, tsd.billing_hours as billing_hours,
|
SELECT
|
||||||
tsd.billing_amount as billing_amount, tsd.activity_type as activity_type,
|
|
||||||
tsd.description as description, ts.currency as currency,
|
tsd.name as name,
|
||||||
tsd.project_name as project_name
|
tsd.parent as time_sheet,
|
||||||
FROM `tabTimesheet Detail` tsd
|
tsd.from_time as from_time,
|
||||||
INNER JOIN `tabTimesheet` ts ON ts.name = tsd.parent
|
tsd.to_time as to_time,
|
||||||
WHERE tsd.parenttype = 'Timesheet'
|
tsd.billing_hours as billing_hours,
|
||||||
and tsd.docstatus=1 {0}
|
tsd.billing_amount as billing_amount,
|
||||||
and tsd.is_billable = 1
|
tsd.activity_type as activity_type,
|
||||||
and tsd.sales_invoice is null""".format(condition), {'project': project, 'parent': parent, 'from_time': from_time, 'to_time': to_time}, as_dict=1)
|
tsd.description as description,
|
||||||
|
ts.currency as currency,
|
||||||
|
tsd.project_name as project_name
|
||||||
|
|
||||||
|
FROM `tabTimesheet Detail` tsd
|
||||||
|
|
||||||
|
INNER JOIN `tabTimesheet` ts
|
||||||
|
ON ts.name = tsd.parent
|
||||||
|
|
||||||
|
WHERE
|
||||||
|
|
||||||
|
tsd.parenttype = 'Timesheet'
|
||||||
|
AND tsd.docstatus = 1
|
||||||
|
AND tsd.is_billable = 1
|
||||||
|
AND tsd.sales_invoice is NULL
|
||||||
|
{condition}
|
||||||
|
|
||||||
|
ORDER BY tsd.from_time ASC
|
||||||
|
"""
|
||||||
|
|
||||||
|
filters = {
|
||||||
|
"project": project,
|
||||||
|
"parent": parent,
|
||||||
|
"from_time": from_time,
|
||||||
|
"to_time": to_time
|
||||||
|
}
|
||||||
|
|
||||||
|
return frappe.db.sql(query, filters, as_dict=1)
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_timesheet_detail_rate(timelog, currency):
|
def get_timesheet_detail_rate(timelog, currency):
|
||||||
|
|||||||
Reference in New Issue
Block a user