Compare commits

...

4 Commits

Author SHA1 Message Date
Raffael Meyer
927cd173cf fix: typo
(cherry picked from commit 0f2f11cb33)

# Conflicts:
#	erpnext/accounts/doctype/sales_invoice/sales_invoice.json
2025-03-03 00:12:51 +00:00
barredterra
216563d1e3 feat: sort timesheets by start time
(cherry picked from commit c82611aa62)

# Conflicts:
#	erpnext/projects/doctype/timesheet/timesheet.py
2025-03-03 00:12:51 +00:00
barredterra
b2bb4d9020 feat: refactor and enhance sales invoice timesheet
(cherry picked from commit 1110f88e5a)

# Conflicts:
#	erpnext/accounts/doctype/sales_invoice/sales_invoice.js
#	erpnext/accounts/doctype/sales_invoice/sales_invoice.py
#	erpnext/accounts/doctype/sales_invoice_timesheet/sales_invoice_timesheet.json
#	erpnext/projects/doctype/timesheet/timesheet.py
2025-03-03 00:12:51 +00:00
barredterra
fcdf1b4241 feat: add total_billing_hours to Sales Invoice
(cherry picked from commit b57521a337)

# Conflicts:
#	erpnext/accounts/doctype/sales_invoice/sales_invoice.js
#	erpnext/accounts/doctype/sales_invoice/sales_invoice.json
2025-03-03 00:12:50 +00:00
5 changed files with 258 additions and 1 deletions

View File

@@ -796,7 +796,11 @@ frappe.ui.form.on("Sales Invoice", {
}
},
<<<<<<< HEAD
onload: function (frm) {
=======
onload: function(frm) {
>>>>>>> 1110f88e5a (feat: refactor and enhance sales invoice timesheet)
frm.redemption_conversion_factor = null;
},
@@ -909,20 +913,101 @@ frappe.ui.form.on("Sales Invoice", {
}
},
<<<<<<< HEAD
<<<<<<< HEAD
=======
project: function(frm) {
if (frm.doc.project) {
frm.events.add_timesheet_data(frm, {
project: frm.doc.project
});
}
},
>>>>>>> 1110f88e5a (feat: refactor and enhance sales invoice timesheet)
async add_timesheet_data(frm, kwargs) {
if (kwargs === "Sales Invoice") {
// called via frm.trigger()
kwargs = Object();
}
<<<<<<< HEAD
if (!Object.prototype.hasOwnProperty.call(kwargs, "project") && frm.doc.project) {
=======
if (!kwargs.hasOwnProperty("project") && frm.doc.project) {
>>>>>>> 1110f88e5a (feat: refactor and enhance sales invoice timesheet)
kwargs.project = frm.doc.project;
}
const timesheets = await frm.events.get_timesheet_data(frm, kwargs);
return frm.events.set_timesheet_data(frm, timesheets);
<<<<<<< HEAD
=======
add_timesheet_row: function(frm, row, exchange_rate) {
frm.add_child('timesheets', {
'activity_type': row.activity_type,
'description': row.description,
'time_sheet': row.parent,
'billing_hours': row.billing_hours,
'billing_amount': flt(row.billing_amount) * flt(exchange_rate),
'timesheet_detail': row.name
=======
},
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 []
}
>>>>>>> 1110f88e5a (feat: refactor and enhance sales invoice timesheet)
});
},
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;
frm.refresh_field("timesheets");
frm.trigger("calculate_timesheet_totals");
>>>>>>> b57521a337 (feat: add `total_billing_hours` to Sales Invoice)
},
<<<<<<< HEAD
async get_timesheet_data(frm, kwargs) {
return frappe
.call({
@@ -1053,6 +1138,58 @@ frappe.ui.form.on("Sales Invoice", {
},
__("Get Items From")
);
=======
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) {
if (frm.doc.docstatus===0 && !frm.doc.is_return) {
frm.add_custom_button(__("Fetch Timesheet"), function() {
let d = new frappe.ui.Dialog({
title: __("Fetch Timesheet"),
fields: [
{
"label" : __("From"),
"fieldname": "from_time",
"fieldtype": "Date",
"reqd": 1,
},
{
fieldtype: "Column Break",
fieldname: "col_break_1",
},
{
"label" : __("To"),
"fieldname": "to_time",
"fieldtype": "Date",
"reqd": 1,
},
{
"label" : __("Project"),
"fieldname": "project",
"fieldtype": "Link",
"options": "Project",
"default": frm.doc.project
},
],
primary_action: function() {
const data = d.get_values();
frm.events.add_timesheet_data(frm, {
from_time: data.from_time,
to_time: data.to_time,
project: data.project
});
d.hide();
},
primary_action_label: __("Get Timesheets")
});
d.show();
});
>>>>>>> 1110f88e5a (feat: refactor and enhance sales invoice timesheet)
}
if (frm.doc.is_debit_note) {
@@ -1082,7 +1219,34 @@ var set_timesheet_detail_rate = function (cdt, cdn, currency, timelog) {
});
};
<<<<<<< HEAD
var select_loyalty_program = function (frm, loyalty_programs) {
=======
create_invoice_discounting: function(frm) {
frappe.model.open_mapped_doc({
method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.create_invoice_discounting",
frm: frm
});
},
create_dunning: function(frm) {
frappe.model.open_mapped_doc({
method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.create_dunning",
frm: frm
});
}
});
frappe.ui.form.on("Sales Invoice Timesheet", {
timesheets_remove(frm, cdt, cdn) {
frm.trigger("calculate_timesheet_totals");
}
});
var select_loyalty_program = function(frm, loyalty_programs) {
>>>>>>> b57521a337 (feat: add `total_billing_hours` to Sales Invoice)
var dialog = new frappe.ui.Dialog({
title: __("Select Loyalty Program"),
fields: [

View File

@@ -49,6 +49,18 @@
"set_target_warehouse",
"section_break_42",
"items",
<<<<<<< HEAD
=======
"pricing_rule_details",
"pricing_rules",
"packing_list",
"packed_items",
"product_bundle_help",
"time_sheet_list",
"timesheets",
"total_billing_amount",
"total_billing_hours",
>>>>>>> b57521a337 (feat: add `total_billing_hours` to Sales Invoice)
"section_break_30",
"total_qty",
"total_net_weight",
@@ -1977,6 +1989,7 @@
"read_only": 1
},
{
<<<<<<< HEAD
"default": "0",
"fieldname": "ignore_default_payment_terms_template",
"fieldtype": "Check",
@@ -1985,11 +1998,14 @@
"read_only": 1
},
{
=======
>>>>>>> b57521a337 (feat: add `total_billing_hours` to Sales Invoice)
"fieldname": "total_billing_hours",
"fieldtype": "Float",
"label": "Total Billing Hours",
"print_hide": 1,
"read_only": 1
<<<<<<< HEAD
},
{
"fieldname": "amount_eligible_for_commission",
@@ -2199,6 +2215,8 @@
"label": "Company Contact Person",
"options": "Contact",
"print_hide": 1
=======
>>>>>>> b57521a337 (feat: add `total_billing_hours` to Sales Invoice)
}
],
"icon": "fa fa-file-text",
@@ -2211,7 +2229,15 @@
"link_fieldname": "consolidated_invoice"
}
],
<<<<<<< HEAD
<<<<<<< HEAD
"modified": "2025-02-06 15:59:54.636202",
=======
"modified": "2021-08-02 18:36:51.978581",
>>>>>>> b57521a337 (feat: add `total_billing_hours` to Sales Invoice)
=======
"modified": "2021-08-15 18:40:20.445127",
>>>>>>> 0f2f11cb33 (fix: typo)
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice",
@@ -2266,4 +2292,4 @@
"title_field": "customer_name",
"track_changes": 1,
"track_seen": 1
}
}

