Merge pull request #37819 from s-aga-r/FIX-5274
fix: `TypeError` in PR for non-stock item
diff --git a/erpnext/accounts/report/general_ledger/general_ledger.js b/erpnext/accounts/report/general_ledger/general_ledger.js
index 37d0659..c0b4f59 100644
--- a/erpnext/accounts/report/general_ledger/general_ledger.js
+++ b/erpnext/accounts/report/general_ledger/general_ledger.js
@@ -193,7 +193,13 @@
"fieldname": "add_values_in_transaction_currency",
"label": __("Add Columns in Transaction Currency"),
"fieldtype": "Check"
+ },
+ {
+ "fieldname": "show_remarks",
+ "label": __("Show Remarks"),
+ "fieldtype": "Check"
}
+
]
}
diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py
index 79bfd78..5e484cf 100644
--- a/erpnext/accounts/report/general_ledger/general_ledger.py
+++ b/erpnext/accounts/report/general_ledger/general_ledger.py
@@ -163,6 +163,9 @@
select_fields = """, debit, credit, debit_in_account_currency,
credit_in_account_currency """
+ if filters.get("show_remarks"):
+ select_fields += """,remarks"""
+
order_by_statement = "order by posting_date, account, creation"
if filters.get("include_dimensions"):
@@ -195,7 +198,7 @@
voucher_type, voucher_no, {dimension_fields}
cost_center, project, {transaction_currency_fields}
against_voucher_type, against_voucher, account_currency,
- remarks, against, is_opening, creation {select_fields}
+ against, is_opening, creation {select_fields}
from `tabGL Entry`
where company=%(company)s {conditions}
{order_by_statement}
@@ -631,8 +634,10 @@
"width": 100,
},
{"label": _("Supplier Invoice No"), "fieldname": "bill_no", "fieldtype": "Data", "width": 100},
- {"label": _("Remarks"), "fieldname": "remarks", "width": 400},
]
)
+ if filters.get("show_remarks"):
+ columns.extend([{"label": _("Remarks"), "fieldname": "remarks", "width": 400}])
+
return columns
diff --git a/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py b/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py
index eac5426..e842d2e 100644
--- a/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py
+++ b/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py
@@ -47,6 +47,7 @@
out = []
for name, details in gle_map.items():
tax_amount, total_amount, grand_total, base_total = 0, 0, 0, 0
+ bill_no, bill_date = "", ""
tax_withholding_category = tax_category_map.get(name)
rate = tax_rate_map.get(tax_withholding_category)
@@ -70,7 +71,10 @@
if net_total_map.get(name):
if voucher_type == "Journal Entry" and tax_amount and rate:
# back calcalute total amount from rate and tax_amount
- total_amount = grand_total = base_total = tax_amount / (rate / 100)
+ if rate:
+ total_amount = grand_total = base_total = tax_amount / (rate / 100)
+ elif voucher_type == "Purchase Invoice":
+ total_amount, grand_total, base_total, bill_no, bill_date = net_total_map.get(name)
else:
total_amount, grand_total, base_total = net_total_map.get(name)
else:
@@ -96,7 +100,7 @@
row.update(
{
- "section_code": tax_withholding_category,
+ "section_code": tax_withholding_category or "",
"entity_type": party_map.get(party, {}).get(party_type),
"rate": rate,
"total_amount": total_amount,
@@ -106,10 +110,14 @@
"transaction_date": posting_date,
"transaction_type": voucher_type,
"ref_no": name,
+ "supplier_invoice_no": bill_no,
+ "supplier_invoice_date": bill_date,
}
)
out.append(row)
+ out.sort(key=lambda x: x["section_code"])
+
return out
@@ -157,14 +165,14 @@
def get_columns(filters):
pan = "pan" if frappe.db.has_column(filters.party_type, "pan") else "tax_id"
columns = [
- {"label": _(frappe.unscrub(pan)), "fieldname": pan, "fieldtype": "Data", "width": 60},
{
- "label": _(filters.get("party_type")),
- "fieldname": "party",
- "fieldtype": "Dynamic Link",
- "options": "party_type",
- "width": 180,
+ "label": _("Section Code"),
+ "options": "Tax Withholding Category",
+ "fieldname": "section_code",
+ "fieldtype": "Link",
+ "width": 90,
},
+ {"label": _(frappe.unscrub(pan)), "fieldname": pan, "fieldtype": "Data", "width": 60},
]
if filters.naming_series == "Naming Series":
@@ -179,51 +187,60 @@
columns.extend(
[
- {
- "label": _("Date of Transaction"),
- "fieldname": "transaction_date",
- "fieldtype": "Date",
- "width": 100,
- },
- {
- "label": _("Section Code"),
- "options": "Tax Withholding Category",
- "fieldname": "section_code",
- "fieldtype": "Link",
- "width": 90,
- },
{"label": _("Entity Type"), "fieldname": "entity_type", "fieldtype": "Data", "width": 100},
- {
- "label": _("Total Amount"),
- "fieldname": "total_amount",
- "fieldtype": "Float",
- "width": 90,
- },
+ ]
+ )
+ if filters.party_type == "Supplier":
+ columns.extend(
+ [
+ {
+ "label": _("Supplier Invoice No"),
+ "fieldname": "supplier_invoice_no",
+ "fieldtype": "Data",
+ "width": 120,
+ },
+ {
+ "label": _("Supplier Invoice Date"),
+ "fieldname": "supplier_invoice_date",
+ "fieldtype": "Date",
+ "width": 120,
+ },
+ ]
+ )
+
+ columns.extend(
+ [
{
"label": _("TDS Rate %") if filters.get("party_type") == "Supplier" else _("TCS Rate %"),
"fieldname": "rate",
"fieldtype": "Percent",
- "width": 90,
+ "width": 60,
},
{
- "label": _("Tax Amount"),
- "fieldname": "tax_amount",
+ "label": _("Total Amount"),
+ "fieldname": "total_amount",
"fieldtype": "Float",
- "width": 90,
- },
- {
- "label": _("Grand Total"),
- "fieldname": "grand_total",
- "fieldtype": "Float",
- "width": 90,
+ "width": 120,
},
{
"label": _("Base Total"),
"fieldname": "base_total",
"fieldtype": "Float",
- "width": 90,
+ "width": 120,
},
- {"label": _("Transaction Type"), "fieldname": "transaction_type", "width": 100},
+ {
+ "label": _("Tax Amount"),
+ "fieldname": "tax_amount",
+ "fieldtype": "Float",
+ "width": 120,
+ },
+ {
+ "label": _("Grand Total"),
+ "fieldname": "grand_total",
+ "fieldtype": "Float",
+ "width": 120,
+ },
+ {"label": _("Transaction Type"), "fieldname": "transaction_type", "width": 130},
{
"label": _("Reference No."),
"fieldname": "ref_no",
@@ -231,6 +248,12 @@
"options": "transaction_type",
"width": 180,
},
+ {
+ "label": _("Date of Transaction"),
+ "fieldname": "transaction_date",
+ "fieldtype": "Date",
+ "width": 100,
+ },
]
)
@@ -253,27 +276,7 @@
"Tax Withholding Account", {"company": filters.get("company")}, pluck="account"
)
- query_filters = {
- "account": ("in", tds_accounts),
- "posting_date": ("between", [filters.get("from_date"), filters.get("to_date")]),
- "is_cancelled": 0,
- "against": ("not in", bank_accounts),
- }
-
- party = frappe.get_all(filters.get("party_type"), pluck="name")
- or_filters.update({"against": ("in", party), "voucher_type": "Journal Entry"})
-
- if filters.get("party"):
- del query_filters["account"]
- del query_filters["against"]
- or_filters = {"against": filters.get("party"), "party": filters.get("party")}
-
- tds_docs = frappe.get_all(
- "GL Entry",
- filters=query_filters,
- or_filters=or_filters,
- fields=["voucher_no", "voucher_type", "against", "party"],
- )
+ tds_docs = get_tds_docs_query(filters, bank_accounts, tds_accounts).run(as_dict=True)
for d in tds_docs:
if d.voucher_type == "Purchase Invoice":
@@ -309,6 +312,47 @@
)
+def get_tds_docs_query(filters, bank_accounts, tds_accounts):
+ if not tds_accounts:
+ frappe.throw(
+ _("No {0} Accounts found for this company.").format(frappe.bold("Tax Withholding")),
+ title="Accounts Missing Error",
+ )
+ gle = frappe.qb.DocType("GL Entry")
+ query = (
+ frappe.qb.from_(gle)
+ .select("voucher_no", "voucher_type", "against", "party")
+ .where((gle.is_cancelled == 0))
+ )
+
+ if filters.get("from_date"):
+ query = query.where(gle.posting_date >= filters.get("from_date"))
+ if filters.get("to_date"):
+ query = query.where(gle.posting_date <= filters.get("to_date"))
+
+ if bank_accounts:
+ query = query.where(gle.against.notin(bank_accounts))
+
+ if filters.get("party"):
+ party = [filters.get("party")]
+ query = query.where(
+ ((gle.account.isin(tds_accounts) & gle.against.isin(party)))
+ | ((gle.voucher_type == "Journal Entry") & (gle.party == filters.get("party")))
+ | gle.party.isin(party)
+ )
+ else:
+ party = frappe.get_all(filters.get("party_type"), pluck="name")
+ query = query.where(
+ ((gle.account.isin(tds_accounts) & gle.against.isin(party)))
+ | (
+ (gle.voucher_type == "Journal Entry")
+ & ((gle.party_type == filters.get("party_type")) | (gle.party_type == ""))
+ )
+ | gle.party.isin(party)
+ )
+ return query
+
+
def get_journal_entry_party_map(journal_entries):
journal_entry_party_map = {}
for d in frappe.db.get_all(
@@ -335,6 +379,8 @@
"base_tax_withholding_net_total",
"grand_total",
"base_total",
+ "bill_no",
+ "bill_date",
],
"Sales Invoice": ["base_net_total", "grand_total", "base_total"],
"Payment Entry": [
@@ -353,7 +399,13 @@
for entry in entries:
tax_category_map.update({entry.name: entry.tax_withholding_category})
if doctype == "Purchase Invoice":
- value = [entry.base_tax_withholding_net_total, entry.grand_total, entry.base_total]
+ value = [
+ entry.base_tax_withholding_net_total,
+ entry.grand_total,
+ entry.base_total,
+ entry.bill_no,
+ entry.bill_date,
+ ]
elif doctype == "Sales Invoice":
value = [entry.base_net_total, entry.grand_total, entry.base_total]
elif doctype == "Payment Entry":
diff --git a/erpnext/buying/doctype/supplier/supplier.json b/erpnext/buying/doctype/supplier/supplier.json
index f37db5f..60dd54c 100644
--- a/erpnext/buying/doctype/supplier/supplier.json
+++ b/erpnext/buying/doctype/supplier/supplier.json
@@ -174,7 +174,7 @@
"fieldname": "supplier_type",
"fieldtype": "Select",
"label": "Supplier Type",
- "options": "Company\nIndividual",
+ "options": "Company\nIndividual\nProprietorship\nPartnership",
"reqd": 1
},
{
@@ -485,7 +485,7 @@
"link_fieldname": "party"
}
],
- "modified": "2023-09-25 12:48:21.869563",
+ "modified": "2023-10-19 16:55:15.148325",
"modified_by": "Administrator",
"module": "Buying",
"name": "Supplier",
diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py
index 73a248f..d09001c 100644
--- a/erpnext/controllers/status_updater.py
+++ b/erpnext/controllers/status_updater.py
@@ -47,15 +47,15 @@
],
[
"To Bill",
- "eval:(self.per_delivered == 100 or self.skip_delivery_note) and self.per_billed < 100 and self.docstatus == 1",
+ "eval:(self.per_delivered >= 100 or self.skip_delivery_note) and self.per_billed < 100 and self.docstatus == 1",
],
[
"To Deliver",
- "eval:self.per_delivered < 100 and self.per_billed == 100 and self.docstatus == 1 and not self.skip_delivery_note",
+ "eval:self.per_delivered < 100 and self.per_billed >= 100 and self.docstatus == 1 and not self.skip_delivery_note",
],
[
"Completed",
- "eval:(self.per_delivered == 100 or self.skip_delivery_note) and self.per_billed == 100 and self.docstatus == 1",
+ "eval:(self.per_delivered >= 100 or self.skip_delivery_note) and self.per_billed >= 100 and self.docstatus == 1",
],
["Cancelled", "eval:self.docstatus==2"],
["Closed", "eval:self.status=='Closed' and self.docstatus != 2"],
diff --git a/erpnext/projects/report/billing_summary.py b/erpnext/projects/report/billing_summary.py
deleted file mode 100644
index ac1524a..0000000
--- a/erpnext/projects/report/billing_summary.py
+++ /dev/null
@@ -1,155 +0,0 @@
-# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-import frappe
-from frappe import _
-from frappe.utils import flt, time_diff_in_hours
-
-
-def get_columns():
- return [
- {
- "label": _("Employee ID"),
- "fieldtype": "Link",
- "fieldname": "employee",
- "options": "Employee",
- "width": 300,
- },
- {
- "label": _("Employee Name"),
- "fieldtype": "data",
- "fieldname": "employee_name",
- "hidden": 1,
- "width": 200,
- },
- {
- "label": _("Timesheet"),
- "fieldtype": "Link",
- "fieldname": "timesheet",
- "options": "Timesheet",
- "width": 150,
- },
- {"label": _("Working Hours"), "fieldtype": "Float", "fieldname": "total_hours", "width": 150},
- {
- "label": _("Billable Hours"),
- "fieldtype": "Float",
- "fieldname": "total_billable_hours",
- "width": 150,
- },
- {"label": _("Billing Amount"), "fieldtype": "Currency", "fieldname": "amount", "width": 150},
- ]
-
-
-def get_data(filters):
- data = []
- if filters.from_date > filters.to_date:
- frappe.msgprint(_("From Date can not be greater than To Date"))
- return data
-
- timesheets = get_timesheets(filters)
-
- filters.from_date = frappe.utils.get_datetime(filters.from_date)
- filters.to_date = frappe.utils.add_to_date(
- frappe.utils.get_datetime(filters.to_date), days=1, seconds=-1
- )
-
- timesheet_details = get_timesheet_details(filters, timesheets.keys())
-
- for ts, ts_details in timesheet_details.items():
- total_hours = 0
- total_billing_hours = 0
- total_amount = 0
-
- for row in ts_details:
- from_time, to_time = filters.from_date, filters.to_date
-
- if row.to_time < from_time or row.from_time > to_time:
- continue
-
- if row.from_time > from_time:
- from_time = row.from_time
-
- if row.to_time < to_time:
- to_time = row.to_time
-
- activity_duration, billing_duration = get_billable_and_total_duration(row, from_time, to_time)
-
- total_hours += activity_duration
- total_billing_hours += billing_duration
- total_amount += billing_duration * flt(row.billing_rate)
-
- if total_hours:
- data.append(
- {
- "employee": timesheets.get(ts).employee,
- "employee_name": timesheets.get(ts).employee_name,
- "timesheet": ts,
- "total_billable_hours": total_billing_hours,
- "total_hours": total_hours,
- "amount": total_amount,
- }
- )
-
- return data
-
-
-def get_timesheets(filters):
- record_filters = [
- ["start_date", "<=", filters.to_date],
- ["end_date", ">=", filters.from_date],
- ]
- if not filters.get("include_draft_timesheets"):
- record_filters.append(["docstatus", "=", 1])
- else:
- record_filters.append(["docstatus", "!=", 2])
- if "employee" in filters:
- record_filters.append(["employee", "=", filters.employee])
-
- timesheets = frappe.get_all(
- "Timesheet", filters=record_filters, fields=["employee", "employee_name", "name"]
- )
- timesheet_map = frappe._dict()
- for d in timesheets:
- timesheet_map.setdefault(d.name, d)
-
- return timesheet_map
-
-
-def get_timesheet_details(filters, timesheet_list):
- timesheet_details_filter = {"parent": ["in", timesheet_list]}
-
- if "project" in filters:
- timesheet_details_filter["project"] = filters.project
-
- timesheet_details = frappe.get_all(
- "Timesheet Detail",
- filters=timesheet_details_filter,
- fields=[
- "from_time",
- "to_time",
- "hours",
- "is_billable",
- "billing_hours",
- "billing_rate",
- "parent",
- ],
- )
-
- timesheet_details_map = frappe._dict()
- for d in timesheet_details:
- timesheet_details_map.setdefault(d.parent, []).append(d)
-
- return timesheet_details_map
-
-
-def get_billable_and_total_duration(activity, start_time, end_time):
- precision = frappe.get_precision("Timesheet Detail", "hours")
- activity_duration = time_diff_in_hours(end_time, start_time)
- billing_duration = 0.0
- if activity.is_billable:
- billing_duration = activity.billing_hours
- if activity_duration != activity.billing_hours:
- billing_duration = activity_duration * activity.billing_hours / activity.hours
-
- return flt(activity_duration, precision), flt(billing_duration, precision)
diff --git a/erpnext/projects/report/employee_billing_summary/__init__.py b/erpnext/projects/report/employee_billing_summary/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/projects/report/employee_billing_summary/__init__.py
+++ /dev/null
diff --git a/erpnext/projects/report/employee_billing_summary/employee_billing_summary.js b/erpnext/projects/report/employee_billing_summary/employee_billing_summary.js
deleted file mode 100644
index 2c25465..0000000
--- a/erpnext/projects/report/employee_billing_summary/employee_billing_summary.js
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-
-frappe.query_reports["Employee Billing Summary"] = {
- "filters": [
- {
- fieldname: "employee",
- label: __("Employee"),
- fieldtype: "Link",
- options: "Employee",
- reqd: 1
- },
- {
- fieldname:"from_date",
- label: __("From Date"),
- fieldtype: "Date",
- default: frappe.datetime.add_months(frappe.datetime.month_start(), -1),
- reqd: 1
- },
- {
- fieldname:"to_date",
- label: __("To Date"),
- fieldtype: "Date",
- default: frappe.datetime.add_days(frappe.datetime.month_start(), -1),
- reqd: 1
- },
- {
- fieldname:"include_draft_timesheets",
- label: __("Include Timesheets in Draft Status"),
- fieldtype: "Check",
- },
- ]
-}
diff --git a/erpnext/projects/report/employee_billing_summary/employee_billing_summary.py b/erpnext/projects/report/employee_billing_summary/employee_billing_summary.py
deleted file mode 100644
index a2f7378..0000000
--- a/erpnext/projects/report/employee_billing_summary/employee_billing_summary.py
+++ /dev/null
@@ -1,15 +0,0 @@
-# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-import frappe
-
-from erpnext.projects.report.billing_summary import get_columns, get_data
-
-
-def execute(filters=None):
- filters = frappe._dict(filters or {})
- columns = get_columns()
-
- data = get_data(filters)
- return columns, data
diff --git a/erpnext/projects/report/project_billing_summary/project_billing_summary.js b/erpnext/projects/report/project_billing_summary/project_billing_summary.js
deleted file mode 100644
index fce0c68..0000000
--- a/erpnext/projects/report/project_billing_summary/project_billing_summary.js
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-
-frappe.query_reports["Project Billing Summary"] = {
- "filters": [
- {
- fieldname: "project",
- label: __("Project"),
- fieldtype: "Link",
- options: "Project",
- reqd: 1
- },
- {
- fieldname:"from_date",
- label: __("From Date"),
- fieldtype: "Date",
- default: frappe.datetime.add_months(frappe.datetime.month_start(), -1),
- reqd: 1
- },
- {
- fieldname:"to_date",
- label: __("To Date"),
- fieldtype: "Date",
- default: frappe.datetime.add_days(frappe.datetime.month_start(),-1),
- reqd: 1
- },
- {
- fieldname:"include_draft_timesheets",
- label: __("Include Timesheets in Draft Status"),
- fieldtype: "Check",
- },
- ]
-}
diff --git a/erpnext/projects/report/project_billing_summary/project_billing_summary.json b/erpnext/projects/report/project_billing_summary/project_billing_summary.json
deleted file mode 100644
index 817d0cd..0000000
--- a/erpnext/projects/report/project_billing_summary/project_billing_summary.json
+++ /dev/null
@@ -1,36 +0,0 @@
-{
- "add_total_row": 1,
- "creation": "2019-03-11 16:22:39.460524",
- "disable_prepared_report": 0,
- "disabled": 0,
- "docstatus": 0,
- "doctype": "Report",
- "idx": 0,
- "is_standard": "Yes",
- "modified": "2019-06-13 15:54:55.255947",
- "modified_by": "Administrator",
- "module": "Projects",
- "name": "Project Billing Summary",
- "owner": "Administrator",
- "prepared_report": 0,
- "ref_doctype": "Timesheet",
- "report_name": "Project Billing Summary",
- "report_type": "Script Report",
- "roles": [
- {
- "role": "Projects User"
- },
- {
- "role": "HR User"
- },
- {
- "role": "Manufacturing User"
- },
- {
- "role": "Employee"
- },
- {
- "role": "Accounts User"
- }
- ]
-}
\ No newline at end of file
diff --git a/erpnext/projects/report/project_billing_summary/project_billing_summary.py b/erpnext/projects/report/project_billing_summary/project_billing_summary.py
deleted file mode 100644
index a2f7378..0000000
--- a/erpnext/projects/report/project_billing_summary/project_billing_summary.py
+++ /dev/null
@@ -1,15 +0,0 @@
-# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-import frappe
-
-from erpnext.projects.report.billing_summary import get_columns, get_data
-
-
-def execute(filters=None):
- filters = frappe._dict(filters or {})
- columns = get_columns()
-
- data = get_data(filters)
- return columns, data
diff --git a/erpnext/projects/report/project_billing_summary/__init__.py b/erpnext/projects/report/timesheet_billing_summary/__init__.py
similarity index 100%
rename from erpnext/projects/report/project_billing_summary/__init__.py
rename to erpnext/projects/report/timesheet_billing_summary/__init__.py
diff --git a/erpnext/projects/report/timesheet_billing_summary/timesheet_billing_summary.js b/erpnext/projects/report/timesheet_billing_summary/timesheet_billing_summary.js
new file mode 100644
index 0000000..1efd0c6
--- /dev/null
+++ b/erpnext/projects/report/timesheet_billing_summary/timesheet_billing_summary.js
@@ -0,0 +1,67 @@
+// Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.query_reports["Timesheet Billing Summary"] = {
+ tree: true,
+ initial_depth: 0,
+ filters: [
+ {
+ fieldname: "employee",
+ label: __("Employee"),
+ fieldtype: "Link",
+ options: "Employee",
+ on_change: function (report) {
+ unset_group_by(report, "employee");
+ },
+ },
+ {
+ fieldname: "project",
+ label: __("Project"),
+ fieldtype: "Link",
+ options: "Project",
+ on_change: function (report) {
+ unset_group_by(report, "project");
+ },
+ },
+ {
+ fieldname: "from_date",
+ label: __("From Date"),
+ fieldtype: "Date",
+ default: frappe.datetime.add_months(
+ frappe.datetime.month_start(),
+ -1
+ ),
+ },
+ {
+ fieldname: "to_date",
+ label: __("To Date"),
+ fieldtype: "Date",
+ default: frappe.datetime.add_days(
+ frappe.datetime.month_start(),
+ -1
+ ),
+ },
+ { // NOTE: `update_group_by_options` expects this filter to be the fifth in the list
+ fieldname: "group_by",
+ label: __("Group By"),
+ fieldtype: "Select",
+ options: [
+ "",
+ { value: "employee", label: __("Employee") },
+ { value: "project", label: __("Project") },
+ { value: "date", label: __("Start Date") },
+ ],
+ },
+ {
+ fieldname: "include_draft_timesheets",
+ label: __("Include Timesheets in Draft Status"),
+ fieldtype: "Check",
+ },
+ ],
+};
+
+function unset_group_by(report, fieldname) {
+ if (report.get_filter_value(fieldname) && report.get_filter_value("group_by") == fieldname) {
+ report.set_filter_value("group_by", "");
+ }
+}
diff --git a/erpnext/projects/report/employee_billing_summary/employee_billing_summary.json b/erpnext/projects/report/timesheet_billing_summary/timesheet_billing_summary.json
similarity index 61%
rename from erpnext/projects/report/employee_billing_summary/employee_billing_summary.json
rename to erpnext/projects/report/timesheet_billing_summary/timesheet_billing_summary.json
index e5626a0..0f070cb 100644
--- a/erpnext/projects/report/employee_billing_summary/employee_billing_summary.json
+++ b/erpnext/projects/report/timesheet_billing_summary/timesheet_billing_summary.json
@@ -1,36 +1,42 @@
{
"add_total_row": 1,
- "creation": "2019-03-08 15:08:19.929728",
- "disable_prepared_report": 0,
+ "columns": [],
+ "creation": "2023-10-10 23:53:43.692067",
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
+ "filters": [],
"idx": 0,
"is_standard": "Yes",
- "modified": "2019-06-13 15:54:49.213973",
+ "letter_head": "ALYF GmbH",
+ "letterhead": null,
+ "modified": "2023-10-11 00:58:30.639078",
"modified_by": "Administrator",
"module": "Projects",
- "name": "Employee Billing Summary",
+ "name": "Timesheet Billing Summary",
"owner": "Administrator",
"prepared_report": 0,
"ref_doctype": "Timesheet",
- "report_name": "Employee Billing Summary",
+ "report_name": "Timesheet Billing Summary",
"report_type": "Script Report",
"roles": [
{
"role": "Projects User"
},
{
- "role": "HR User"
+ "role": "Employee"
+ },
+ {
+ "role": "Accounts User"
},
{
"role": "Manufacturing User"
},
{
- "role": "Employee"
+ "role": "HR User"
},
{
- "role": "Accounts User"
+ "role": "Employee Self Service"
}
]
}
\ No newline at end of file
diff --git a/erpnext/projects/report/timesheet_billing_summary/timesheet_billing_summary.py b/erpnext/projects/report/timesheet_billing_summary/timesheet_billing_summary.py
new file mode 100644
index 0000000..a6e7150
--- /dev/null
+++ b/erpnext/projects/report/timesheet_billing_summary/timesheet_billing_summary.py
@@ -0,0 +1,146 @@
+import frappe
+from frappe import _
+from frappe.model.docstatus import DocStatus
+
+
+def execute(filters=None):
+ group_fieldname = filters.pop("group_by", None)
+
+ filters = frappe._dict(filters or {})
+ columns = get_columns(filters, group_fieldname)
+
+ data = get_data(filters, group_fieldname)
+ return columns, data
+
+
+def get_columns(filters, group_fieldname=None):
+ group_columns = {
+ "date": {
+ "label": _("Date"),
+ "fieldtype": "Date",
+ "fieldname": "date",
+ "width": 150,
+ },
+ "project": {
+ "label": _("Project"),
+ "fieldtype": "Link",
+ "fieldname": "project",
+ "options": "Project",
+ "width": 200,
+ "hidden": int(bool(filters.get("project"))),
+ },
+ "employee": {
+ "label": _("Employee ID"),
+ "fieldtype": "Link",
+ "fieldname": "employee",
+ "options": "Employee",
+ "width": 200,
+ "hidden": int(bool(filters.get("employee"))),
+ },
+ }
+ columns = []
+ if group_fieldname:
+ columns.append(group_columns.get(group_fieldname))
+ columns.extend(
+ column for column in group_columns.values() if column.get("fieldname") != group_fieldname
+ )
+ else:
+ columns.extend(group_columns.values())
+
+ columns.extend(
+ [
+ {
+ "label": _("Employee Name"),
+ "fieldtype": "data",
+ "fieldname": "employee_name",
+ "hidden": 1,
+ },
+ {
+ "label": _("Timesheet"),
+ "fieldtype": "Link",
+ "fieldname": "timesheet",
+ "options": "Timesheet",
+ "width": 150,
+ },
+ {"label": _("Working Hours"), "fieldtype": "Float", "fieldname": "hours", "width": 150},
+ {
+ "label": _("Billing Hours"),
+ "fieldtype": "Float",
+ "fieldname": "billing_hours",
+ "width": 150,
+ },
+ {
+ "label": _("Billing Amount"),
+ "fieldtype": "Currency",
+ "fieldname": "billing_amount",
+ "width": 150,
+ },
+ ]
+ )
+
+ return columns
+
+
+def get_data(filters, group_fieldname=None):
+ _filters = []
+ if filters.get("employee"):
+ _filters.append(("employee", "=", filters.get("employee")))
+ if filters.get("project"):
+ _filters.append(("Timesheet Detail", "project", "=", filters.get("project")))
+ if filters.get("from_date"):
+ _filters.append(("Timesheet Detail", "from_time", ">=", filters.get("from_date")))
+ if filters.get("to_date"):
+ _filters.append(("Timesheet Detail", "to_time", "<=", filters.get("to_date")))
+ if not filters.get("include_draft_timesheets"):
+ _filters.append(("docstatus", "=", DocStatus.submitted()))
+ else:
+ _filters.append(("docstatus", "in", (DocStatus.submitted(), DocStatus.draft())))
+
+ data = frappe.get_list(
+ "Timesheet",
+ fields=[
+ "name as timesheet",
+ "`tabTimesheet`.employee",
+ "`tabTimesheet`.employee_name",
+ "`tabTimesheet Detail`.from_time as date",
+ "`tabTimesheet Detail`.project",
+ "`tabTimesheet Detail`.hours",
+ "`tabTimesheet Detail`.billing_hours",
+ "`tabTimesheet Detail`.billing_amount",
+ ],
+ filters=_filters,
+ order_by="`tabTimesheet Detail`.from_time",
+ )
+
+ return group_by(data, group_fieldname) if group_fieldname else data
+
+
+def group_by(data, fieldname):
+ groups = {row.get(fieldname) for row in data}
+ grouped_data = []
+ for group in sorted(groups):
+ group_row = {
+ fieldname: group,
+ "hours": sum(row.get("hours") for row in data if row.get(fieldname) == group),
+ "billing_hours": sum(row.get("billing_hours") for row in data if row.get(fieldname) == group),
+ "billing_amount": sum(row.get("billing_amount") for row in data if row.get(fieldname) == group),
+ "indent": 0,
+ "is_group": 1,
+ }
+ if fieldname == "employee":
+ group_row["employee_name"] = next(
+ row.get("employee_name") for row in data if row.get(fieldname) == group
+ )
+
+ grouped_data.append(group_row)
+ for row in data:
+ if row.get(fieldname) != group:
+ continue
+
+ _row = row.copy()
+ _row[fieldname] = None
+ _row["indent"] = 1
+ _row["is_group"] = 0
+ grouped_data.append(_row)
+
+ return grouped_data
diff --git a/erpnext/projects/workspace/projects/projects.json b/erpnext/projects/workspace/projects/projects.json
index 94ae9c0..e6bead9 100644
--- a/erpnext/projects/workspace/projects/projects.json
+++ b/erpnext/projects/workspace/projects/projects.json
@@ -155,9 +155,9 @@
"dependencies": "Project",
"hidden": 0,
"is_query_report": 1,
- "label": "Project Billing Summary",
+ "label": "Timesheet Billing Summary",
"link_count": 0,
- "link_to": "Project Billing Summary",
+ "link_to": "Timesheet Billing Summary",
"link_type": "Report",
"onboard": 0,
"type": "Link"
@@ -192,7 +192,7 @@
"type": "Link"
}
],
- "modified": "2023-07-04 14:39:08.935853",
+ "modified": "2023-10-10 23:54:33.082108",
"modified_by": "Administrator",
"module": "Projects",
"name": "Projects",
@@ -234,8 +234,8 @@
"type": "DocType"
},
{
- "label": "Project Billing Summary",
- "link_to": "Project Billing Summary",
+ "label": "Timesheet Billing Summary",
+ "link_to": "Timesheet Billing Summary",
"type": "Report"
},
{
diff --git a/erpnext/selling/doctype/customer/customer.json b/erpnext/selling/doctype/customer/customer.json
index 40cab9f..3b97123 100644
--- a/erpnext/selling/doctype/customer/customer.json
+++ b/erpnext/selling/doctype/customer/customer.json
@@ -134,7 +134,7 @@
"label": "Customer Type",
"oldfieldname": "customer_type",
"oldfieldtype": "Select",
- "options": "Company\nIndividual",
+ "options": "Company\nIndividual\nProprietorship\nPartnership",
"reqd": 1
},
{
@@ -584,7 +584,7 @@
"link_fieldname": "party"
}
],
- "modified": "2023-09-21 12:23:20.706020",
+ "modified": "2023-10-19 16:56:27.327035",
"modified_by": "Administrator",
"module": "Selling",
"name": "Customer",
diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py
index a40cde1..e4f1a28 100755
--- a/erpnext/selling/doctype/sales_order/sales_order.py
+++ b/erpnext/selling/doctype/sales_order/sales_order.py
@@ -217,7 +217,15 @@
def validate_with_previous_doc(self):
super(SalesOrder, self).validate_with_previous_doc(
- {"Quotation": {"ref_dn_field": "prevdoc_docname", "compare_fields": [["company", "="]]}}
+ {
+ "Quotation": {"ref_dn_field": "prevdoc_docname", "compare_fields": [["company", "="]]},
+ "Quotation Item": {
+ "ref_dn_field": "quotation_item",
+ "compare_fields": [["item_code", "="], ["uom", "="], ["conversion_factor", "="]],
+ "is_child_table": True,
+ "allow_duplicate_prev_row_id": True,
+ },
+ }
)
if cint(frappe.db.get_single_value("Selling Settings", "maintain_same_sales_rate")):
diff --git a/erpnext/stock/doctype/bin/bin.json b/erpnext/stock/doctype/bin/bin.json
index a115727..02684a7 100644
--- a/erpnext/stock/doctype/bin/bin.json
+++ b/erpnext/stock/doctype/bin/bin.json
@@ -5,20 +5,23 @@
"doctype": "DocType",
"engine": "InnoDB",
"field_order": [
- "warehouse",
"item_code",
- "reserved_qty",
+ "column_break_yreo",
+ "warehouse",
+ "section_break_stag",
"actual_qty",
- "ordered_qty",
- "indented_qty",
"planned_qty",
- "projected_qty",
+ "indented_qty",
+ "ordered_qty",
+ "column_break_xn5j",
+ "reserved_qty",
"reserved_qty_for_production",
"reserved_qty_for_sub_contract",
"reserved_qty_for_production_plan",
- "ma_rate",
+ "projected_qty",
+ "section_break_pmrs",
"stock_uom",
- "fcfs_rate",
+ "column_break_0slj",
"valuation_rate",
"stock_value"
],
@@ -56,7 +59,7 @@
"fieldname": "reserved_qty",
"fieldtype": "Float",
"in_list_view": 1,
- "label": "Reserved Quantity",
+ "label": "Reserved Qty",
"oldfieldname": "reserved_qty",
"oldfieldtype": "Currency",
"read_only": 1
@@ -67,7 +70,7 @@
"fieldtype": "Float",
"in_filter": 1,
"in_list_view": 1,
- "label": "Actual Quantity",
+ "label": "Actual Qty",
"oldfieldname": "actual_qty",
"oldfieldtype": "Currency",
"read_only": 1
@@ -77,7 +80,7 @@
"fieldname": "ordered_qty",
"fieldtype": "Float",
"in_list_view": 1,
- "label": "Ordered Quantity",
+ "label": "Ordered Qty",
"oldfieldname": "ordered_qty",
"oldfieldtype": "Currency",
"read_only": 1
@@ -86,7 +89,7 @@
"default": "0.00",
"fieldname": "indented_qty",
"fieldtype": "Float",
- "label": "Requested Quantity",
+ "label": "Requested Qty",
"oldfieldname": "indented_qty",
"oldfieldtype": "Currency",
"read_only": 1
@@ -116,21 +119,10 @@
{
"fieldname": "reserved_qty_for_sub_contract",
"fieldtype": "Float",
- "label": "Reserved Qty for sub contract",
+ "label": "Reserved Qty for Subcontract",
"read_only": 1
},
{
- "fieldname": "ma_rate",
- "fieldtype": "Float",
- "hidden": 1,
- "label": "Moving Average Rate",
- "oldfieldname": "ma_rate",
- "oldfieldtype": "Currency",
- "print_hide": 1,
- "read_only": 1,
- "report_hide": 1
- },
- {
"fieldname": "stock_uom",
"fieldtype": "Link",
"in_filter": 1,
@@ -141,17 +133,6 @@
"read_only": 1
},
{
- "fieldname": "fcfs_rate",
- "fieldtype": "Float",
- "hidden": 1,
- "label": "FCFS Rate",
- "oldfieldname": "fcfs_rate",
- "oldfieldtype": "Currency",
- "print_hide": 1,
- "read_only": 1,
- "report_hide": 1
- },
- {
"fieldname": "valuation_rate",
"fieldtype": "Float",
"label": "Valuation Rate",
@@ -172,13 +153,33 @@
"fieldtype": "Float",
"label": "Reserved Qty for Production Plan",
"read_only": 1
+ },
+ {
+ "fieldname": "section_break_stag",
+ "fieldtype": "Section Break"
+ },
+ {
+ "fieldname": "column_break_yreo",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "column_break_xn5j",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "section_break_pmrs",
+ "fieldtype": "Section Break"
+ },
+ {
+ "fieldname": "column_break_0slj",
+ "fieldtype": "Column Break"
}
],
"hide_toolbar": 1,
"idx": 1,
"in_create": 1,
"links": [],
- "modified": "2023-05-02 23:26:21.806965",
+ "modified": "2023-11-01 15:35:51.722534",
"modified_by": "Administrator",
"module": "Stock",
"name": "Bin",
diff --git a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py
index 7f0dc2d..8bbc660 100644
--- a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py
+++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py
@@ -205,7 +205,11 @@
)
docs = frappe.db.get_all(
"Asset",
- filters={receipt_document_type: item.receipt_document, "item_code": item.item_code},
+ filters={
+ receipt_document_type: item.receipt_document,
+ "item_code": item.item_code,
+ "docstatus": ["!=", 2],
+ },
fields=["name", "docstatus"],
)
if not docs or len(docs) != item.qty:
diff --git a/erpnext/stock/page/stock_balance/stock_balance.js b/erpnext/stock/page/stock_balance/stock_balance.js
index f00dd3e..90b8d45 100644
--- a/erpnext/stock/page/stock_balance/stock_balance.js
+++ b/erpnext/stock/page/stock_balance/stock_balance.js
@@ -11,6 +11,7 @@
label: __('Warehouse'),
fieldtype:'Link',
options:'Warehouse',
+ default: frappe.route_options && frappe.route_options.warehouse,
change: function() {
page.item_dashboard.start = 0;
page.item_dashboard.refresh();
@@ -22,6 +23,7 @@
label: __('Item'),
fieldtype:'Link',
options:'Item',
+ default: frappe.route_options && frappe.route_options.item_code,
change: function() {
page.item_dashboard.start = 0;
page.item_dashboard.refresh();
@@ -33,6 +35,7 @@
label: __('Item Group'),
fieldtype:'Link',
options:'Item Group',
+ default: frappe.route_options && frappe.route_options.item_group,
change: function() {
page.item_dashboard.start = 0;
page.item_dashboard.refresh();
diff --git a/erpnext/translations/de.csv b/erpnext/translations/de.csv
index c8fc2cf..c627f81 100644
--- a/erpnext/translations/de.csv
+++ b/erpnext/translations/de.csv
@@ -3340,7 +3340,7 @@
Cannot Optimize Route as Driver Address is Missing.,"Route kann nicht optimiert werden, da die Fahreradresse fehlt.",
Cannot complete task {0} as its dependant task {1} are not ccompleted / cancelled.,"Aufgabe {0} kann nicht abgeschlossen werden, da die abhängige Aufgabe {1} nicht abgeschlossen / abgebrochen wurde.",
Cannot find a matching Item. Please select some other value for {0}.,Ein passender Artikel kann nicht gefunden werden. Bitte einen anderen Wert für {0} auswählen.,
-"Cannot overbill for Item {0} in row {1} more than {2}. To allow over-billing, please set allowance in Accounts Settings","Artikel {0} in Zeile {1} kann nicht mehr als {2} in Rechnung gestellt werden. Um eine Überberechnung zuzulassen, legen Sie die Überberechnung in den Kontoeinstellungen fest",
+"Cannot overbill for Item {0} in row {1} more than {2}. To allow over-billing, please set allowance in Accounts Settings","Für Artikel {0} in Zeile {1} kann nicht mehr als {2} zusätzlich in Rechnung gestellt werden. Um diese Überfakturierung zuzulassen, passen Sie bitte die Grenzwerte in den Buchhaltungseinstellungen an.",
"Capacity Planning Error, planned start time can not be same as end time","Kapazitätsplanungsfehler, die geplante Startzeit darf nicht mit der Endzeit übereinstimmen",
Categories,Kategorien,
Changes in {0},Änderungen in {0},
@@ -3746,7 +3746,7 @@
This page keeps track of your items in which buyers have showed some interest.,"Diese Seite verfolgt Ihre Artikel, an denen Käufer Interesse gezeigt haben.",
Thursday,Donnerstag,
Title,Bezeichnung,
-"To allow over billing, update ""Over Billing Allowance"" in Accounts Settings or the Item.","Aktualisieren Sie "Over Billing Allowance" in den Kontoeinstellungen oder im Artikel, um eine Überberechnung zuzulassen.",
+"To allow over billing, update ""Over Billing Allowance"" in Accounts Settings or the Item.","Aktualisieren Sie "Over Billing Allowance" in den Buchhaltungseinstellungen oder im Artikel, um eine Überberechnung zuzulassen.",
"To allow over receipt / delivery, update ""Over Receipt/Delivery Allowance"" in Stock Settings or the Item.","Um eine Überbestätigung / Überlieferung zu ermöglichen, aktualisieren Sie "Überbestätigung / Überlieferung" in den Lagereinstellungen oder im Artikel.",
Total,Summe,
Total Payment Request amount cannot be greater than {0} amount,Der Gesamtbetrag der Zahlungsanforderung darf nicht größer als {0} sein,
@@ -4113,8 +4113,8 @@
Accounting Period,Abrechnungszeitraum,
Period Name,Zeitraumname,
Closed Documents,Geschlossene Dokumente,
-Accounts Settings,Konteneinstellungen,
-Settings for Accounts,Konteneinstellungen,
+Accounts Settings,Buchhaltungseinstellungen,
+Settings for Accounts,Einstellungen für die Buchhaltung,
Make Accounting Entry For Every Stock Movement,Eine Buchung für jede Lagerbewegung erstellen,
Users with this role are allowed to set frozen accounts and create / modify accounting entries against frozen accounts,Benutzer mit dieser Rolle sind berechtigt Konten zu sperren und Buchungen zu gesperrten Konten zu erstellen/verändern,
Determine Address Tax Category From,Adresssteuerkategorie bestimmen von,
@@ -7617,7 +7617,7 @@
Journal Entry Type,Buchungssatz-Typ,
Journal Entry Template Account,Buchungssatzvorlagenkonto,
Process Deferred Accounting,Aufgeschobene Buchhaltung verarbeiten,
-Manual entry cannot be created! Disable automatic entry for deferred accounting in accounts settings and try again,Manuelle Eingabe kann nicht erstellt werden! Deaktivieren Sie die automatische Eingabe für die verzögerte Buchhaltung in den Konteneinstellungen und versuchen Sie es erneut,
+Manual entry cannot be created! Disable automatic entry for deferred accounting in accounts settings and try again,Manuelle Eingabe kann nicht erstellt werden! Deaktivieren Sie die automatische Eingabe für die verzögerte Buchhaltung in den Buchhaltungseinstellungen und versuchen Sie es erneut,
End date cannot be before start date,Das Enddatum darf nicht vor dem Startdatum liegen,
Total Counts Targeted,Gesamtzahl der anvisierten Zählungen,
Total Counts Completed,Gesamtzahl der abgeschlossenen Zählungen,
@@ -8826,5 +8826,32 @@
Is Mandatory,Ist obligatorisch,
WhatsApp,WhatsApp,
Make a call,Einen Anruf tätigen,
+Enable Automatic Party Matching,Automatisches Zuordnen von Parteien aktivieren,
+Auto match and set the Party in Bank Transactions,"Partei automatisch anhand der Kontonummer bzw. IBAN zuordnen",
+Enable Fuzzy Matching,Fuzzy Matching aktivieren,
+Approximately match the description/party name against parties,"Partei automatisch anhand grober Übereinstimmung des Namens zuordnen"
+Accounts Closing,Kontenabschluss,
+Period Closing Settings,Periodenabschlusseinstellungen,
+Ignore Account Closing Balance,Saldo des Kontos zum Periodenabschluss ignorieren,
+Financial reports will be generated using GL Entry doctypes (should be enabled if Period Closing Voucher is not posted for all years sequentially or missing),"Finanzberichte werden anhand des Hauptbuchs erstellt (sollte aktiviert sein, wenn Periodenabschlüsse fehlen oder nicht sequentiell für alle Jahre gebucht werden)",
+Asset Settings,Vermögenswerteinstellungen,
+POS Setting,POS-Einstellungen,
+Create Ledger Entries for Change Amount,Buchungssätze für Wechselgeld erstellen,
+"If enabled, ledger entries will be posted for change amount in POS transactions","Wenn aktiviert, werden Buchungssätze für Wechselgeld in POS-Transaktionen erstellt",
+Credit Limit Settings,Kreditlimit-Einstellungen,
+Role Allowed to Over Bill,
+Users with this role are allowed to over bill above the allowance percentage,"Roll, die mehr als den erlaubten Prozentsatz zusätzlich abrechnen darf",
+Role allowed to bypass Credit Limit,"Rolle, die das Kreditlimit umgehen darf",
+Invoice Cancellation,Rechnungsstornierung,
+Delete Accounting and Stock Ledger Entries on deletion of Transaction,Beim Löschen einer Transaktion auch die entsprechenden Buchungs- und Lagerbuchungssätze löschen,
+Enable Common Party Accounting,Verknüpfung von Kunden und Liefeanten erlauben,
+Allow multi-currency invoices against single party account,Rechnungsbeträge in Fremdwährungen dürfen umgerechnet und in der Hauptwährung gebucht werden,
+Enabling this will allow creation of multi-currency invoices against single party account in company currency,Bei Aktivierung können Rechnungen in Fremdwährungen gegen ein Konto in der Hauptwährung gebucht werden,
+Payment Terms from orders will be fetched into the invoices as is,Zahlungsbedingungen aus Aufträgen werden eins zu eins in Rechnungen übernommen,
+Automatically Fetch Payment Terms from Order,Zahlungsbedingungen aus Auftrag in die Rechnung übernehmen,
+Enable Custom Cash Flow Format,Individuelles Cashflow-Format aktivieren,
+Tax Settings,Umsatzsteuer-Einstellungen,
+Book Tax Loss on Early Payment Discount,Umsatzsteueranteil bei Skonto berücksichtigen,
+Split Early Payment Discount Loss into Income and Tax Loss,"Skontobetrag in Aufwand und Umsatzsteuerkorrektur aufteilen",
Approve,Genehmigen,
Reject,Ablehnen,
diff --git a/erpnext/translations/zh.csv b/erpnext/translations/zh.csv
index 1d8a261..cf89dc6 100644
--- a/erpnext/translations/zh.csv
+++ b/erpnext/translations/zh.csv
@@ -8743,3 +8743,4 @@
Make a call,打个电话,
Approve,同意,
Reject,拒绝,
+Stock,库存,