From 19d5074c25646de3b0239eeadf56b50bb3eca667 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Mon, 30 Nov 2020 15:49:00 +0530 Subject: [PATCH 01/14] fix: get formatted value in 'taxes' print template --- erpnext/templates/print_formats/includes/taxes.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/templates/print_formats/includes/taxes.html b/erpnext/templates/print_formats/includes/taxes.html index 6e984f3901..304e845287 100644 --- a/erpnext/templates/print_formats/includes/taxes.html +++ b/erpnext/templates/print_formats/includes/taxes.html @@ -20,10 +20,10 @@ {%- if (charge.tax_amount or doc.flags.print_taxes_with_zero_amount) and (not charge.included_in_print_rate or doc.flags.show_inclusive_tax_in_print) -%}
-
+ +
- {{ frappe.format_value(frappe.utils.flt(charge.tax_amount), - table_meta.get_field("tax_amount"), doc, currency=doc.currency) }} + {{ charge.get_formatted('tax_amount', doc) }}
{%- endif -%} From 9abc685504cc14a949fe922d343ff42d5f0f9a2a Mon Sep 17 00:00:00 2001 From: Kenneth Sequeira Date: Wed, 2 Dec 2020 21:13:50 +0530 Subject: [PATCH 02/14] feat: add jinja templating in contract template --- erpnext/crm/doctype/contract/contract.js | 32 +++++++++++-------- erpnext/crm/doctype/contract/contract.json | 4 ++- .../contract_template/contract_template.py | 23 +++++++++++++ 3 files changed, 44 insertions(+), 15 deletions(-) diff --git a/erpnext/crm/doctype/contract/contract.js b/erpnext/crm/doctype/contract/contract.js index ee9e895130..6c0d739c89 100644 --- a/erpnext/crm/doctype/contract/contract.js +++ b/erpnext/crm/doctype/contract/contract.js @@ -1,23 +1,27 @@ // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -cur_frm.add_fetch("contract_template", "contract_terms", "contract_terms"); -cur_frm.add_fetch("contract_template", "requires_fulfilment", "requires_fulfilment"); - -// Add fulfilment terms from contract template into contract frappe.ui.form.on("Contract", { contract_template: function (frm) { - // Populate the fulfilment terms table from a contract template, if any if (frm.doc.contract_template) { - frappe.model.with_doc("Contract Template", frm.doc.contract_template, function () { - var tabletransfer = frappe.model.get_doc("Contract Template", frm.doc.contract_template); - - frm.doc.fulfilment_terms = []; - $.each(tabletransfer.fulfilment_terms, function (index, row) { - var d = frm.add_child("fulfilment_terms"); - d.requirement = row.requirement; - frm.refresh_field("fulfilment_terms"); - }); + frappe.call({ + method: 'erpnext.crm.doctype.contract_template.contract_template.get_contract_template', + args: { + template_name: frm.doc.contract_template, + doc: frm.doc + }, + callback: function(r) { + if(r && r.message){ + frm.set_value("contract_terms", r.message.contract_terms); + + // Populate the fulfilment terms table from a contract template, if any + r.message.contract_template.fulfilment_terms.forEach(element => { + let d = frm.add_child("fulfilment_terms"); + d.requirement = element.requirement; + }); + frm.refresh_field("fulfilment_terms"); + } + } }); } } diff --git a/erpnext/crm/doctype/contract/contract.json b/erpnext/crm/doctype/contract/contract.json index 0026e4a02e..fbc9f1c8d3 100755 --- a/erpnext/crm/doctype/contract/contract.json +++ b/erpnext/crm/doctype/contract/contract.json @@ -1,4 +1,5 @@ { + "actions": [], "allow_import": 1, "allow_rename": 1, "creation": "2018-04-12 06:32:04.582486", @@ -175,6 +176,7 @@ }, { "default": "0", + "fetch_from": "contract_template.requires_fulfilment", "fieldname": "requires_fulfilment", "fieldtype": "Check", "label": "Requires Fulfilment" @@ -247,7 +249,7 @@ ], "is_submittable": 1, "links": [], - "modified": "2020-03-30 06:56:07.257932", + "modified": "2020-12-02 21:12:44.118155", "modified_by": "Administrator", "module": "CRM", "name": "Contract", diff --git a/erpnext/crm/doctype/contract_template/contract_template.py b/erpnext/crm/doctype/contract_template/contract_template.py index 601ee9a28b..48ab8aad47 100644 --- a/erpnext/crm/doctype/contract_template/contract_template.py +++ b/erpnext/crm/doctype/contract_template/contract_template.py @@ -5,6 +5,29 @@ from __future__ import unicode_literals import frappe from frappe.model.document import Document +from frappe.utils.jinja import validate_template +from six import string_types +import json class ContractTemplate(Document): pass + + def validate(self): + if self.contract_terms: + validate_template(self.contract_terms) + +@frappe.whitelist() +def get_contract_template(template_name, doc): + if isinstance(doc, string_types): + doc = json.loads(doc) + + contract_template = frappe.get_doc("Contract Template", template_name) + contract_terms = None + + if contract_template.contract_terms: + contract_terms = frappe.render_template(contract_template.contract_terms, doc) + + return { + 'contract_template': contract_template, + 'contract_terms': contract_terms + } \ No newline at end of file From 131e46bab79c6f275fd0358ffbb0510a2bc2eef4 Mon Sep 17 00:00:00 2001 From: Kenneth Sequeira Date: Wed, 2 Dec 2020 21:37:55 +0530 Subject: [PATCH 03/14] chore: add help section for Jinja --- .../contract_template/contract_template.json | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/erpnext/crm/doctype/contract_template/contract_template.json b/erpnext/crm/doctype/contract_template/contract_template.json index 5e4582f8d3..9fc24798cd 100644 --- a/erpnext/crm/doctype/contract_template/contract_template.json +++ b/erpnext/crm/doctype/contract_template/contract_template.json @@ -11,7 +11,9 @@ "contract_terms", "sb_fulfilment", "requires_fulfilment", - "fulfilment_terms" + "fulfilment_terms", + "section_break_6", + "contract_template_help" ], "fields": [ { @@ -41,10 +43,20 @@ "fieldtype": "Table", "label": "Fulfilment Terms and Conditions", "options": "Contract Template Fulfilment Terms" + }, + { + "fieldname": "section_break_6", + "fieldtype": "Section Break" + }, + { + "fieldname": "contract_template_help", + "fieldtype": "HTML", + "label": "Contract Template Help", + "options": "