View File

@@ -1110,6 +1110,7 @@ class SalesInvoice(SellingController):
self.set("timesheets", [])
if self.project:
for data in get_projectwise_timesheet_data(self.project):
<<<<<<< HEAD
self.append(
"timesheets",
{
@@ -1121,6 +1122,16 @@ class SalesInvoice(SellingController):
"description": data.description,
},
)
=======
self.append('timesheets', {
'time_sheet': data.time_sheet,
'billing_hours': data.billing_hours,
'billing_amount': data.billing_amount,
'timesheet_detail': data.name,
'activity_type': data.activity_type,
'description': data.description
})
>>>>>>> 1110f88e5a (feat: refactor and enhance sales invoice timesheet)
self.calculate_billing_amount_for_timesheet()

View File

@@ -103,6 +103,7 @@
"fieldname": "section_break_11",
"fieldtype": "Section Break",
"label": "Reference"
<<<<<<< HEAD
},
{
"fieldname": "project_name",
@@ -113,11 +114,17 @@
{
"fieldname": "column_break_13",
"fieldtype": "Column Break"
=======
>>>>>>> 1110f88e5a (feat: refactor and enhance sales invoice timesheet)
}
],
"istable": 1,
"links": [],
<<<<<<< HEAD
"modified": "2024-03-27 13:10:36.562795",
=======
"modified": "2021-08-02 23:03:08.084930",
>>>>>>> 1110f88e5a (feat: refactor and enhance sales invoice timesheet)
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice Timesheet",

View File

@@ -298,8 +298,18 @@ def get_projectwise_timesheet_data(project=None, parent=None, from_time=None, to
if from_time and to_time:
condition += "AND CAST(tsd.from_time as DATE) BETWEEN %(from_time)s AND %(to_time)s"
<<<<<<< HEAD
<<<<<<< HEAD
query = f"""
SELECT
=======
return frappe.db.sql("""
=======
query = f"""
>>>>>>> c82611aa62 (feat: sort timesheets by start time)
SELECT
>>>>>>> 1110f88e5a (feat: refactor and enhance sales invoice timesheet)
tsd.name as name,
tsd.parent as time_sheet,
tsd.from_time as from_time,
@@ -308,6 +318,7 @@ def get_projectwise_timesheet_data(project=None, parent=None, from_time=None, to
tsd.billing_amount as billing_amount,
tsd.activity_type as activity_type,
tsd.description as description,
<<<<<<< HEAD
ts.currency as currency,
tsd.project_name as project_name
FROM `tabTimesheet Detail` tsd
@@ -343,6 +354,44 @@ def get_timesheet_detail_rate(timelog, currency):
return timelog_detail.billing_amount * exchange_rate
return timelog_detail.billing_amount
=======
ts.currency as currency
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
<<<<<<< HEAD
AND tsd.sales_invoice is null
""".format(condition), {
'project': project,
'parent': parent,
'from_time': from_time,
'to_time': to_time
}, as_dict=1)
>>>>>>> 1110f88e5a (feat: refactor and enhance sales invoice timesheet)
=======
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)
>>>>>>> c82611aa62 (feat: sort timesheets by start time)
@frappe.whitelist()
@frappe.validate_and_sanitize_search_inputs