feat: Show timesheets related to customer's projects on customer portal (#19443)

* fix: Show timesheets related to customer's projects on customer portal

* style: fix codacy
diff --git a/erpnext/projects/doctype/timesheet/timesheet.py b/erpnext/projects/doctype/timesheet/timesheet.py
index 9ee2927..bc88250 100644
--- a/erpnext/projects/doctype/timesheet/timesheet.py
+++ b/erpnext/projects/doctype/timesheet/timesheet.py
@@ -353,17 +353,35 @@
 def get_timesheets_list(doctype, txt, filters, limit_start, limit_page_length=20, order_by="modified"):
 	user = frappe.session.user
 	# find customer name from contact.
-	customer = frappe.db.sql('''SELECT dl.link_name FROM `tabContact` AS c inner join \
-		`tabDynamic Link` AS dl ON c.first_name=dl.link_name WHERE c.email_id=%s''',user)
+	customer = ''
+	timesheets = []
+
+	contact = frappe.db.exists('Contact', {'user': user})
+	if contact:
+		# find customer
+		contact = frappe.get_doc('Contact', contact)
+		customer = contact.get_link_for('Customer')
 
 	if customer:
-		# find list of Sales Invoice for made for customer.
-		sales_invoice = frappe.db.sql('''SELECT name FROM `tabSales Invoice` WHERE customer = %s''',customer)
+		sales_invoices = [d.name for d in frappe.get_all('Sales Invoice', filters={'customer': customer})] or [None]
+		projects = [d.name for d in frappe.get_all('Project', filters={'customer': customer})]
 		# Return timesheet related data to web portal.
-		return frappe. db.sql('''SELECT ts.name, tsd.activity_type, ts.status, ts.total_billable_hours, \
-			tsd.sales_invoice, tsd.project  FROM `tabTimesheet` AS ts inner join `tabTimesheet Detail` \
-			AS tsd ON tsd.parent = ts.name where tsd.sales_invoice IN %s order by\
-			end_date asc limit {0} , {1}'''.format(limit_start, limit_page_length), [sales_invoice], as_dict = True)
+		timesheets = frappe.db.sql('''
+			SELECT
+				ts.name, tsd.activity_type, ts.status, ts.total_billable_hours,
+				COALESCE(ts.sales_invoice, tsd.sales_invoice) AS sales_invoice, tsd.project
+			FROM `tabTimesheet` ts, `tabTimesheet Detail` tsd
+			WHERE tsd.parent = ts.name AND
+				(
+					ts.sales_invoice IN %(sales_invoices)s OR
+					tsd.sales_invoice IN %(sales_invoices)s OR
+					tsd.project IN %(projects)s
+				)
+			ORDER BY `end_date` ASC
+			LIMIT {0}, {1}
+		'''.format(limit_start, limit_page_length), dict(sales_invoices=sales_invoices, projects=projects), as_dict=True) #nosec
+
+	return timesheets
 
 def get_list_context(context=None):
 	return {
diff --git a/erpnext/templates/includes/timesheet/timesheet_row.html b/erpnext/templates/includes/timesheet/timesheet_row.html
index e9cfcda..4852f59 100644
--- a/erpnext/templates/includes/timesheet/timesheet_row.html
+++ b/erpnext/templates/includes/timesheet/timesheet_row.html
@@ -1,13 +1,14 @@
-<div class="web-list-item">
-	<a href="/timesheets?name={{ doc.name | urlencode }}" class="no-decoration">
-		<div class="row">
-			<div class="col-xs-3">
-				<span class="indicator {{ "red" if doc.status=="Cancelled" else "green" if doc.status=="Billed" else "blue" if doc.status=="Submitted" else "darkgrey" }}">{{ doc.name }}</span>
-			</div>
-			<div class="col-xs-3"> Billable Hours: {{ doc.total_billable_hours}} </div>
-			<div class="col-xs-2"> {{ _(doc.sales_invoice) }} </div>
-			<div class="col-xs-2"> {{ _(doc.project) }} </div>
-			<div class="col-xs-2"> {{ _(doc.activity_type) }} </div>
+<div class="web-list-item transaction-list-item">
+	<div class="row">
+		<div class="col-xs-3">
+			<span class='indicator {{ "red" if doc.status=="Cancelled" else "green" if doc.status=="Billed" else "blue" if doc.status=="Submitted" else "darkgrey" }} small'>
+				{{ doc.name }}
+			</span>
 		</div>
-	</a>
+		<div class="col-xs-2 small"> {{ doc.total_billable_hours }} </div>
+		<div class="col-xs-2 small"> {{ doc.project or '' }} </div>
+		<div class="col-xs-2 small"> {{ doc.sales_invoice or '' }} </div>
+		<div class="col-xs-2 small"> {{ _(doc.activity_type) }} </div>
+	</div>
+	<!-- <a href="/timesheets?name={{ doc.name | urlencode }}" class="transaction-item-link">Link</a> -->
 </div>