Contract Template Example

\n\n
Contract for Customer {{ party_name }}\n\n-Valid From : {{ start_date }} \n-Valid To : {{ end_date }}\n
\n\n

How to get fieldnames

\n\n

The fieldnames you can use in your email template are the fields in the document from which you are sending the email. You can find out the fields of any documents via Setup > Customize Form View and selecting the document type (e.g. Contract)

\n\n

Templating

\n\n

Templates are compiled using the Jinja Templating Language. To learn more about Jinja, read this documentation.

" } ], "links": [], - "modified": "2020-11-11 17:49:44.879363", + "modified": "2020-12-02 21:36:53.097074", "modified_by": "Administrator", "module": "CRM", "name": "Contract Template", From c4eb41c92b399bbb9f4ae005ef98522b30eaa227 Mon Sep 17 00:00:00 2001 From: Kenneth Sequeira Date: Fri, 4 Dec 2020 15:00:22 +0530 Subject: [PATCH 04/14] fix; styling for sider --- erpnext/crm/doctype/contract/contract.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/crm/doctype/contract/contract.js b/erpnext/crm/doctype/contract/contract.js index 6c0d739c89..b02d06a601 100644 --- a/erpnext/crm/doctype/contract/contract.js +++ b/erpnext/crm/doctype/contract/contract.js @@ -11,7 +11,7 @@ frappe.ui.form.on("Contract", { doc: frm.doc }, callback: function(r) { - if(r && r.message){ + if (r && r.message) { frm.set_value("contract_terms", r.message.contract_terms); // Populate the fulfilment terms table from a contract template, if any From 5f95ba57a8b66e3d4f493c05e4342cf205fdded4 Mon Sep 17 00:00:00 2001 From: Kenneth Sequeira Date: Mon, 7 Dec 2020 10:45:13 +0530 Subject: [PATCH 05/14] fix: py code clean up and doc help --- erpnext/crm/doctype/contract_template/contract_template.json | 4 ++-- erpnext/crm/doctype/contract_template/contract_template.py | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/erpnext/crm/doctype/contract_template/contract_template.json b/erpnext/crm/doctype/contract_template/contract_template.json index 9fc24798cd..7cc5ec13cf 100644 --- a/erpnext/crm/doctype/contract_template/contract_template.json +++ b/erpnext/crm/doctype/contract_template/contract_template.json @@ -52,11 +52,11 @@ "fieldname": "contract_template_help", "fieldtype": "HTML", "label": "Contract Template Help", - "options": "

Contract Template Example

\n\n
Contract for Customer {{ party_name }}\n\n-Valid From : {{ start_date }} \n-Valid To : {{ end_date }}\n
\n\n

How to get fieldnames

\n\n

The fieldnames you can use in your email template are the fields in the document from which you are sending the email. You can find out the fields of any documents via Setup > Customize Form View and selecting the document type (e.g. Contract)

\n\n

Templating

\n\n

Templates are compiled using the Jinja Templating Language. To learn more about Jinja, read this documentation.

" + "options": "

Contract Template Example

\n\n
Contract for Customer {{ party_name }}\n\n-Valid From : {{ start_date }} \n-Valid To : {{ end_date }}\n
\n\n

How to get fieldnames

\n\n

The field names you can use in your Contract Template are the fields in the Contract for which you are creating the template. You can find out the fields of any documents via Setup > Customize Form View and selecting the document type (e.g. Contract)

\n\n

Templating

\n\n

Templates are compiled using the Jinja Templating Language. To learn more about Jinja, read this documentation.

