From e6791ee78e2afabb00a73b6935841ef8988dc219 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Fri, 23 Oct 2015 10:39:24 +0530 Subject: [PATCH 01/15] [fix] digest values in absolute --- erpnext/setup/doctype/email_digest/email_digest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/setup/doctype/email_digest/email_digest.py b/erpnext/setup/doctype/email_digest/email_digest.py index f6c074dfc4..289a6be4e4 100644 --- a/erpnext/setup/doctype/email_digest/email_digest.py +++ b/erpnext/setup/doctype/email_digest/email_digest.py @@ -346,7 +346,7 @@ class EmailDigest(Document): self.get_next_sending() def fmt_money(self, value): - return fmt_money(value, currency = self.currency) + return fmt_money(abs(value), currency = self.currency) def send(): now_date = now_datetime().date() From 5033e7b4318f24124ef6b9790883d5283504279e Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Fri, 23 Oct 2015 10:51:56 +0530 Subject: [PATCH 02/15] [hot] fix stock projected qty --- .../stock/report/stock_projected_qty/stock_projected_qty.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py b/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py index 348cca2fd3..cdc5507bf2 100644 --- a/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py +++ b/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals import frappe from frappe import _ +from frappe.utils import flt def execute(filters=None): filters = frappe._dict(filters or {}) @@ -45,7 +46,7 @@ def get_data(filters): data.append([item.name, item.item_name, item.description, item.item_group, item.brand, bin.warehouse, item.stock_uom, bin.actual_qty, bin.planned_qty, bin.indented_qty, bin.ordered_qty, bin.reserved_qty, - bin.projected_qty, re_order_level, re_order_qty, re_order_level - bin.projected_qty]) + bin.projected_qty, re_order_level, re_order_qty, re_order_level - flt(bin.projected_qty)]) return data From a10b52c6e68fd02dd5759963b06e9628cb4b2115 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 23 Oct 2015 13:02:49 +0530 Subject: [PATCH 03/15] Don't delete tax template and pos profile while deleting company transactions --- .../setup/doctype/company/delete_company_transactions.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/setup/doctype/company/delete_company_transactions.py b/erpnext/setup/doctype/company/delete_company_transactions.py index f7334b1e6d..ba2a5b83f5 100644 --- a/erpnext/setup/doctype/company/delete_company_transactions.py +++ b/erpnext/setup/doctype/company/delete_company_transactions.py @@ -22,8 +22,10 @@ def delete_company_transactions(company_name): for doctype in frappe.db.sql_list("""select parent from tabDocField where fieldtype='Link' and options='Company'"""): - if doctype not in ("Account", "Cost Center", "Warehouse", "Budget Detail", "Party Account", "Employee"): - delete_for_doctype(doctype, company_name) + if doctype not in ("Account", "Cost Center", "Warehouse", "Budget Detail", + "Party Account", "Employee", "Sales Taxes and Charges Template", + "Purchase Taxes and Charges Template", "POS Profile"): + delete_for_doctype(doctype, company_name) # Clear notification counts clear_notifications() From 7a9f46d9d11ead1ddd601d4ac3a7ba9d21f19f39 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 23 Oct 2015 13:19:01 +0530 Subject: [PATCH 04/15] [fix] Value mapping while making bank entry from Expense Claim --- erpnext/accounts/doctype/journal_entry/journal_entry.py | 9 ++++++--- erpnext/hr/doctype/expense_claim/expense_claim.js | 7 ++++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index 9525a3aef0..9eaf1ddfc4 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -498,14 +498,17 @@ def get_default_bank_cash_account(company, voucher_type, mode_of_payment=None): if voucher_type=="Bank Entry": account = frappe.db.get_value("Company", company, "default_bank_account") if not account: - account = frappe.db.get_value("Account", {"company": company, "account_type": "Bank", "is_group": 0}) + account = frappe.db.get_value("Account", + {"company": company, "account_type": "Bank", "is_group": 0}) elif voucher_type=="Cash Entry": account = frappe.db.get_value("Company", company, "default_cash_account") if not account: - account = frappe.db.get_value("Account", {"company": company, "account_type": "Cash", "is_group": 0}) + account = frappe.db.get_value("Account", + {"company": company, "account_type": "Cash", "is_group": 0}) if account: - account_details = frappe.db.get_value("Account", account, ["account_currency", "account_type"], as_dict=1) + account_details = frappe.db.get_value("Account", account, + ["account_currency", "account_type"], as_dict=1) return { "account": account, "balance": get_balance_on(account), diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.js b/erpnext/hr/doctype/expense_claim/expense_claim.js index 1d405da00a..780cd70612 100644 --- a/erpnext/hr/doctype/expense_claim/expense_claim.js +++ b/erpnext/hr/doctype/expense_claim/expense_claim.js @@ -18,24 +18,25 @@ erpnext.hr.ExpenseClaimController = frappe.ui.form.Controller.extend({ jv.voucher_type = 'Bank Entry'; jv.company = cur_frm.doc.company; jv.remark = 'Payment against Expense Claim: ' + cur_frm.doc.name; - jv.fiscal_year = cur_frm.doc.fiscal_year; var expense = cur_frm.doc.expenses || []; for(var i = 0; i < expense.length; i++){ var d1 = frappe.model.add_child(jv, 'Journal Entry Account', 'accounts'); - d1.debit = expense[i].sanctioned_amount; d1.account = expense[i].default_account; + d1.debit_in_account_currency = expense[i].sanctioned_amount; d1.reference_type = cur_frm.doc.doctype; d1.reference_name = cur_frm.doc.name; } // credit to bank var d1 = frappe.model.add_child(jv, 'Journal Entry Account', 'accounts'); - d1.credit = cur_frm.doc.total_sanctioned_amount; + d1.credit_in_account_currency = cur_frm.doc.total_sanctioned_amount; d1.reference_type = cur_frm.doc.doctype; d1.reference_name = cur_frm.doc.name; if(r.message) { d1.account = r.message.account; d1.balance = r.message.balance; + d1.account_currency = r.message.account_currency; + d1.account_type = r.message.account_type; } loaddoc('Journal Entry', jv.name); From b9bfe6117e5c31cd88acca33c385732ae72d9fc6 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Fri, 23 Oct 2015 14:49:09 +0530 Subject: [PATCH 05/15] [optimization] Stock Projected Qty report --- .../stock_projected_qty.py | 47 +++++++++++++++++-- 1 file changed, 43 insertions(+), 4 deletions(-) diff --git a/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py b/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py index cdc5507bf2..51e383627c 100644 --- a/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py +++ b/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals import frappe from frappe import _ -from frappe.utils import flt +from frappe.utils import flt, today def execute(filters=None): filters = frappe._dict(filters or {}) @@ -19,12 +19,19 @@ def get_columns(): _("Shortage Qty") + ":Float:100"] def get_data(filters): - item_map = {} + bin_list = get_bin_list(filters) + item_map = get_item_map(filters.get("item_code")) warehouse_company = {} data = [] - for bin in get_bin_list(filters): - item = item_map.setdefault(bin.item_code, frappe.get_doc("Item", bin.item_code)) + for bin in bin_list: + item = item_map.get(bin.item_code) + + if not item: + # likely an item that has reached its end of life + continue + + # item = item_map.setdefault(bin.item_code, get_item(bin.item_code)) company = warehouse_company.setdefault(bin.warehouse, frappe.db.get_value("Warehouse", bin.warehouse, "company")) if filters.brand and filters.brand != item.brand: @@ -62,3 +69,35 @@ def get_bin_list(filters): filters=bin_filters, order_by="item_code, warehouse") return bin_list + +def get_item_map(item_code): + """Optimization: get only the item doc and re_order_levels table""" + + condition = "" + if item_code: + condition = 'and item_code = "{0}"'.format(frappe.db.escape(item_code)) + + items = frappe.db.sql("""select * from `tabItem` item + where is_stock_item = 1 + {condition} + and (end_of_life > %(today)s or end_of_life is null or end_of_life='0000-00-00') + and exists (select name from `tabBin` bin where bin.item_code=item.name)"""\ + .format(condition=condition), {"today": today()}, as_dict=True) + + condition = "" + if item_code: + condition = 'where parent="{0}"'.format(frappe.db.escape(item_code)) + + reorder_levels = frappe._dict() + for ir in frappe.db.sql("""select * from `tabItem Reorder` {condition}""".format(condition=condition), as_dict=1): + if ir.parent not in reorder_levels: + reorder_levels[ir.parent] = [] + + reorder_levels[ir.parent].append(ir) + + item_map = frappe._dict() + for item in items: + item["reorder_levels"] = reorder_levels.get(item.name) or [] + item_map[item.name] = item + + return item_map From 00818bfa906eb1ab22094853fc82cd2a82585bcd Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 23 Oct 2015 16:10:56 +0530 Subject: [PATCH 06/15] Set tax rule based on date --- erpnext/accounts/doctype/tax_rule/tax_rule.py | 11 ++++---- erpnext/public/js/utils/party.js | 26 ++++++++++++++++++- 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/erpnext/accounts/doctype/tax_rule/tax_rule.py b/erpnext/accounts/doctype/tax_rule/tax_rule.py index bff18980b6..7b6208293a 100644 --- a/erpnext/accounts/doctype/tax_rule/tax_rule.py +++ b/erpnext/accounts/doctype/tax_rule/tax_rule.py @@ -109,7 +109,8 @@ def get_party_details(party, party_type, args=None): def get_tax_template(posting_date, args): """Get matching tax rule""" args = frappe._dict(args) - conditions = [] + conditions = ["""(from_date is null or from_date = '' or from_date <= '{0}') + and (to_date is null or to_date = '' or to_date >= '{0}')""".format(posting_date)] for key, value in args.iteritems(): if key in "use_for_shopping_cart": @@ -117,16 +118,16 @@ def get_tax_template(posting_date, args): else: conditions.append("ifnull({0}, '') in ('', '{1}')".format(key, frappe.db.escape(cstr(value)))) - matching = frappe.db.sql("""select * from `tabTax Rule` + tax_rule = frappe.db.sql("""select * from `tabTax Rule` where {0}""".format(" and ".join(conditions)), as_dict = True) - if not matching: + if not tax_rule: return None - for rule in matching: + for rule in tax_rule: rule.no_of_keys_matched = 0 for key in args: if rule.get(key): rule.no_of_keys_matched += 1 - rule = sorted(matching, lambda b, a: cmp(a.no_of_keys_matched, b.no_of_keys_matched) or cmp(a.priority, b.priority))[0] + rule = sorted(tax_rule, lambda b, a: cmp(a.no_of_keys_matched, b.no_of_keys_matched) or cmp(a.priority, b.priority))[0] return rule.sales_tax_template or rule.purchase_tax_template diff --git a/erpnext/public/js/utils/party.js b/erpnext/public/js/utils/party.js index 9173b71703..cc6f112545 100644 --- a/erpnext/public/js/utils/party.js +++ b/erpnext/public/js/utils/party.js @@ -26,7 +26,12 @@ erpnext.utils.get_party_details = function(frm, method, args, callback) { } } if(!args) return; - + + if(frappe.meta.get_docfield(frm.doc.doctype, "taxes")) { + if(!erpnext.utils.validate_mandatory(frm, "Posting/Transaction Date", + args.posting_date, args.party_type=="Customer" ? "customer": "supplier")) return; + } + args.currency = frm.doc.currency; args.company = frm.doc.company; args.doctype = frm.doc.doctype; @@ -64,6 +69,15 @@ erpnext.utils.get_address_display = function(frm, address_field, display_field) if(r.message){ frm.set_value(display_field, r.message) } + + if(frappe.meta.get_docfield(frm.doc.doctype, "taxes")) { + if(!erpnext.utils.validate_mandatory(frm, "Customer/Supplier", + frm.doc.customer || frm.doc.supplier, address_field)) return; + + if(!erpnext.utils.validate_mandatory(frm, "Posting/Transaction Date", + frm.doc.posting_date || frm.doc.transaction_date, address_field)) return; + } else return; + frappe.call({ method: "erpnext.accounts.party.set_taxes", args: { @@ -99,3 +113,13 @@ erpnext.utils.get_contact_details = function(frm) { }) } } + +erpnext.utils.validate_mandatory = function(frm, label, value, trigger_on) { + if(!value) { + frm.doc[trigger_on] = ""; + refresh_field(trigger_on); + frappe.msgprint(__("Please enter {0} first", [label])); + return false; + } + return true; +} \ No newline at end of file From abc0b64b681efb37f4bf4cffbebe4e332fcfab09 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 26 Oct 2015 11:58:43 +0530 Subject: [PATCH 07/15] [fix] Label changed for Target Warehouse in Delivery Note --- .../delivery_note_item/delivery_note_item.json | 12 +++++++----- erpnext/stock/doctype/packed_item/packed_item.json | 6 +++--- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json index ceefaf5f29..96e26609a0 100644 --- a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json +++ b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json @@ -735,7 +735,7 @@ "ignore_user_permissions": 0, "in_filter": 0, "in_list_view": 1, - "label": "Warehouse", + "label": "From Warehouse", "no_copy": 0, "oldfieldname": "warehouse", "oldfieldtype": "Link", @@ -755,13 +755,15 @@ "allow_on_submit": 0, "bold": 0, "collapsible": 0, + "depends_on": "", + "description": "", "fieldname": "target_warehouse", "fieldtype": "Link", "hidden": 0, "ignore_user_permissions": 0, "in_filter": 0, "in_list_view": 0, - "label": "Target Warehouse", + "label": "To Warehouse (Optional)", "no_copy": 0, "options": "Warehouse", "permlevel": 0, @@ -831,7 +833,7 @@ "ignore_user_permissions": 0, "in_filter": 0, "in_list_view": 0, - "label": "Available Qty at Warehouse", + "label": "Available Qty at From Warehouse", "no_copy": 1, "oldfieldname": "actual_qty", "oldfieldtype": "Currency", @@ -857,7 +859,7 @@ "ignore_user_permissions": 0, "in_filter": 0, "in_list_view": 0, - "label": "Available Batch Qty at Warehouse", + "label": "Available Batch Qty at From Warehouse", "no_copy": 1, "permlevel": 0, "precision": "", @@ -1160,7 +1162,7 @@ "is_submittable": 0, "issingle": 0, "istable": 1, - "modified": "2015-10-19 03:04:50.887288", + "modified": "2015-10-26 02:19:18.053222", "modified_by": "Administrator", "module": "Stock", "name": "Delivery Note Item", diff --git a/erpnext/stock/doctype/packed_item/packed_item.json b/erpnext/stock/doctype/packed_item/packed_item.json index bfd78bf08e..88e11d7d8b 100644 --- a/erpnext/stock/doctype/packed_item/packed_item.json +++ b/erpnext/stock/doctype/packed_item/packed_item.json @@ -155,7 +155,7 @@ "ignore_user_permissions": 0, "in_filter": 0, "in_list_view": 0, - "label": "Warehouse", + "label": "From Warehouse", "no_copy": 0, "oldfieldname": "warehouse", "oldfieldtype": "Link", @@ -179,7 +179,7 @@ "ignore_user_permissions": 0, "in_filter": 0, "in_list_view": 0, - "label": "Target Warehouse", + "label": "To Warehouse (Optional)", "no_copy": 0, "options": "Warehouse", "permlevel": 0, @@ -511,7 +511,7 @@ "is_submittable": 0, "issingle": 0, "istable": 1, - "modified": "2015-10-12 07:38:58.896987", + "modified": "2015-10-26 02:25:47.718911", "modified_by": "Administrator", "module": "Stock", "name": "Packed Item", From 23bd21778e50581c97841097762ab00c89d5295e Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 26 Oct 2015 12:43:50 +0530 Subject: [PATCH 08/15] [fix] Events in Email Digest --- erpnext/setup/doctype/email_digest/email_digest.py | 2 +- erpnext/setup/doctype/email_digest/templates/default.html | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/setup/doctype/email_digest/email_digest.py b/erpnext/setup/doctype/email_digest/email_digest.py index 289a6be4e4..e2b0de0eb6 100644 --- a/erpnext/setup/doctype/email_digest/email_digest.py +++ b/erpnext/setup/doctype/email_digest/email_digest.py @@ -139,7 +139,7 @@ class EmailDigest(Document): for i, e in enumerate(events): e.starts_on_label = format_time(e.starts_on) - e.ends_on_label = format_time(e.ends_on) + e.ends_on_label = format_time(e.ends_on) if e.ends_on else None e.date = formatdate(e.starts) e.link = get_url_to_form("Event", e.name) diff --git a/erpnext/setup/doctype/email_digest/templates/default.html b/erpnext/setup/doctype/email_digest/templates/default.html index 3aad202529..d0bd13a7d4 100644 --- a/erpnext/setup/doctype/email_digest/templates/default.html +++ b/erpnext/setup/doctype/email_digest/templates/default.html @@ -52,6 +52,8 @@ {% if e.all_day %} {{ _("All Day") }} + {% elif (not e.ends_on_label or e.starts_on_label == e.ends_on_label)%} + {{ e.starts_on_label }} {% else %} {{ e.starts_on_label }} - {{ e.ends_on_label }} {% endif %} From 6485d4a7495fd2e72bb07019efaa167661261286 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 26 Oct 2015 14:31:10 +0530 Subject: [PATCH 09/15] [fix] account currency is not mandatory in get exchange rate --- .../accounts/doctype/journal_entry/journal_entry.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index 9eaf1ddfc4..082652143a 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -817,11 +817,19 @@ def get_account_balance_and_party_type(account, date, company, debit=None, credi return grid_values @frappe.whitelist() -def get_exchange_rate(account, account_currency, company, +def get_exchange_rate(account, account_currency=None, company=None, reference_type=None, reference_name=None, debit=None, credit=None, exchange_rate=None): from erpnext.setup.utils import get_exchange_rate + account_details = frappe.db.get_value("Account", account, + ["account_type", "root_type", "account_currency", "company"], as_dict=1) + + if not company: + company = account_details.company + + if not account_currency: + account_currency = account_details.account_currency + company_currency = get_company_currency(company) - account_details = frappe.db.get_value("Account", account, ["account_type", "root_type"], as_dict=1) if account_currency != company_currency: if reference_type in ("Sales Invoice", "Purchase Invoice") and reference_name: From 415df048342081f29343c5d71203033a22389396 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 26 Oct 2015 16:43:09 +0530 Subject: [PATCH 10/15] [fix] Account Type in Chart of Accounts --- erpnext/accounts/doctype/account/account.py | 2 ++ erpnext/accounts/page/accounts_browser/accounts_browser.js | 7 +++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/account/account.py b/erpnext/accounts/doctype/account/account.py index c3e4c393e4..8544b178c9 100644 --- a/erpnext/accounts/doctype/account/account.py +++ b/erpnext/accounts/doctype/account/account.py @@ -147,6 +147,8 @@ class Account(Document): self.validate_warehouse(old_warehouse) if self.warehouse: self.validate_warehouse(self.warehouse) + elif self.warehouse: + self.warehouse = None def validate_warehouse(self, warehouse): if frappe.db.get_value("Stock Ledger Entry", {"warehouse": warehouse}): diff --git a/erpnext/accounts/page/accounts_browser/accounts_browser.js b/erpnext/accounts/page/accounts_browser/accounts_browser.js index abd7612ce0..b172ae0db2 100644 --- a/erpnext/accounts/page/accounts_browser/accounts_browser.js +++ b/erpnext/accounts/page/accounts_browser/accounts_browser.js @@ -209,13 +209,12 @@ erpnext.AccountsChart = Class.extend({ {fieldtype:'Check', fieldname:'is_group', label:__('Is Group'), description: __('Further accounts can be made under Groups, but entries can be made against non-Groups')}, {fieldtype:'Select', fieldname:'account_type', label:__('Account Type'), - options: ['', 'Bank', 'Cash', 'Warehouse', 'Receivable', 'Payable', - 'Equity', 'Cost of Goods Sold', 'Fixed Asset', 'Expense Account', - 'Income Account', 'Tax', 'Chargeable', 'Temporary'].join('\n'), + options: ['', 'Bank', 'Cash', 'Warehouse', 'Tax', 'Chargeable'].join('\n'), description: __("Optional. This setting will be used to filter in various transactions.") }, {fieldtype:'Float', fieldname:'tax_rate', label:__('Tax Rate')}, {fieldtype:'Link', fieldname:'warehouse', label:__('Warehouse'), options:"Warehouse"}, - {fieldtype:'Link', fieldname:'account_currency', label:__('Currency'), options:"Currency"} + {fieldtype:'Link', fieldname:'account_currency', label:__('Currency'), options:"Currency", + description: __("Optional. Sets company's default currency, if not specified.")} ] }) From 83e68bb837593f31f13f7199b32b8f281e7d0104 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 26 Oct 2015 17:40:31 +0530 Subject: [PATCH 11/15] minor fix --- erpnext/public/js/utils/party.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/public/js/utils/party.js b/erpnext/public/js/utils/party.js index cc6f112545..427cd89c22 100644 --- a/erpnext/public/js/utils/party.js +++ b/erpnext/public/js/utils/party.js @@ -22,7 +22,7 @@ erpnext.utils.get_party_details = function(frm, method, args, callback) { } if (args) { - args.posting_date = frm.doc.transaction_date; + args.posting_date = frm.doc.posting_date || frm.doc.transaction_date; } } if(!args) return; From 6c6f3789d0b5709d14d97ae377db32f06feaed4b Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 23 Oct 2015 17:53:34 +0530 Subject: [PATCH 12/15] [fix] Is POS trigger --- erpnext/accounts/doctype/sales_invoice/sales_invoice.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index 9d620c2692..95e5a1c340 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -160,6 +160,9 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte me.set_dynamic_labels(); me.calculate_taxes_and_totals(); if(callback_fn) callback_fn(); + frappe.after_ajax(function() { + cur_frm.doc.__missing_values_set = false; + }) } } }); From 3f3696d1eb029b45099d636ae041e36adc31ece3 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Mon, 26 Oct 2015 19:57:31 +0530 Subject: [PATCH 13/15] [enhancement] [website] numeric attribute selector, smart selection of variant based on attribute combinations --- erpnext/stock/doctype/item/item.py | 61 ++++++++++++++++-- erpnext/templates/generators/item.html | 10 ++- erpnext/templates/includes/product_page.js | 75 ++++++++++++++++++++-- 3 files changed, 133 insertions(+), 13 deletions(-) diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index 94c0c620c2..0f1f0e0808 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -5,6 +5,7 @@ from __future__ import unicode_literals import frappe import json import urllib +import itertools from frappe import msgprint, _ from frappe.utils import cstr, flt, cint, getdate, now_datetime, formatdate from frappe.website.website_generator import WebsiteGenerator @@ -130,6 +131,8 @@ class Item(WebsiteGenerator): self.set_attribute_context(context) + self.set_disabled_attributes(context) + context.parents = self.get_parents(context) return context @@ -189,15 +192,63 @@ class Item(WebsiteGenerator): for attr in self.attributes: values = context.attribute_values.setdefault(attr.attribute, []) - # get list of values defined (for sequence) - for attr_value in frappe.db.get_all("Item Attribute Value", - fields=["attribute_value"], filters={"parent": attr.attribute}, order_by="idx asc"): + if cint(frappe.db.get_value("Item Attribute", attr.attribute, "numeric_values")): + for val in sorted(attribute_values_available.get(attr.attribute, []), key=flt): + values.append(val) - if attr_value.attribute_value in attribute_values_available.get(attr.attribute, []): - values.append(attr_value.attribute_value) + else: + # get list of values defined (for sequence) + for attr_value in frappe.db.get_all("Item Attribute Value", + fields=["attribute_value"], filters={"parent": attr.attribute}, order_by="idx asc"): + + if attr_value.attribute_value in attribute_values_available.get(attr.attribute, []): + values.append(attr_value.attribute_value) context.variant_info = json.dumps(context.variants) + def set_disabled_attributes(self, context): + """Disable selection options of attribute combinations that do not result in a variant""" + if not self.attributes: + return + + context.disabled_attributes = {} + attributes = [attr.attribute for attr in self.attributes] + + def find_variant(combination): + for variant in context.variants: + if len(variant.attributes) < len(attributes): + continue + + if "combination" not in variant: + ref_combination = [] + + for attr in variant.attributes: + idx = attributes.index(attr.attribute) + ref_combination.insert(idx, attr.attribute_value) + + variant["combination"] = ref_combination + + if not (set(combination) - set(variant["combination"])): + # check if the combination is a subset of a variant combination + # eg. [Blue, 0.5] is a possible combination if exists [Blue, Large, 0.5] + return True + + for i, attr in enumerate(self.attributes): + if i==0: + continue + + combination_source = [] + + # loop through previous attributes + for prev_attr in self.attributes[:i]: + combination_source.append([context.selected_attributes[prev_attr.attribute]]) + + combination_source.append(context.attribute_values[attr.attribute]) + + for combination in itertools.product(*combination_source): + if not find_variant(combination): + context.disabled_attributes.setdefault(attr.attribute, []).append(combination[-1]) + def check_warehouse_is_set_for_stock_item(self): if self.is_stock_item==1 and not self.default_warehouse and frappe.get_all("Warehouse"): frappe.msgprint(_("Default Warehouse is mandatory for stock Item."), diff --git a/erpnext/templates/generators/item.html b/erpnext/templates/generators/item.html index 4bd521d5a7..acbcedfd82 100644 --- a/erpnext/templates/generators/item.html +++ b/erpnext/templates/generators/item.html @@ -29,7 +29,8 @@
{% if has_variants %} {% for d in attributes %} -
{{ _(d.attribute) }}
+ {%- endif %} {% endfor %} {% endif %}
diff --git a/erpnext/templates/includes/product_page.js b/erpnext/templates/includes/product_page.js index 2345de46e3..e28f35182f 100644 --- a/erpnext/templates/includes/product_page.js +++ b/erpnext/templates/includes/product_page.js @@ -64,8 +64,23 @@ frappe.ready(function() { }); }); - $("[itemscope] .item-view-attribute select").on("change", function() { - var item_code = encodeURIComponent(get_item_code()); + $("[itemscope] .item-view-attribute .form-control").on("change", function() { + try { + var item_code = encodeURIComponent(get_item_code()); + } catch(e) { + // unable to find variant + // then chose the closest available one + + var attribute = $(this).attr("data-attribute"); + var attribute_value = $(this).val() + var item_code = update_attribute_selectors(attribute, attribute_value); + + if (!item_code) { + msgprint(__("Please select some other value for {0}", [attribute])) + throw e; + } + } + if (window.location.search.indexOf(item_code)!==-1) { return; } @@ -83,10 +98,8 @@ var toggle_update_cart = function(qty) { function get_item_code() { if(window.variant_info) { - attributes = {}; - $('[itemscope]').find(".item-view-attribute select").each(function() { - attributes[$(this).attr('data-attribute')] = $(this).val(); - }); + var attributes = get_selected_attributes(); + for(var i in variant_info) { var variant = variant_info[i]; var match = true; @@ -106,3 +119,53 @@ function get_item_code() { return item_code; } } + +function update_attribute_selectors(selected_attribute, selected_attribute_value) { + // find the closest match keeping the selected attribute in focus and get the item code + + var attributes = get_selected_attributes(); + + var previous_match_score = 0; + var matched; + for(var i in variant_info) { + var variant = variant_info[i]; + var match_score = 0; + var has_selected_attribute = false; + + console.log(variant); + + for(var j in variant.attributes) { + if(attributes[variant.attributes[j].attribute]===variant.attributes[j].attribute_value) { + match_score = match_score + 1; + + if (variant.attributes[j].attribute==selected_attribute && variant.attributes[j].attribute_value==selected_attribute_value) { + has_selected_attribute = true; + } + } + } + + if (has_selected_attribute && (match_score > previous_match_score)) { + previous_match_score = match_score; + matched = variant; + } + } + + if (matched) { + for (var j in matched.attributes) { + var attr = matched.attributes[j]; + $('[itemscope]') + .find(repl('.item-view-attribute .form-control[data-attribute="%(attribute)s"]', attr)) + .val(attr.attribute_value); + } + + return matched.name; + } +} + +function get_selected_attributes() { + var attributes = {}; + $('[itemscope]').find(".item-view-attribute .form-control").each(function() { + attributes[$(this).attr('data-attribute')] = $(this).val(); + }); + return attributes; +} From fdeab29e94609da6afe82302e2708ec0b95d2e8e Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 27 Oct 2015 11:23:43 +0530 Subject: [PATCH 14/15] Removed console.log --- erpnext/templates/includes/product_page.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/erpnext/templates/includes/product_page.js b/erpnext/templates/includes/product_page.js index e28f35182f..f7a2360642 100644 --- a/erpnext/templates/includes/product_page.js +++ b/erpnext/templates/includes/product_page.js @@ -132,8 +132,6 @@ function update_attribute_selectors(selected_attribute, selected_attribute_value var match_score = 0; var has_selected_attribute = false; - console.log(variant); - for(var j in variant.attributes) { if(attributes[variant.attributes[j].attribute]===variant.attributes[j].attribute_value) { match_score = match_score + 1; From 99f4b43641f72eea1962851fff8f92a6093ea4ff Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 27 Oct 2015 11:59:35 +0600 Subject: [PATCH 15/15] bumped to version 6.6.5 --- erpnext/__version__.py | 2 +- erpnext/hooks.py | 2 +- setup.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/__version__.py b/erpnext/__version__.py index 1752aca69a..9a900fe8c8 100644 --- a/erpnext/__version__.py +++ b/erpnext/__version__.py @@ -1,2 +1,2 @@ from __future__ import unicode_literals -__version__ = '6.6.4' +__version__ = '6.6.5' diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 5eacd95d2d..0d29e96d2d 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -29,7 +29,7 @@ blogs. """ app_icon = "icon-th" app_color = "#e74c3c" -app_version = "6.6.4" +app_version = "6.6.5" github_link = "https://github.com/frappe/erpnext" error_report_email = "support@erpnext.com" diff --git a/setup.py b/setup.py index 1934282e20..c673dc32e6 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,6 @@ from setuptools import setup, find_packages -version = "6.6.4" +version = "6.6.5" with open("requirements.txt", "r") as f: install_requires = f.readlines()