diff --git a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py index e8b19b402495..41207ee8d37e 100644 --- a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py +++ b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py @@ -188,9 +188,6 @@ def get_tax_accounts(item_list, columns, company_currency, invoice_item_row = {} itemised_tax = {} - tax_amount_precision = get_field_precision(frappe.get_meta(tax_doctype).get_field("tax_amount"), - currency=company_currency) or 2 - for d in item_list: invoice_item_row.setdefault(d.parent, []).append(d) item_row_map.setdefault(d.parent, {}).setdefault(d.item_code or d.item_name, []).append(d) @@ -243,9 +240,8 @@ def get_tax_accounts(item_list, columns, company_currency, item_tax_amount = flt((tax_amount * d.base_net_amount) / item_net_amount) \ if item_net_amount else 0 if item_tax_amount: - tax_value = flt(item_tax_amount, tax_amount_precision) - tax_value = (tax_value * -1 - if (doctype == 'Purchase Invoice' and name in deducted_tax) else tax_value) + tax_value = (item_tax_amount * -1 + if (doctype == 'Purchase Invoice' and name in deducted_tax) else item_tax_amount) itemised_tax.setdefault(d.name, {})[description] = frappe._dict({ "tax_rate": tax_rate, @@ -258,8 +254,7 @@ def get_tax_accounts(item_list, columns, company_currency, for d in invoice_item_row.get(parent, []): itemised_tax.setdefault(d.name, {})[description] = frappe._dict({ "tax_rate": "NA", - "tax_amount": flt((tax_amount * d.base_net_amount) / d.base_net_total, - tax_amount_precision) + "tax_amount": flt((tax_amount * d.base_net_amount) / d.base_net_total) }) tax_columns.sort() diff --git a/erpnext/buying/report/purchase_details/__init__.py b/erpnext/buying/report/purchase_details/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/erpnext/buying/report/purchase_details/purchase_details.js b/erpnext/buying/report/purchase_details/purchase_details.js new file mode 100644 index 000000000000..172f9859ae25 --- /dev/null +++ b/erpnext/buying/report/purchase_details/purchase_details.js @@ -0,0 +1,112 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.query_reports["Purchase Details"] = { + "filters": [ + { + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company"), + reqd: 1 + }, + { + fieldname: "doctype", + label: __("Based On"), + fieldtype: "Select", + options: ["Purchase Order","Purchase Receipt","Purchase Invoice"], + default: "Purchase Invoice", + reqd: 1 + }, + { + fieldname: "qty_field", + label: __("Quantity Type"), + fieldtype: "Select", + options: ["Stock Qty", "Contents Qty", "Transaction Qty"], + default: "Stock Qty", + reqd: 1 + }, + { + fieldname: "from_date", + label: __("From Date"), + fieldtype: "Date", + default: frappe.datetime.add_months(frappe.datetime.get_today(), -1), + reqd: 1 + }, + { + fieldname: "to_date", + label: __("To Date"), + fieldtype: "Date", + default: frappe.datetime.get_today(), + reqd: 1 + }, + { + fieldname: "supplier", + label: __("Supplier"), + fieldtype: "Link", + options: "Supplier" + }, + { + fieldname: "supplier_group", + label: __("Supplier Group"), + fieldtype: "Link", + options: "Supplier Group" + }, + { + fieldname: "item_code", + label: __("Item"), + fieldtype: "Link", + options: "Item" + }, + { + fieldname: "item_group", + label: __("Item Group"), + fieldtype: "Link", + options: "Item Group" + }, + { + fieldname: "brand", + label: __("Brand"), + fieldtype: "Link", + options: "Brand" + }, + { + fieldname: "group_by_1", + label: __("Group By Level 1"), + fieldtype: "Select", + options: ["Ungrouped", "Group by Supplier", "Group by Supplier Group", "Group by Transaction", + "Group by Item", "Group by Item Group", "Group by Brand"], + default: "Ungrouped" + }, + { + fieldname: "group_by_2", + label: __("Group By Level 2"), + fieldtype: "Select", + options: ["Ungrouped", "Group by Supplier", "Group by Supplier Group", "Group by Transaction", + "Group by Item", "Group by Item Group", "Group by Brand"], + default: "Group by Supplier" + }, + { + fieldname: "group_by_3", + label: __("Group By Level 3"), + fieldtype: "Select", + options: ["Ungrouped", "Group by Supplier", "Group by Supplier Group", "Group by Transaction", + "Group by Item", "Group by Item Group", "Group by Brand"], + default: "Group by Transaction" + }, + { + fieldname: "group_same_items", + label: __("Group Same Items"), + fieldtype: "Check", + default: 1 + }, + { + fieldname: "include_taxes", + label: __("Include Taxes"), + fieldtype: "Check" + }, + ], + "initial_depth": 1 +} diff --git a/erpnext/buying/report/purchase_details/purchase_details.json b/erpnext/buying/report/purchase_details/purchase_details.json new file mode 100644 index 000000000000..612edebcdc83 --- /dev/null +++ b/erpnext/buying/report/purchase_details/purchase_details.json @@ -0,0 +1,27 @@ +{ + "add_total_row": 0, + "creation": "2019-03-25 15:25:54.566163", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "modified": "2019-03-25 15:25:54.566163", + "modified_by": "Administrator", + "module": "Buying", + "name": "Purchase Details", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Purchase Order", + "report_name": "Purchase Details", + "report_type": "Script Report", + "roles": [ + { + "role": "Purchase User" + }, + { + "role": "Accounts User" + } + ] +} diff --git a/erpnext/buying/report/purchase_details/purchase_details.py b/erpnext/buying/report/purchase_details/purchase_details.py new file mode 100644 index 000000000000..4079050cdb1c --- /dev/null +++ b/erpnext/buying/report/purchase_details/purchase_details.py @@ -0,0 +1,8 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +from erpnext.selling.report.sales_details.sales_details import SalesPurchaseDetailsReport + +def execute(filters=None): + return SalesPurchaseDetailsReport(filters).run("Supplier") diff --git a/erpnext/config/buying.py b/erpnext/config/buying.py index ba3492330eb5..127aeba801c6 100644 --- a/erpnext/config/buying.py +++ b/erpnext/config/buying.py @@ -164,6 +164,12 @@ def get_data(): "reference_doctype": "Material Request", "onboard": 1, }, + { + "type": "report", + "is_query_report": True, + "name": "Purchase Details", + "doctype": "Purchase Order" + }, ] }, { diff --git a/erpnext/config/selling.py b/erpnext/config/selling.py index f18aadb9ef14..d26e506d4bc5 100644 --- a/erpnext/config/selling.py +++ b/erpnext/config/selling.py @@ -252,6 +252,12 @@ def get_data(): "name": "Sales Order Trends", "doctype": "Sales Order" }, + { + "type": "report", + "is_query_report": True, + "name": "Sales Details", + "doctype": "Sales Order" + }, ] }, { diff --git a/erpnext/selling/report/sales_details/__init__.py b/erpnext/selling/report/sales_details/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/erpnext/selling/report/sales_details/sales_details.js b/erpnext/selling/report/sales_details/sales_details.js new file mode 100644 index 000000000000..ce63aae23499 --- /dev/null +++ b/erpnext/selling/report/sales_details/sales_details.js @@ -0,0 +1,126 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.query_reports["Sales Details"] = { + "filters": [ + { + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company"), + reqd: 1 + }, + { + fieldname: "doctype", + label: __("Based On"), + fieldtype: "Select", + options: ["Sales Order","Delivery Note","Sales Invoice"], + default: "Sales Invoice", + reqd: 1 + }, + { + fieldname: "qty_field", + label: __("Quantity Type"), + fieldtype: "Select", + options: ["Stock Qty", "Contents Qty", "Transaction Qty"], + default: "Stock Qty", + reqd: 1 + }, + { + fieldname: "from_date", + label: __("From Date"), + fieldtype: "Date", + default: frappe.datetime.add_months(frappe.datetime.get_today(), -1), + reqd: 1 + }, + { + fieldname: "to_date", + label: __("To Date"), + fieldtype: "Date", + default: frappe.datetime.get_today(), + reqd: 1 + }, + { + fieldname: "customer", + label: __("Customer"), + fieldtype: "Link", + options: "Customer" + }, + { + fieldname: "customer_group", + label: __("Customer Group"), + fieldtype: "Link", + options: "Customer Group" + }, + { + fieldname: "item_code", + label: __("Item"), + fieldtype: "Link", + options: "Item" + }, + { + fieldname: "item_group", + label: __("Item Group"), + fieldtype: "Link", + options: "Item Group" + }, + { + fieldname: "brand", + label: __("Brand"), + fieldtype: "Link", + options: "Brand" + }, + { + fieldname: "territory", + label: __("Territory"), + fieldtype: "Link", + options: "Territory" + }, + { + fieldname: "sales_person", + label: __("Sales Person"), + fieldtype: "Link", + options: "Sales Person" + }, + { + fieldname: "group_by_1", + label: __("Group By Level 1"), + fieldtype: "Select", + options: ["Ungrouped", "Group by Customer", "Group by Customer Group", "Group by Transaction", + "Group by Item", "Group by Item Group", "Group by Brand", "Group by Territory", "Group by Sales Person"], + default: "Ungrouped" + }, + { + fieldname: "group_by_2", + label: __("Group By Level 2"), + fieldtype: "Select", + options: ["Ungrouped", "Group by Customer", "Group by Customer Group", "Group by Transaction", + "Group by Item", "Group by Item Group", "Group by Brand", "Group by Territory", "Group by Sales Person"], + default: "Group by Customer" + }, + { + fieldname: "group_by_3", + label: __("Group By Level 3"), + fieldtype: "Select", + options: ["Ungrouped", "Group by Customer", "Group by Customer Group", "Group by Transaction", + "Group by Item", "Group by Item Group", "Group by Brand", "Group by Territory", "Group by Sales Person"], + default: "Group by Transaction" + }, + { + fieldname: "group_same_items", + label: __("Group Same Items"), + fieldtype: "Check", + default: 1 + }, + { + fieldname: "include_taxes", + label: __("Include Taxes"), + fieldtype: "Check" + }, + ], + "initial_depth": 1 +} + + diff --git a/erpnext/selling/report/sales_details/sales_details.json b/erpnext/selling/report/sales_details/sales_details.json new file mode 100644 index 000000000000..19cf25ca158c --- /dev/null +++ b/erpnext/selling/report/sales_details/sales_details.json @@ -0,0 +1,27 @@ +{ + "add_total_row": 0, + "creation": "2019-03-24 16:27:15.822005", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "modified": "2019-03-24 16:27:15.822005", + "modified_by": "Administrator", + "module": "Selling", + "name": "Sales Details", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Sales Order", + "report_name": "Sales Details", + "report_type": "Script Report", + "roles": [ + { + "role": "Accounts User" + }, + { + "role": "Sales User" + } + ] +} diff --git a/erpnext/selling/report/sales_details/sales_details.py b/erpnext/selling/report/sales_details/sales_details.py new file mode 100644 index 000000000000..bc5b50e0fdfa --- /dev/null +++ b/erpnext/selling/report/sales_details/sales_details.py @@ -0,0 +1,530 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe import _, scrub, unscrub +from frappe.utils import getdate, nowdate, flt, cint, cstr +from erpnext.accounts.report.item_wise_sales_register.item_wise_sales_register import get_tax_accounts +from frappe.desk.query_report import group_report_data +from six import iteritems + +class SalesPurchaseDetailsReport(object): + def __init__(self, filters=None): + self.filters = frappe._dict(filters or {}) + self.filters.from_date = getdate(self.filters.from_date or nowdate()) + self.filters.to_date = getdate(self.filters.to_date or nowdate()) + + self.additional_customer_info = frappe._dict() + + self.date_field = 'transaction_date' \ + if self.filters.doctype in ['Sales Order', 'Purchase Order'] else 'posting_date' + + if not self.filters.get("company"): + self.filters["company"] = frappe.db.get_single_value('Global Defaults', 'default_company') + self.company_currency = frappe.get_cached_value('Company', self.filters.get("company"), "default_currency") + + def run(self, party_type): + if self.filters.from_date > self.filters.to_date: + frappe.throw(_("From Date must be before To Date")) + + self.filters.party_type = party_type + + self.get_entries() + self.get_itemised_taxes() + self.prepare_data() + data = self.get_grouped_data() + columns = self.get_columns() + return columns, data + + def get_entries(self): + party_field = scrub(self.filters.party_type) + party_name_field = party_field + "_name" + qty_field = self.get_qty_fieldname() + + sales_person_table = ", `tabSales Team` sp" if self.filters.party_type == "Customer" else "" + sales_person_condition = "and sp.parent = s.name and sp.parenttype = %(doctype)s" if self.filters.party_type == "Customer" else "" + sales_person_field = ", GROUP_CONCAT(DISTINCT sp.sales_person SEPARATOR ', ') as sales_person" if self.filters.party_type == "Customer" else "" + + supplier_table = ", `tabSupplier` sup" if self.filters.party_type == "Supplier" else "" + supplier_condition = "and sup.name = s.supplier" if self.filters.party_type == "Supplier" else "" + + territory_field = ", s.territory" if self.filters.party_type == "Customer" else "" + + party_group_field = ", s.customer_group as party_group, 'Customer Group' as party_group_dt" if self.filters.party_type == "Customer"\ + else ", sup.supplier_group as party_group, 'Supplier Group' as party_group_dt" + + is_opening_condition = "and s.is_opening != 'Yes'" if self.filters.doctype in ['Sales Invoice', 'Purchase Invoice']\ + else "" + + filter_conditions = self.get_conditions() + + self.entries = frappe.db.sql(""" + select + s.name as parent, i.name, s.{date_field} as date, + s.{party_field} as party, s.{party_name_field} as party_name, + i.item_code, i.item_name, + i.{qty_field} as qty, + i.uom, i.stock_uom, i.alt_uom, + i.base_net_amount, i.base_amount, + i.brand, i.item_group + {party_group_field} {sales_person_field} {territory_field} + from + `tab{doctype} Item` i, `tab{doctype}` s {sales_person_table} {supplier_table} + where i.parent = s.name and s.docstatus = 1 and s.company = %(company)s + and s.{date_field} between %(from_date)s and %(to_date)s + {sales_person_condition} {supplier_condition} {is_opening_condition} {filter_conditions} + group by s.name, i.name + order by s.{date_field}, s.{party_field}, s.name, i.item_code + """.format( + party_field=party_field, + party_name_field=party_name_field, + party_group_field=party_group_field, + territory_field=territory_field, + qty_field=qty_field, + date_field=self.date_field, + doctype=self.filters.doctype, + sales_person_field=sales_person_field, + sales_person_table=sales_person_table, + sales_person_condition=sales_person_condition, + supplier_table=supplier_table, + supplier_condition=supplier_condition, + is_opening_condition=is_opening_condition, + filter_conditions=filter_conditions + ), self.filters, as_dict=1) + + if self.filters.party_type == "Customer" and "Group by Customer" in [self.filters.group_by_1, self.filters.group_by_2, self.filters.group_by_3]: + additional_customer_info = frappe.db.sql(""" + select + s.customer, GROUP_CONCAT(DISTINCT s.territory SEPARATOR ', ') as territory + {sales_person_field} + from + `tab{doctype} Item` i, `tab{doctype}` s {sales_person_table} + where i.parent = s.name and s.docstatus = 1 and s.company = %(company)s + and sp.parent = s.name and sp.parenttype = %(doctype)s + and s.{date_field} between %(from_date)s and %(to_date)s + {sales_person_condition} {is_opening_condition} {filter_conditions} + group by s.{party_field} + """.format( + party_field=party_field, + date_field=self.date_field, + doctype=self.filters.doctype, + sales_person_field=sales_person_field, + sales_person_table=sales_person_table, + sales_person_condition=sales_person_condition, + supplier_table=supplier_table, + supplier_condition=supplier_condition, + is_opening_condition=is_opening_condition, + filter_conditions=filter_conditions + ), self.filters, as_dict=1) + + for d in additional_customer_info: + self.additional_customer_info[d.customer] = d + + def get_itemised_taxes(self): + if self.entries: + self.itemised_tax, self.tax_columns = get_tax_accounts(self.entries, [], self.company_currency, self.filters.doctype, + "Sales Taxes and Charges" if self.filters.party_type == "Customer" else "Purchase Taxes and Charges") + self.tax_amount_fields = ["tax_" + scrub(tax) for tax in self.tax_columns] + self.tax_rate_fields = ["tax_" + scrub(tax) + "_rate" for tax in self.tax_columns] + else: + self.itemised_tax, self.tax_columns = {}, [] + self.tax_amount_fields, self.tax_rate_fields = [], [] + + def prepare_data(self): + for d in self.entries: + # Set UOM based on qty field + if self.filters.qty_field == "Transaction Qty": + d.uom = d.uom + elif self.filters.qty_field == "Contents Qty": + d.uom = d.alt_uom or d.stock_uom + else: + d.uom = d.stock_uom + + # Add additional fields + d.update({ + "doc_type": "Item", + "reference": d.item_code, + "voucher_no": d.parent, + "group_doctype": "Item Group", + "group": d.item_group, + "brand": d.brand, + scrub(self.filters.party_type) + "_name": d.party_name, + }) + + if "Group by Item" in [self.filters.group_by_1, self.filters.group_by_2, self.filters.group_by_3]: + d['doc_type'] = self.filters.doctype + d['reference'] = d.get("voucher_no") + + # Add tax fields + for f, tax in zip(self.tax_amount_fields, self.tax_columns): + tax_amount = self.itemised_tax.get(d.name, {}).get(tax, {}).get("tax_amount", 0.0) + d[f] = flt(tax_amount) + for f, tax in zip(self.tax_rate_fields, self.tax_columns): + tax_rate = self.itemised_tax.get(d.name, {}).get(tax, {}).get("tax_rate", 0.0) + d[f] = flt(tax_rate) + + self.postprocess_row(d) + + def get_grouped_data(self): + data = self.entries + + self.group_by = [None] + for i in range(3): + group_label = self.filters.get("group_by_" + str(i + 1), "").replace("Group by ", "") + + if not group_label or group_label == "Ungrouped": + continue + if group_label in ['Customer', 'Supplier']: + group_field = "party" + elif group_label == "Transaction": + group_field = "voucher_no" + elif group_label == "Item": + group_field = "item_code" + elif group_label in ["Customer Group", "Supplier Group"]: + group_field = "party_group" + else: + group_field = scrub(group_label) + + self.group_by.append(group_field) + + # Group same items + if cint(self.filters.get("group_same_items")): + data = group_report_data(data, ("item_code", "uom", "voucher_no"), calculate_totals=self.calculate_group_totals, + totals_only=True) + + if len(self.group_by) <= 1: + return data + + return group_report_data(data, self.group_by, calculate_totals=self.calculate_group_totals) + + def calculate_group_totals(self, data, group_field, group_value, grouped_by): + total_fields = ['qty', 'base_net_amount', 'base_amount'] + total_fields += self.tax_amount_fields + averageif_fields = self.tax_rate_fields + + totals = {} + + # Copy grouped by into total row + for f, g in iteritems(grouped_by): + totals[f] = g + + # Set zeros + for f in total_fields + averageif_fields + [f + "_count" for f in averageif_fields]: + totals[f] = 0 + + # Add totals + for d in data: + for f in total_fields: + totals[f] += flt(d[f]) + + for f in averageif_fields: + if flt(d[f]): + totals[f] += flt(d[f]) + totals[f + "_count"] += 1 + + # Set group values + if data: + if group_field == ("item_code", "uom", "voucher_no"): + for f, v in iteritems(data[0]): + if f not in totals: + totals[f] = v + + if 'voucher_no' in grouped_by: + fields_to_copy = ['date', 'sales_person', 'territory'] + for f in fields_to_copy: + if f in data[0]: + totals[f] = data[0][f] + totals['date'] = data[0].get('date') + + if 'item_code' in grouped_by: + totals['group_doctype'] = "Item Group" + totals['group'] = data[0].get('item_group') + + if group_field == 'party': + totals['group_doctype'] = data[0].get("party_group_dt") + totals['group'] = data[0].get("party_group") + + if self.filters.party_type == "Customer": + details = self.additional_customer_info.get(group_value, frappe._dict()) + totals.update({ + "sales_person": grouped_by.get("sales_person") or details.sales_person, + "territory": grouped_by.get("territory") or details.territory + }) + + # Set reference field + group_reference_doctypes = { + "party": self.filters.party_type, + "voucher_no": self.filters.doctype, + "item_code": "Item", + } + + if group_field == ("item_code", "uom", "voucher_no") and data: + totals['doc_type'] = data[0].get('doc_type') + totals['reference'] = data[0].get('reference') + else: + reference_field = group_field[0] if isinstance(group_field, (list, tuple)) else group_field + reference_dt = group_reference_doctypes.get(reference_field, unscrub(cstr(reference_field))) + totals['doc_type'] = reference_dt + totals['reference'] = grouped_by.get(reference_field) if group_field else "'Total'" + + if not group_field and self.group_by == [None]: + totals['voucher_no'] = "'Total'" + + self.postprocess_row(totals) + return totals + + def postprocess_row(self, row): + # Calculate rate + rate_fields = [ + ('base_net_rate', 'base_net_amount'), + ('base_rate', 'base_amount') + ] + if flt(row['qty']): + for target, source in rate_fields: + row[target] = flt(row[source]) / flt(row['qty']) + + # Calculate total taxes and grand total + row["total_tax_amount"] = 0.0 + for f in self.tax_amount_fields: + row["total_tax_amount"] += row[f] + + row["grand_total"] = row["base_net_amount"] + row["total_tax_amount"] + + # Calculate tax rates by averaging + for f in self.tax_rate_fields: + row[f] = row.get(f, 0.0) + if flt(row.get(f + "_count")): + row[f] /= flt(row.get(f + "_count")) + + if f + "_count" in row: + del row[f + "_count"] + + def get_qty_fieldname(self): + filter_to_field = { + "Stock Qty": "stock_qty", + "Contents Qty": "alt_uom_qty", + "Transaction Qty": "qty" + } + return filter_to_field.get(self.filters.qty_field, "stock_qty") + + def get_conditions(self): + conditions = [] + + if self.filters.get("customer"): + conditions.append("s.customer=%(customer)s") + + if self.filters.get("customer_group"): + lft, rgt = frappe.db.get_value("Customer Group", self.filters.customer_group, ["lft", "rgt"]) + conditions.append("""s.customer_group in (select name from `tabCustomer Group` + where lft>=%s and rgt<=%s and docstatus<2)""" % (lft, rgt)) + + if self.filters.get("supplier"): + conditions.append("s.supplier=%(supplier)s") + + if self.filters.get("supplier_group"): + lft, rgt = frappe.db.get_value("Supplier Group", self.filters.supplier_group, ["lft", "rgt"]) + conditions.append("""sup.supplier_group in (select name from `tabSupplier Group` + where lft>=%s and rgt<=%s and docstatus<2)""" % (lft, rgt)) + + if self.filters.get("item_code"): + conditions.append("i.item_code=%(item_code)s") + + if self.filters.get("item_group"): + lft, rgt = frappe.db.get_value("Item Group", self.filters.item_group, ["lft", "rgt"]) + conditions.append("""i.item_group in (select name from `tabItem Group` + where lft>=%s and rgt<=%s and docstatus<2)""" % (lft, rgt)) + + if self.filters.get("brand"): + conditions.append("i.brand=%(brand)s") + + if self.filters.get("territory"): + lft, rgt = frappe.db.get_value("Territory", self.filters.territory, ["lft", "rgt"]) + conditions.append("""s.territory in (select name from `tabTerritory` + where lft>=%s and rgt<=%s and docstatus<2)""" % (lft, rgt)) + + if self.filters.get("sales_person"): + lft, rgt = frappe.db.get_value("Sales Person", self.filters.sales_person, ["lft", "rgt"]) + conditions.append("""sp.sales_person in (select name from `tabSales Person` + where lft>=%s and rgt<=%s and docstatus<2)""" % (lft, rgt)) + + return "and {}".format(" and ".join(conditions)) if conditions else "" + + def get_columns(self): + if len(self.group_by) > 1: + columns = [ + { + "label": _("Reference"), + "fieldtype": "Dynamic Link", + "fieldname": "reference", + "options": "doc_type", + "width": 300 + }, + { + "label": _("Type"), + "fieldtype": "Data", + "fieldname": "doc_type", + "width": 110 + }, + ] + + group_list = [self.filters.group_by_1, self.filters.group_by_2, self.filters.group_by_3] + if "Group by Transaction" not in group_list and "Group by Item" not in group_list: + columns.append({ + "label": _(self.filters.doctype), + "fieldtype": "Link", + "fieldname": "voucher_no", + "options": self.filters.doctype, + "width": 140 + }) + + if "Group by Customer" not in group_list and "Group by Supplier" not in group_list: + columns.append({ + "label": _(self.filters.party_type), + "fieldtype": "Link", + "fieldname": "party", + "options": self.filters.party_type, + "width": 150 + }) + + columns += [ + { + "label": _("Date"), + "fieldtype": "Date", + "fieldname": "date", + "width": 80 + }, + ] + else: + columns = [ + { + "label": _("Date"), + "fieldtype": "Date", + "fieldname": "date", + "width": 80 + }, + { + "label": _(self.filters.doctype), + "fieldtype": "Link", + "fieldname": "voucher_no", + "options": self.filters.doctype, + "width": 140 + }, + { + "label": _(self.filters.party_type), + "fieldtype": "Link", + "fieldname": "party", + "options": self.filters.party_type, + "width": 150 + }, + { + "label": _("Item"), + "fieldtype": "Link", + "fieldname": "item_code", + "options": "Item", + "width": 150 + }, + ] + + columns += [ + { + "label": _("UOM"), + "fieldtype": "Link", + "options": "UOM", + "fieldname": "uom", + "width": 50 + }, + { + "label": _("Qty"), + "fieldtype": "Float", + "fieldname": "qty", + "width": 80 + }, + { + "label": _("Net Rate"), + "fieldtype": "Currency", + "fieldname": "base_net_rate", + "options": "Company:company:default_currency", + "width": 120 + }, + { + "label": _("Net Amount"), + "fieldtype": "Currency", + "fieldname": "base_net_amount", + "options": "Company:company:default_currency", + "width": 120 + }, + { + "label": _("Taxes and Charges"), + "fieldtype": "Currency", + "fieldname": "total_tax_amount", + "options": "Company:company:default_currency", + "width": 120 + }, + { + "label": _("Grand Total"), + "fieldtype": "Currency", + "fieldname": "grand_total", + "options": "Company:company:default_currency", + "width": 120 + }, + ] + + if self.filters.include_taxes: + for tax_description in self.tax_columns: + amount_field = "tax_" + scrub(tax_description) + rate_field = amount_field + "_rate" + columns += [ + { + "label": _(tax_description) + " (%)", + "fieldtype": "Percent", + "fieldname": rate_field, + "width": 60 + }, + { + "label": _(tax_description), + "fieldtype": "Currency", + "fieldname": amount_field, + "options": "Company:company:default_currency", + "width": 120 + }, + ] + + if self.filters.party_type == "Customer": + columns += [ + { + "label": _("Sales Person"), + "fieldtype": "Data", + "fieldname": "sales_person", + "width": 150 + }, + { + "label": _("Territory"), + "fieldtype": "Link", + "fieldname": "territory", + "options": "Territory", + "width": 100 + }, + ] + + columns += [ + { + "label": _("Group"), + "fieldtype": "Dynamic Link", + "fieldname": "group", + "options": "group_doctype", + "width": 100 + }, + { + "label": _("Brand"), + "fieldtype": "Link", + "fieldname": "brand", + "options": "Brand", + "width": 100 + }, + ] + + return columns + +def execute(filters=None): + return SalesPurchaseDetailsReport(filters).run("Customer")