" } ], "links": [], - "modified": "2020-12-02 21:36:53.097074", + "modified": "2020-12-07 10:44:22.587047", "modified_by": "Administrator", "module": "CRM", "name": "Contract Template", diff --git a/erpnext/crm/doctype/contract_template/contract_template.py b/erpnext/crm/doctype/contract_template/contract_template.py index 48ab8aad47..69fd86f7fb 100644 --- a/erpnext/crm/doctype/contract_template/contract_template.py +++ b/erpnext/crm/doctype/contract_template/contract_template.py @@ -10,8 +10,6 @@ from six import string_types import json class ContractTemplate(Document): - pass - def validate(self): if self.contract_terms: validate_template(self.contract_terms) From 85af44b7ed255e799e4869556927357421cb7c3e Mon Sep 17 00:00:00 2001 From: Kenneth Sequeira Date: Mon, 7 Dec 2020 11:18:20 +0530 Subject: [PATCH 06/14] fix: client side UX fixes --- erpnext/crm/doctype/contract/contract.js | 16 ++++++++++------ erpnext/crm/doctype/contract/contract.json | 3 +-- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/erpnext/crm/doctype/contract/contract.js b/erpnext/crm/doctype/contract/contract.js index b02d06a601..9968855163 100644 --- a/erpnext/crm/doctype/contract/contract.js +++ b/erpnext/crm/doctype/contract/contract.js @@ -12,14 +12,18 @@ frappe.ui.form.on("Contract", { }, callback: function(r) { if (r && r.message) { + let contract_template = r.message.contract_template; frm.set_value("contract_terms", r.message.contract_terms); + frm.set_value("requires_fulfilment", contract_template.requires_fulfilment); - // Populate the fulfilment terms table from a contract template, if any - r.message.contract_template.fulfilment_terms.forEach(element => { - let d = frm.add_child("fulfilment_terms"); - d.requirement = element.requirement; - }); - frm.refresh_field("fulfilment_terms"); + if (frm.doc.requires_fulfilment) { + // Populate the fulfilment terms table from a contract template, if any + r.message.contract_template.fulfilment_terms.forEach(element => { + let d = frm.add_child("fulfilment_terms"); + d.requirement = element.requirement; + }); + frm.refresh_field("fulfilment_terms"); + } } } }); diff --git a/erpnext/crm/doctype/contract/contract.json b/erpnext/crm/doctype/contract/contract.json index fbc9f1c8d3..de3230f0e6 100755 --- a/erpnext/crm/doctype/contract/contract.json +++ b/erpnext/crm/doctype/contract/contract.json @@ -176,7 +176,6 @@ }, { "default": "0", - "fetch_from": "contract_template.requires_fulfilment", "fieldname": "requires_fulfilment", "fieldtype": "Check", "label": "Requires Fulfilment" @@ -249,7 +248,7 @@ ], "is_submittable": 1, "links": [], - "modified": "2020-12-02 21:12:44.118155", + "modified": "2020-12-07 11:15:58.385521", "modified_by": "Administrator", "module": "CRM", "name": "Contract", From db0bbfac978b62afb157df51978218a7960d66a2 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 8 Dec 2020 12:20:07 +0530 Subject: [PATCH 07/14] feat(reports): get item details from Item doctype instead of the Transaction --- .../item_wise_purchase_register.py | 21 ++++--- .../item_wise_sales_register.py | 58 ++++++++++--------- .../item_wise_sales_history.py | 32 ++++++---- 3 files changed, 67 insertions(+), 44 deletions(-) diff --git a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py index 3445df7206..a6d6a35688 100644 --- a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py +++ b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py @@ -8,6 +8,7 @@ from frappe.utils import flt from erpnext.accounts.report.item_wise_sales_register.item_wise_sales_register import (get_tax_accounts, get_grand_total, add_total_row, get_display_value, get_group_by_and_display_fields, add_sub_total_row, get_group_by_conditions) +from erpnext.selling.report.item_wise_sales_history.item_wise_sales_history import get_item_details, get_customer_details def execute(filters=None): return _execute(filters) @@ -22,7 +23,7 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum aii_account_map = get_aii_accounts() if item_list: itemised_tax, tax_columns = get_tax_accounts(item_list, columns, company_currency, - doctype="Purchase Invoice", tax_doctype="Purchase Taxes and Charges") + doctype='Purchase Invoice', tax_doctype='Purchase Taxes and Charges') po_pr_map = get_purchase_receipts_against_purchase_order(item_list) @@ -34,10 +35,14 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum if filters.get('group_by'): grand_total = get_grand_total(filters, 'Purchase Invoice') + item_details = get_item_details() + for d in item_list: if not d.stock_qty: continue + item_record = item_details.get(d.item_code) + purchase_receipt = None if d.purchase_receipt: purchase_receipt = d.purchase_receipt @@ -48,8 +53,8 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum row = { 'item_code': d.item_code, - 'item_name': d.item_name, - 'item_group': d.item_group, + 'item_name': item_record.item_name, + 'item_group': item_record.item_group, 'description': d.description, 'invoice': d.parent, 'posting_date': d.posting_date, @@ -81,10 +86,10 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum for tax in tax_columns: item_tax = itemised_tax.get(d.name, {}).get(tax, {}) row.update({ - frappe.scrub(tax + ' Rate'): item_tax.get("tax_rate", 0), - frappe.scrub(tax + ' Amount'): item_tax.get("tax_amount", 0), + frappe.scrub(tax + ' Rate'): item_tax.get('tax_rate', 0), + frappe.scrub(tax + ' Amount'): item_tax.get('tax_amount', 0), }) - total_tax += flt(item_tax.get("tax_amount")) + total_tax += flt(item_tax.get('tax_amount')) row.update({ 'total_tax': total_tax, @@ -309,8 +314,8 @@ def get_items(filters, additional_query_columns): select `tabPurchase Invoice Item`.`name`, `tabPurchase Invoice Item`.`parent`, `tabPurchase Invoice`.posting_date, `tabPurchase Invoice`.credit_to, `tabPurchase Invoice`.company, - `tabPurchase Invoice`.supplier, `tabPurchase Invoice`.remarks, `tabPurchase Invoice`.base_net_total, `tabPurchase Invoice Item`.`item_code`, - `tabPurchase Invoice Item`.`item_name`, `tabPurchase Invoice Item`.`item_group`, `tabPurchase Invoice Item`.description, + `tabPurchase Invoice`.supplier, `tabPurchase Invoice`.remarks, `tabPurchase Invoice`.base_net_total, + `tabPurchase Invoice Item`.`item_code`, `tabPurchase Invoice Item`.description, `tabPurchase Invoice Item`.`project`, `tabPurchase Invoice Item`.`purchase_order`, `tabPurchase Invoice Item`.`purchase_receipt`, `tabPurchase Invoice Item`.`po_detail`, `tabPurchase Invoice Item`.`expense_account`, `tabPurchase Invoice Item`.`stock_qty`, 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 a05dcd75ce..f54ceb0d2f 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 @@ -8,6 +8,7 @@ from frappe.utils import flt, cstr from frappe.model.meta import get_field_precision from frappe.utils.xlsxutils import handle_html from erpnext.accounts.report.sales_register.sales_register import get_mode_of_payments +from erpnext.selling.report.item_wise_sales_history.item_wise_sales_history import get_item_details, get_customer_details def execute(filters=None): return _execute(filters) @@ -16,7 +17,7 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum if not filters: filters = {} columns = get_columns(additional_table_columns, filters) - company_currency = frappe.get_cached_value('Company', filters.get("company"), "default_currency") + company_currency = frappe.get_cached_value('Company', filters.get('company'), 'default_currency') item_list = get_items(filters, additional_query_columns) if item_list: @@ -33,7 +34,13 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum if filters.get('group_by'): grand_total = get_grand_total(filters, 'Sales Invoice') + customer_details = get_customer_details() + item_details = get_item_details() + for d in item_list: + customer_record = customer_details.get(d.customer) + item_record = item_details.get(d.item_code) + delivery_note = None if d.delivery_note: delivery_note = d.delivery_note @@ -45,14 +52,14 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum row = { 'item_code': d.item_code, - 'item_name': d.item_name, - 'item_group': d.item_group, + 'item_name': item_record.item_name, + 'item_group': item_record.item_group, 'description': d.description, 'invoice': d.parent, 'posting_date': d.posting_date, 'customer': d.customer, - 'customer_name': d.customer_name, - 'customer_group': d.customer_group, + 'customer_name': customer_record.customer_name, + 'customer_group': customer_record.customer_group, } if additional_query_columns: @@ -90,10 +97,10 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum for tax in tax_columns: item_tax = itemised_tax.get(d.name, {}).get(tax, {}) row.update({ - frappe.scrub(tax + ' Rate'): item_tax.get("tax_rate", 0), - frappe.scrub(tax + ' Amount'): item_tax.get("tax_amount", 0), + frappe.scrub(tax + ' Rate'): item_tax.get('tax_rate', 0), + frappe.scrub(tax + ' Amount'): item_tax.get('tax_amount', 0), }) - total_tax += flt(item_tax.get("tax_amount")) + total_tax += flt(item_tax.get('tax_amount')) row.update({ 'total_tax': total_tax, @@ -226,7 +233,7 @@ def get_columns(additional_table_columns, filters): if filters.get('group_by') != 'Territory': columns.extend([ { - 'label': _("Territory"), + 'label': _('Territory'), 'fieldname': 'territory', 'fieldtype': 'Link', 'options': 'Territory', @@ -374,13 +381,12 @@ def get_items(filters, additional_query_columns): `tabSales Invoice`.posting_date, `tabSales Invoice`.debit_to, `tabSales Invoice`.project, `tabSales Invoice`.customer, `tabSales Invoice`.remarks, `tabSales Invoice`.territory, `tabSales Invoice`.company, `tabSales Invoice`.base_net_total, - `tabSales Invoice Item`.item_code, `tabSales Invoice Item`.item_name, - `tabSales Invoice Item`.item_group, `tabSales Invoice Item`.description, `tabSales Invoice Item`.sales_order, - `tabSales Invoice Item`.delivery_note, `tabSales Invoice Item`.income_account, - `tabSales Invoice Item`.cost_center, `tabSales Invoice Item`.stock_qty, - `tabSales Invoice Item`.stock_uom, `tabSales Invoice Item`.base_net_rate, - `tabSales Invoice Item`.base_net_amount, `tabSales Invoice`.customer_name, - `tabSales Invoice`.customer_group, `tabSales Invoice Item`.so_detail, + `tabSales Invoice Item`.item_code, `tabSales Invoice Item`.description, + `tabSales Invoice Item`.sales_order, `tabSales Invoice Item`.delivery_note, + `tabSales Invoice Item`.income_account, `tabSales Invoice Item`.cost_center, + `tabSales Invoice Item`.stock_qty, `tabSales Invoice Item`.stock_uom, + `tabSales Invoice Item`.base_net_rate, `tabSales Invoice Item`.base_net_amount, + `tabSales Invoice`.customer_name, `tabSales Invoice`.customer_group, `tabSales Invoice Item`.so_detail, `tabSales Invoice`.update_stock, `tabSales Invoice Item`.uom, `tabSales Invoice Item`.qty {0} from `tabSales Invoice`, `tabSales Invoice Item` where `tabSales Invoice`.name = `tabSales Invoice Item`.parent @@ -417,14 +423,14 @@ def get_deducted_taxes(): return frappe.db.sql_list("select name from `tabPurchase Taxes and Charges` where add_deduct_tax = 'Deduct'") def get_tax_accounts(item_list, columns, company_currency, - doctype="Sales Invoice", tax_doctype="Sales Taxes and Charges"): + doctype='Sales Invoice', tax_doctype='Sales Taxes and Charges'): import json item_row_map = {} tax_columns = [] invoice_item_row = {} itemised_tax = {} - tax_amount_precision = get_field_precision(frappe.get_meta(tax_doctype).get_field("tax_amount"), + 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: @@ -469,8 +475,8 @@ def get_tax_accounts(item_list, columns, company_currency, tax_rate = tax_data tax_amount = 0 - if charge_type == "Actual" and not tax_rate: - tax_rate = "NA" + if charge_type == 'Actual' and not tax_rate: + tax_rate = 'NA' item_net_amount = sum([flt(d.base_net_amount) for d in item_row_map.get(parent, {}).get(item_code, [])]) @@ -484,17 +490,17 @@ def get_tax_accounts(item_list, columns, company_currency, if (doctype == 'Purchase Invoice' and name in deducted_tax) else tax_value) itemised_tax.setdefault(d.name, {})[description] = frappe._dict({ - "tax_rate": tax_rate, - "tax_amount": tax_value + 'tax_rate': tax_rate, + 'tax_amount': tax_value }) except ValueError: continue - elif charge_type == "Actual" and tax_amount: + elif charge_type == 'Actual' and tax_amount: 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_rate': 'NA', + 'tax_amount': flt((tax_amount * d.base_net_amount) / d.base_net_total, tax_amount_precision) }) @@ -563,7 +569,7 @@ def add_total_row(data, filters, prev_group_by_value, item, total_row_map, }) total_row_map.setdefault('total_row', { - subtotal_display_field: "Total", + subtotal_display_field: 'Total', 'stock_qty': 0.0, 'amount': 0.0, 'bold': 1, diff --git a/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py b/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py index c716aa96e0..8473276001 100644 --- a/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py +++ b/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py @@ -10,8 +10,8 @@ from frappe.utils.nestedset import get_descendants_of def execute(filters=None): filters = frappe._dict(filters or {}) if filters.from_date > filters.to_date: - frappe.throw(_('From Date cannot be greater than To Date')) - + frappe.throw(_("From Date cannot be greater than To Date")) + columns = get_columns(filters) data = get_data(filters) @@ -148,14 +148,16 @@ def get_data(filters): company_list.append(filters.get("company")) customer_details = get_customer_details() + item_details = get_item_details() sales_order_records = get_sales_order_details(company_list, filters) for record in sales_order_records: customer_record = customer_details.get(record.customer) + item_record = item_details.get(record.item_code) row = { "item_code": record.item_code, - "item_name": record.item_name, - "item_group": record.item_group, + "item_name": item_record.item_name, + "item_group": item_record.item_group, "description": record.description, "quantity": record.qty, "uom": record.uom, @@ -196,8 +198,8 @@ def get_conditions(filters): return conditions def get_customer_details(): - details = frappe.get_all('Customer', - fields=['name', 'customer_name', "customer_group"]) + details = frappe.get_all("Customer", + fields=["name", "customer_name", "customer_group"]) customer_details = {} for d in details: customer_details.setdefault(d.name, frappe._dict({ @@ -206,15 +208,25 @@ def get_customer_details(): })) return customer_details +def get_item_details(): + details = frappe.db.get_all("Item", + fields=["item_code", "item_name", "item_group"]) + item_details = {} + for d in details: + item_details.setdefault(d.item_code, frappe._dict({ + "item_name": d.item_name, + "item_group": d.item_group + })) + return item_details + def get_sales_order_details(company_list, filters): conditions = get_conditions(filters) return frappe.db.sql(""" SELECT - so_item.item_code, so_item.item_name, so_item.item_group, - so_item.description, so_item.qty, so_item.uom, - so_item.base_rate, so_item.base_amount, so.name, - so.transaction_date, so.customer, so.territory, + so_item.item_code, so_item.description, so_item.qty, + so_item.uom, so_item.base_rate, so_item.base_amount, + so.name, so.transaction_date, so.customer,so.territory, so.project, so_item.delivered_qty, so_item.billed_amt, so.company FROM From fa950662b9dbc58f4c9dade8dd2c3bb9baea280d Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 8 Dec 2020 12:32:19 +0530 Subject: [PATCH 08/14] fix: import statement --- .../item_wise_purchase_register/item_wise_purchase_register.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py index a6d6a35688..a36e7f8581 100644 --- a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py +++ b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py @@ -8,7 +8,7 @@ from frappe.utils import flt from erpnext.accounts.report.item_wise_sales_register.item_wise_sales_register import (get_tax_accounts, get_grand_total, add_total_row, get_display_value, get_group_by_and_display_fields, add_sub_total_row, get_group_by_conditions) -from erpnext.selling.report.item_wise_sales_history.item_wise_sales_history import get_item_details, get_customer_details +from erpnext.selling.report.item_wise_sales_history.item_wise_sales_history import get_item_details def execute(filters=None): return _execute(filters) From 69514c47611f9ee2f7f18278d67e8156b6ac452f Mon Sep 17 00:00:00 2001 From: Mohammad Hasnain Date: Wed, 9 Dec 2020 12:10:28 +0530 Subject: [PATCH 09/14] fix: scan barcode does not update barcode item field in sales order --- erpnext/public/js/controllers/transaction.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 7f08cd1359..f0fea04373 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -408,7 +408,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ show_description(row_to_modify.idx, row_to_modify.item_code); - this.frm.from_barcode = true; + this.frm.from_barcode = this.frm.from_barcode ? this.frm.from_barcode + 1: 1; frappe.model.set_value(row_to_modify.doctype, row_to_modify.name, { item_code: data.item_code, qty: (row_to_modify.qty || 0) + 1 @@ -492,7 +492,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ d.item_code = ""; } - this.frm.from_barcode = true; + this.frm.from_barcode = this.frm.from_barcode ? this.frm.from_barcode + 1: 1; this.item_code(doc, cdt, cdn); }, @@ -509,11 +509,12 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ show_batch_dialog = 1; } // clear barcode if setting item (else barcode will take priority) - if(!this.frm.from_barcode) { + if(this.frm.from_barcode == 0) { item.barcode = null; } + this.frm.from_barcode = this.frm.from_barcode - 1 >= 0 ? this.frm.from_barcode - 1 : 0 + - this.frm.from_barcode = false; if(item.item_code || item.barcode || item.serial_no) { if(!this.validate_company_and_party()) { this.frm.fields_dict["items"].grid.grid_rows[item.idx - 1].remove(); From deaf2e80cb0b3c9aad7468c095658b89eb76d1aa Mon Sep 17 00:00:00 2001 From: Mohammad Hasnain Mohsin Rajan Date: Wed, 9 Dec 2020 12:36:14 +0530 Subject: [PATCH 10/14] fix: linting --- erpnext/public/js/controllers/transaction.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index f0fea04373..b15c92c659 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -509,10 +509,10 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ show_batch_dialog = 1; } // clear barcode if setting item (else barcode will take priority) - if(this.frm.from_barcode == 0) { + if (this.frm.from_barcode == 0) { item.barcode = null; } - this.frm.from_barcode = this.frm.from_barcode - 1 >= 0 ? this.frm.from_barcode - 1 : 0 + this.frm.from_barcode = this.frm.from_barcode - 1 >= 0 ? this.frm.from_barcode - 1 : 0; if(item.item_code || item.barcode || item.serial_no) { From dc3e9f4216de29260a2328be94bbeba454ea09e5 Mon Sep 17 00:00:00 2001 From: Saqib Date: Thu, 10 Dec 2020 12:30:15 +0530 Subject: [PATCH 11/14] fix: asset with value zero doesn't show up in fixed asset register (#24091) --- .../fixed_asset_register.py | 35 +++++++++---------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py index af08a2a601..d1457b9b85 100644 --- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py +++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py @@ -75,24 +75,23 @@ def get_data(filters): for asset in assets_record: asset_value = asset.gross_purchase_amount - flt(asset.opening_accumulated_depreciation) \ - flt(depreciation_amount_map.get(asset.name)) - if asset_value: - row = { - "asset_id": asset.asset_id, - "asset_name": asset.asset_name, - "status": asset.status, - "department": asset.department, - "cost_center": asset.cost_center, - "vendor_name": pr_supplier_map.get(asset.purchase_receipt) or pi_supplier_map.get(asset.purchase_invoice), - "gross_purchase_amount": asset.gross_purchase_amount, - "opening_accumulated_depreciation": asset.opening_accumulated_depreciation, - "depreciated_amount": depreciation_amount_map.get(asset.asset_id) or 0.0, - "available_for_use_date": asset.available_for_use_date, - "location": asset.location, - "asset_category": asset.asset_category, - "purchase_date": asset.purchase_date, - "asset_value": asset_value - } - data.append(row) + row = { + "asset_id": asset.asset_id, + "asset_name": asset.asset_name, + "status": asset.status, + "department": asset.department, + "cost_center": asset.cost_center, + "vendor_name": pr_supplier_map.get(asset.purchase_receipt) or pi_supplier_map.get(asset.purchase_invoice), + "gross_purchase_amount": asset.gross_purchase_amount, + "opening_accumulated_depreciation": asset.opening_accumulated_depreciation, + "depreciated_amount": depreciation_amount_map.get(asset.asset_id) or 0.0, + "available_for_use_date": asset.available_for_use_date, + "location": asset.location, + "asset_category": asset.asset_category, + "purchase_date": asset.purchase_date, + "asset_value": asset_value + } + data.append(row) return data From 7acb42d6df3f39695da4e7467f382643d83c0aa7 Mon Sep 17 00:00:00 2001 From: Saqib Date: Thu, 10 Dec 2020 16:22:01 +0530 Subject: [PATCH 12/14] feat(POS): hide images & auto add item checkbox (#24102) --- .../doctype/pos_profile/pos_profile.json | 21 +++++++++++++--- .../page/point_of_sale/pos_controller.js | 24 +++++++++---------- .../page/point_of_sale/pos_item_cart.js | 10 +++++--- .../page/point_of_sale/pos_item_selector.js | 18 ++++++++++---- 4 files changed, 50 insertions(+), 23 deletions(-) diff --git a/erpnext/accounts/doctype/pos_profile/pos_profile.json b/erpnext/accounts/doctype/pos_profile/pos_profile.json index 570111acac..d856ae3476 100644 --- a/erpnext/accounts/doctype/pos_profile/pos_profile.json +++ b/erpnext/accounts/doctype/pos_profile/pos_profile.json @@ -14,7 +14,6 @@ "column_break_9", "update_stock", "ignore_pricing_rule", - "hide_unavailable_items", "warehouse", "campaign", "company_address", @@ -23,6 +22,9 @@ "section_break_11", "payments", "section_break_14", + "hide_images", + "hide_unavailable_items", + "auto_add_item_to_cart", "item_groups", "column_break_16", "customer_groups", @@ -124,7 +126,8 @@ }, { "fieldname": "section_break_14", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "label": "Configuration" }, { "description": "Only show Items from these Item Groups", @@ -314,13 +317,25 @@ "fieldname": "hide_unavailable_items", "fieldtype": "Check", "label": "Hide Unavailable Items" + }, + { + "default": "0", + "fieldname": "hide_images", + "fieldtype": "Check", + "label": "Hide Images" + }, + { + "default": "0", + "fieldname": "auto_add_item_to_cart", + "fieldtype": "Check", + "label": "Automatically Add Filtered Item To Cart" } ], "icon": "icon-cog", "idx": 1, "index_web_pages_for_search": 1, "links": [], - "modified": "2020-10-29 13:18:38.795925", + "modified": "2020-12-10 13:59:28.877572", "modified_by": "Administrator", "module": "Accounts", "name": "POS Profile", diff --git a/erpnext/selling/page/point_of_sale/pos_controller.js b/erpnext/selling/page/point_of_sale/pos_controller.js index ad1633e71d..d4cde43359 100644 --- a/erpnext/selling/page/point_of_sale/pos_controller.js +++ b/erpnext/selling/page/point_of_sale/pos_controller.js @@ -111,24 +111,24 @@ erpnext.PointOfSale.Controller = class { dialog.show(); } - prepare_app_defaults(data) { + async prepare_app_defaults(data) { this.pos_opening = data.name; this.company = data.company; this.pos_profile = data.pos_profile; this.pos_opening_time = data.period_start_date; + this.item_stock_map = {}; + this.settings = {}; frappe.db.get_value('Stock Settings', undefined, 'allow_negative_stock').then(({ message }) => { this.allow_negative_stock = flt(message.allow_negative_stock) || false; }); frappe.db.get_doc("POS Profile", this.pos_profile).then((profile) => { - this.customer_groups = profile.customer_groups.map(group => group.customer_group); - this.cart.make_customer_selector(); + this.settings.customer_groups = profile.customer_groups.map(group => group.customer_group); + this.settings.hide_images = profile.hide_images; + this.settings.auto_add_item_to_cart = profile.auto_add_item_to_cart; + this.make_app(); }); - - this.item_stock_map = {}; - - this.make_app(); } set_opening_entry_status() { @@ -238,12 +238,11 @@ erpnext.PointOfSale.Controller = class { this.item_selector = new erpnext.PointOfSale.ItemSelector({ wrapper: this.$components_wrapper, pos_profile: this.pos_profile, + settings: this.settings, events: { item_selected: args => this.on_cart_update(args), - get_frm: () => this.frm || {}, - - get_allowed_item_group: () => this.item_groups + get_frm: () => this.frm || {} } }) } @@ -251,6 +250,7 @@ erpnext.PointOfSale.Controller = class { init_item_cart() { this.cart = new erpnext.PointOfSale.ItemCart({ wrapper: this.$components_wrapper, + settings: this.settings, events: { get_frm: () => this.frm, @@ -273,9 +273,7 @@ erpnext.PointOfSale.Controller = class { this.customer_details = details; // will add/remove LP payment method this.payment.render_loyalty_points_payment_mode(); - }, - - get_allowed_customer_group: () => this.customer_groups + } } }) } diff --git a/erpnext/selling/page/point_of_sale/pos_item_cart.js b/erpnext/selling/page/point_of_sale/pos_item_cart.js index 7799dacacb..3938300a2a 100644 --- a/erpnext/selling/page/point_of_sale/pos_item_cart.js +++ b/erpnext/selling/page/point_of_sale/pos_item_cart.js @@ -1,8 +1,10 @@ erpnext.PointOfSale.ItemCart = class { - constructor({ wrapper, events }) { + constructor({ wrapper, events, settings }) { this.wrapper = wrapper; this.events = events; this.customer_info = undefined; + this.hide_images = settings.hide_images; + this.allowed_customer_groups = settings.customer_groups; this.init_component(); } @@ -32,6 +34,7 @@ erpnext.PointOfSale.ItemCart = class { `
` ) this.$customer_section = this.$component.find('.customer-section'); + this.make_customer_selector(); } reset_customer_selector() { @@ -302,7 +305,7 @@ erpnext.PointOfSale.ItemCart = class { this.$customer_section.html(`
`); const me = this; const query = { query: 'erpnext.controllers.queries.customer_query' }; - const allowed_customer_group = this.events.get_allowed_customer_group() || []; + const allowed_customer_group = this.allowed_customer_groups || []; if (allowed_customer_group.length) { query.filters = { customer_group: ['in', allowed_customer_group] @@ -423,6 +426,7 @@ erpnext.PointOfSale.ItemCart = class { } update_customer_section() { + const me = this; const { customer, email_id='', mobile_no='', image } = this.customer_info || {}; if (customer) { @@ -460,7 +464,7 @@ erpnext.PointOfSale.ItemCart = class { } function get_customer_image() { - if (image) { + if (!me.hide_images && image) { return `
${image}
` diff --git a/erpnext/selling/page/point_of_sale/pos_item_selector.js b/erpnext/selling/page/point_of_sale/pos_item_selector.js index 49d42814ab..a06b3942f7 100644 --- a/erpnext/selling/page/point_of_sale/pos_item_selector.js +++ b/erpnext/selling/page/point_of_sale/pos_item_selector.js @@ -1,8 +1,10 @@ erpnext.PointOfSale.ItemSelector = class { - constructor({ frm, wrapper, events, pos_profile }) { + constructor({ frm, wrapper, events, pos_profile, settings }) { this.wrapper = wrapper; this.events = events; this.pos_profile = pos_profile; + this.hide_images = settings.hide_images; + this.auto_add_item = settings.auto_add_item_to_cart; this.inti_component(); } @@ -26,13 +28,14 @@ erpnext.PointOfSale.ItemSelector = class {
ALL ITEMS
-
+
` ); this.$component = this.wrapper.find('.items-selector'); + this.$items_container = this.$component.find('.items-container'); } async load_items_data() { @@ -65,7 +68,6 @@ erpnext.PointOfSale.ItemSelector = class { render_item_list(items) { - this.$items_container = this.$component.find('.items-container'); this.$items_container.html(''); items.forEach(item => { @@ -75,11 +77,12 @@ erpnext.PointOfSale.ItemSelector = class { } get_item_html(item) { + const me = this; const { item_image, serial_no, batch_no, barcode, actual_qty, stock_uom } = item; const indicator_color = actual_qty > 10 ? "green" : actual_qty <= 0 ? "red" : "orange"; function get_item_image_html() { - if (item_image) { + if (!me.hide_images && item_image) { return `
${frappe.get_abbr(item.item_name)}
` @@ -203,6 +206,7 @@ erpnext.PointOfSale.ItemSelector = class { ignore_inputs: true, page: cur_page.page.page }); + // for selecting the last filtered item on search frappe.ui.keys.on("enter", () => { const selector_is_visible = this.$component.is(':visible'); @@ -235,6 +239,7 @@ erpnext.PointOfSale.ItemSelector = class { const items = this.search_index[search_term]; this.items = items; this.render_item_list(items); + this.auto_add_item && this.items.length == 1 && this.add_filtered_item_to_cart(); return; } } @@ -247,8 +252,13 @@ erpnext.PointOfSale.ItemSelector = class { } this.items = items; this.render_item_list(items); + this.auto_add_item && this.items.length == 1 && this.add_filtered_item_to_cart(); }); } + + add_filtered_item_to_cart() { + this.$items_container.find(".item-wrapper").click(); + } resize_selector(minimize) { minimize ? From ce1ca282962d1bafeb4c3c64853249c3e7ec96cd Mon Sep 17 00:00:00 2001 From: Mohammad Hasnain Mohsin Rajan Date: Thu, 10 Dec 2020 17:48:05 +0530 Subject: [PATCH 13/14] fix: linting Co-authored-by: Marica --- erpnext/public/js/controllers/transaction.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index b15c92c659..d4479b3b88 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -408,7 +408,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ show_description(row_to_modify.idx, row_to_modify.item_code); - this.frm.from_barcode = this.frm.from_barcode ? this.frm.from_barcode + 1: 1; + this.frm.from_barcode = this.frm.from_barcode ? this.frm.from_barcode + 1 : 1; frappe.model.set_value(row_to_modify.doctype, row_to_modify.name, { item_code: data.item_code, qty: (row_to_modify.qty || 0) + 1 From 8abe7b91fe90003d28ae74fb50c4303acf09258a Mon Sep 17 00:00:00 2001 From: Mohammad Hasnain Mohsin Rajan Date: Thu, 10 Dec 2020 17:48:22 +0530 Subject: [PATCH 14/14] fix: linting Co-authored-by: Marica --- erpnext/public/js/controllers/transaction.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index d4479b3b88..3bc20f8733 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -492,7 +492,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ d.item_code = ""; } - this.frm.from_barcode = this.frm.from_barcode ? this.frm.from_barcode + 1: 1; + this.frm.from_barcode = this.frm.from_barcode ? this.frm.from_barcode + 1 : 1; this.item_code(doc, cdt, cdn); },