diff --git a/erpnext/accounts/doctype/account/account.py b/erpnext/accounts/doctype/account/account.py index 0a72d4fa4e6..c6de6410ebc 100644 --- a/erpnext/accounts/doctype/account/account.py +++ b/erpnext/accounts/doctype/account/account.py @@ -89,7 +89,7 @@ class Account(NestedSet): throw(_("Root cannot be edited."), RootNotEditable) if not self.parent_account and not self.is_group: - frappe.throw(_("Root Account must be a group")) + frappe.throw(_("The root account {0} must be a group").format(frappe.bold(self.name))) def validate_root_company_and_sync_account_to_children(self): # ignore validation while creating new compnay or while syncing to child companies diff --git a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py index 00311c4f66d..f496280317a 100644 --- a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py +++ b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py @@ -97,7 +97,7 @@ def build_forest(data): return [parent_account] elif account_name == child: parent_account_list = return_parent(data, parent_account) - if not parent_account_list: + if not parent_account_list and parent_account: frappe.throw(_("The parent account {0} does not exists") .format(parent_account)) return [child] + parent_account_list @@ -108,7 +108,7 @@ def build_forest(data): error_messages = [] for i in data: - account_name, __, account_number, is_group, account_type, root_type = i + account_name, dummy, account_number, is_group, account_type, root_type = i if not account_name: error_messages.append("Row {0}: Please enter Account Name".format(line_no)) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index 2192b7bf989..fbf11bca5de 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -154,8 +154,11 @@ frappe.ui.form.on('Payment Entry', { frm.toggle_display("base_paid_amount", frm.doc.paid_from_account_currency != company_currency); - frm.toggle_display("base_received_amount", (frm.doc.paid_to_account_currency != company_currency && - frm.doc.paid_from_account_currency != frm.doc.paid_to_account_currency)); + frm.toggle_display("base_received_amount", ( + frm.doc.paid_to_account_currency != company_currency && + frm.doc.paid_from_account_currency != frm.doc.paid_to_account_currency + && frm.doc.base_paid_amount != frm.doc.base_received_amount + )); frm.toggle_display("received_amount", (frm.doc.payment_type=="Internal Transfer" || frm.doc.paid_from_account_currency != frm.doc.paid_to_account_currency)) @@ -486,6 +489,7 @@ frappe.ui.form.on('Payment Entry', { paid_amount: function(frm) { frm.set_value("base_paid_amount", flt(frm.doc.paid_amount) * flt(frm.doc.source_exchange_rate)); frm.trigger("reset_received_amount"); + frm.events.hide_unhide_fields(frm); }, received_amount: function(frm) { @@ -509,6 +513,7 @@ frappe.ui.form.on('Payment Entry', { frm.events.set_unallocated_amount(frm); frm.set_paid_amount_based_on_received_amount = false; + frm.events.hide_unhide_fields(frm); }, reset_received_amount: function(frm) { diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py index 2ffa27f6d99..100bb1d3e38 100644 --- a/erpnext/accounts/doctype/pricing_rule/utils.py +++ b/erpnext/accounts/doctype/pricing_rule/utils.py @@ -178,7 +178,8 @@ def filter_pricing_rules(args, pricing_rules, doc=None): if pricing_rules[0].mixed_conditions and doc: stock_qty, amount, items = get_qty_and_rate_for_mixed_conditions(doc, pr_doc, args) - pricing_rules[0].apply_rule_on_other_items = items + for pricing_rule_args in pricing_rules: + pricing_rule_args.apply_rule_on_other_items = items elif pricing_rules[0].is_cumulative: items = [args.get(frappe.scrub(pr_doc.get('apply_on')))] @@ -329,9 +330,9 @@ def get_qty_and_rate_for_mixed_conditions(doc, pr_doc, args): if pr_doc.mixed_conditions: amt = args.get('qty') * args.get("price_list_rate") if args.get("item_code") != row.get("item_code"): - amt = row.get('qty') * row.get("price_list_rate") + amt = row.get('qty') * (row.get("price_list_rate") or args.get("rate")) - sum_qty += row.get("stock_qty") or args.get("stock_qty") + sum_qty += row.get("stock_qty") or args.get("stock_qty") or args.get("qty") sum_amt += amt if pr_doc.is_cumulative: diff --git a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json index 296c7049ce4..a8dfab6a54b 100644 --- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json +++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json @@ -753,8 +753,7 @@ { "fieldname": "manufacturer_part_no", "fieldtype": "Data", - "label": "Manufacturer Part Number", - "read_only": 1 + "label": "Manufacturer Part Number" }, { "depends_on": "is_fixed_asset", @@ -770,7 +769,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2020-04-01 14:20:17.297284", + "modified": "2020-04-07 18:34:35.104178", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice Item", diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index b8c45aba1f5..5218b740edd 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -432,11 +432,12 @@ class SalesInvoice(SellingController): if pos.get("company_address"): self.company_address = pos.get("company_address") - customer_price_list, customer_group = frappe.get_value("Customer", self.customer, ['default_price_list', 'customer_group']) - - customer_group_price_list = frappe.get_value("Customer Group", customer_group, 'default_price_list') - - selling_price_list = customer_price_list or customer_group_price_list or pos.get('selling_price_list') + if self.customer: + customer_price_list, customer_group = frappe.get_value("Customer", self.customer, ['default_price_list', 'customer_group']) + customer_group_price_list = frappe.get_value("Customer Group", customer_group, 'default_price_list') + selling_price_list = customer_price_list or customer_group_price_list or pos.get('selling_price_list') + else: + selling_price_list = pos.get('selling_price_list') if selling_price_list: self.set('selling_price_list', selling_price_list) diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index 156f2181b87..3d9ff273ce2 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -163,8 +163,9 @@ def get_default_price_list(party): def set_price_list(party_details, party, party_type, given_price_list, pos=None): # price list price_list = get_permitted_documents('Price List') - - if price_list: + + # if there is only one permitted document based on user permissions, set it + if price_list and len(price_list) == 1: price_list = price_list[0] elif pos and party_type == 'Customer': customer_price_list = frappe.get_value('Customer', party.name, 'default_price_list') diff --git a/erpnext/accounts/report/purchase_register/purchase_register.js b/erpnext/accounts/report/purchase_register/purchase_register.js index 42b35c2a997..b2b95b2b81b 100644 --- a/erpnext/accounts/report/purchase_register/purchase_register.js +++ b/erpnext/accounts/report/purchase_register/purchase_register.js @@ -34,6 +34,33 @@ frappe.query_reports["Purchase Register"] = { "label": __("Mode of Payment"), "fieldtype": "Link", "options": "Mode of Payment" + }, + { + "fieldname":"cost_center", + "label": __("Cost Center"), + "fieldtype": "Link", + "options": "Cost Center" + }, + { + "fieldname":"warehouse", + "label": __("Warehouse"), + "fieldtype": "Link", + "options": "Warehouse" + }, + { + "fieldname":"item_group", + "label": __("Item Group"), + "fieldtype": "Link", + "options": "Item Group" } ] } + +erpnext.dimension_filters.forEach((dimension) => { + frappe.query_reports["Purchase Register"].filters.splice(7, 0 ,{ + "fieldname": dimension["fieldname"], + "label": __(dimension["label"]), + "fieldtype": "Link", + "options": dimension["document_type"] + }); +}); \ No newline at end of file diff --git a/erpnext/accounts/report/purchase_register/purchase_register.py b/erpnext/accounts/report/purchase_register/purchase_register.py index 3f8abb76e26..9399e707390 100644 --- a/erpnext/accounts/report/purchase_register/purchase_register.py +++ b/erpnext/accounts/report/purchase_register/purchase_register.py @@ -5,6 +5,7 @@ from __future__ import unicode_literals import frappe from frappe.utils import flt from frappe import msgprint, _ +from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions, get_dimension_with_children def execute(filters=None): return _execute(filters) @@ -66,7 +67,7 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum total_tax += tax_amount row.append(tax_amount) - # total tax, grand total, rounded total & outstanding amount + # total tax, grand total, rounded total & outstanding amount row += [total_tax, inv.base_grand_total, flt(inv.base_grand_total, 0), inv.outstanding_amount] data.append(row) @@ -134,6 +135,38 @@ def get_conditions(filters): if filters.get("mode_of_payment"): conditions += " and ifnull(mode_of_payment, '') = %(mode_of_payment)s" + if filters.get("cost_center"): + conditions += """ and exists(select name from `tabPurchase Invoice Item` + where parent=`tabPurchase Invoice`.name + and ifnull(`tabPurchase Invoice Item`.cost_center, '') = %(cost_center)s)""" + + if filters.get("warehouse"): + conditions += """ and exists(select name from `tabPurchase Invoice Item` + where parent=`tabPurchase Invoice`.name + and ifnull(`tabPurchase Invoice Item`.warehouse, '') = %(warehouse)s)""" + + if filters.get("item_group"): + conditions += """ and exists(select name from `tabPurchase Invoice Item` + where parent=`tabPurchase Invoice`.name + and ifnull(`tabPurchase Invoice Item`.item_group, '') = %(item_group)s)""" + + accounting_dimensions = get_accounting_dimensions(as_list=False) + + if accounting_dimensions: + common_condition = """ + and exists(select name from `tabPurchase Invoice Item` + where parent=`tabPurchase Invoice`.name + """ + for dimension in accounting_dimensions: + if filters.get(dimension.fieldname): + if frappe.get_cached_value('DocType', dimension.document_type, 'is_tree'): + filters[dimension.fieldname] = get_dimension_with_children(dimension.document_type, + filters.get(dimension.fieldname)) + + conditions += common_condition + "and ifnull(`tabPurchase Invoice Item`.{0}, '') in %({0})s)".format(dimension.fieldname) + else: + conditions += common_condition + "and ifnull(`tabPurchase Invoice Item`.{0}, '') in (%({0})s))".format(dimension.fieldname) + return conditions def get_invoices(filters, additional_query_columns): diff --git a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json index c409c1f46e0..5a964485a5d 100644 --- a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json +++ b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json @@ -698,8 +698,7 @@ { "fieldname": "manufacturer_part_no", "fieldtype": "Data", - "label": "Manufacturer Part Number", - "read_only": 1 + "label": "Manufacturer Part Number" }, { "default": "0", @@ -712,7 +711,7 @@ ], "idx": 1, "istable": 1, - "modified": "2019-11-07 17:19:12.090355", + "modified": "2020-04-07 18:35:17.558928", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order Item", diff --git a/erpnext/buying/doctype/supplier_quotation_item/supplier_quotation_item.json b/erpnext/buying/doctype/supplier_quotation_item/supplier_quotation_item.json index 7d7d6f4d3db..b50e834ec73 100644 --- a/erpnext/buying/doctype/supplier_quotation_item/supplier_quotation_item.json +++ b/erpnext/buying/doctype/supplier_quotation_item/supplier_quotation_item.json @@ -1,4 +1,5 @@ { + "actions": [], "autoname": "hash", "creation": "2013-05-22 12:43:10", "doctype": "DocType", @@ -522,8 +523,7 @@ { "fieldname": "manufacturer_part_no", "fieldtype": "Data", - "label": "Manufacturer Part Number", - "read_only": 1 + "label": "Manufacturer Part Number" }, { "fieldname": "column_break_15", @@ -532,7 +532,8 @@ ], "idx": 1, "istable": 1, - "modified": "2019-06-02 05:32:46.019237", + "links": [], + "modified": "2020-04-07 18:35:51.175947", "modified_by": "Administrator", "module": "Buying", "name": "Supplier Quotation Item", diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index c17927a35c0..95fe6a2f3fa 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1123,36 +1123,39 @@ def get_supplier_block_status(party_name): } return info -def set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, item_code): +def set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, trans_item): """ Returns a Sales Order Item child item containing the default values """ p_doc = frappe.get_doc(parent_doctype, parent_doctype_name) child_item = frappe.new_doc('Sales Order Item', p_doc, child_docname) - item = frappe.get_doc("Item", item_code) + item = frappe.get_doc("Item", trans_item.get('item_code')) child_item.item_code = item.item_code child_item.item_name = item.item_name child_item.description = item.description - child_item.reqd_by_date = p_doc.delivery_date + child_item.delivery_date = trans_item.get('delivery_date') or p_doc.delivery_date child_item.uom = item.stock_uom - child_item.conversion_factor = get_conversion_factor(item_code, item.stock_uom).get("conversion_factor") or 1.0 + child_item.conversion_factor = get_conversion_factor(item.item_code, item.stock_uom).get("conversion_factor") or 1.0 child_item.warehouse = get_item_warehouse(item, p_doc, overwrite_warehouse=True) + if not child_item.warehouse: + frappe.throw(_("Cannot find {} for item {}. Please set the same in Item Master or Stock Settings.") + .format(frappe.bold("default warehouse"), frappe.bold(item.item_code))) return child_item -def set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docname, item_code): +def set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docname, trans_item): """ Returns a Purchase Order Item child item containing the default values """ p_doc = frappe.get_doc(parent_doctype, parent_doctype_name) child_item = frappe.new_doc('Purchase Order Item', p_doc, child_docname) - item = frappe.get_doc("Item", item_code) + item = frappe.get_doc("Item", trans_item.get('item_code')) child_item.item_code = item.item_code child_item.item_name = item.item_name child_item.description = item.description - child_item.schedule_date = p_doc.schedule_date + child_item.schedule_date = trans_item.get('schedule_date') or p_doc.schedule_date child_item.uom = item.stock_uom - child_item.conversion_factor = get_conversion_factor(item_code, item.stock_uom).get("conversion_factor") or 1.0 + child_item.conversion_factor = get_conversion_factor(item.item_code, item.stock_uom).get("conversion_factor") or 1.0 child_item.base_rate = 1 # Initiallize value will update in parent validation child_item.base_amount = 1 # Initiallize value will update in parent validation return child_item @@ -1196,9 +1199,9 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil if not d.get("docname"): new_child_flag = True if parent_doctype == "Sales Order": - child_item = set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, d.get("item_code")) + child_item = set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, d) if parent_doctype == "Purchase Order": - child_item = set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docname, d.get("item_code")) + child_item = set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docname, d) else: child_item = frappe.get_doc(parent_doctype + ' Item', d.get("docname")) if flt(child_item.get("rate")) == flt(d.get("rate")) and flt(child_item.get("qty")) == flt(d.get("qty")): @@ -1243,6 +1246,7 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil child_item.flags.ignore_validate_update_after_submit = True if new_child_flag: + parent.load_from_db() child_item.idx = len(parent.items) + 1 child_item.insert() else: diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index 9dbd5be9188..9a9f3d1d319 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -148,13 +148,6 @@ class SellingController(StockController): if sales_team and total != 100.0: throw(_("Total allocated percentage for sales team should be 100")) - def validate_order_type(self): - valid_types = ["Sales", "Maintenance", "Shopping Cart"] - if not self.order_type: - self.order_type = "Sales" - elif self.order_type not in valid_types: - throw(_("Order Type must be one of {0}").format(comma_or(valid_types))) - def validate_max_discount(self): for d in self.get("items"): if d.item_code: diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.json b/erpnext/hr/doctype/expense_claim/expense_claim.json index 5c2f4901713..770b47709dd 100644 --- a/erpnext/hr/doctype/expense_claim/expense_claim.json +++ b/erpnext/hr/doctype/expense_claim/expense_claim.json @@ -278,6 +278,7 @@ "label": "More Details" }, { + "allow_on_submit": 1, "default": "Draft", "fieldname": "status", "fieldtype": "Select", @@ -366,7 +367,8 @@ "icon": "fa fa-money", "idx": 1, "is_submittable": 1, - "modified": "2019-11-08 14:13:08.964547", + "links": [], + "modified": "2020-03-16 18:11:07.861985", "modified_by": "Administrator", "module": "HR", "name": "Expense Claim", diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.py b/erpnext/hr/doctype/expense_claim/expense_claim.py index d29b3f75879..fefec01130a 100644 --- a/erpnext/hr/doctype/expense_claim/expense_claim.py +++ b/erpnext/hr/doctype/expense_claim/expense_claim.py @@ -45,10 +45,10 @@ class ExpenseClaim(AccountsController): paid_amount = flt(self.total_amount_reimbursed) + flt(self.total_advance_amount) precision = self.precision("grand_total") if (self.is_paid or (flt(self.total_sanctioned_amount) > 0 - and flt(self.grand_total, precision) == flt(paid_amount, precision))) \ + and flt(flt(self.total_sanctioned_amount) + flt(self.total_taxes_and_charges), precision) == flt(paid_amount, precision))) \ and self.docstatus == 1 and self.approval_status == 'Approved': self.status = "Paid" - elif flt(self.total_sanctioned_amount) > 0 and self.docstatus == 1 and self.approval_status == 'Approved': + elif flt(self.grand_total) > 0 and self.docstatus == 1 and self.approval_status == 'Approved': self.status = "Unpaid" elif self.docstatus == 1 and self.approval_status == 'Rejected': self.status = 'Rejected' diff --git a/erpnext/hr/doctype/expense_claim/test_expense_claim.py b/erpnext/hr/doctype/expense_claim/test_expense_claim.py index b559dfd81d1..7a7946d8ddd 100644 --- a/erpnext/hr/doctype/expense_claim/test_expense_claim.py +++ b/erpnext/hr/doctype/expense_claim/test_expense_claim.py @@ -34,12 +34,14 @@ class TestExpenseClaim(unittest.TestCase): task_name = task.name payable_account = get_payable_account(company_name) - make_expense_claim(payable_account, 300, 200, company_name, "Travel Expenses - _TC4", "_Test Project 1", task_name) + make_expense_claim(payable_account, 300, 200, company_name, + "Travel Expenses - _TC4", "_Test Project 1", task_name, cost_center="Main - _TC4") self.assertEqual(frappe.db.get_value("Task", task_name, "total_expense_claim"), 200) self.assertEqual(frappe.db.get_value("Project", "_Test Project 1", "total_expense_claim"), 200) - expense_claim2 = make_expense_claim(payable_account, 600, 500, company_name, "Travel Expenses - _TC4","_Test Project 1", task_name) + expense_claim2 = make_expense_claim(payable_account, 600, 500, company_name, + "Travel Expenses - _TC4","_Test Project 1", task_name, cost_center="Main - _TC4") self.assertEqual(frappe.db.get_value("Task", task_name, "total_expense_claim"), 700) self.assertEqual(frappe.db.get_value("Project", "_Test Project 1", "total_expense_claim"), 700) @@ -51,7 +53,8 @@ class TestExpenseClaim(unittest.TestCase): def test_expense_claim_status(self): payable_account = get_payable_account(company_name) - expense_claim = make_expense_claim(payable_account, 300, 200, company_name, "Travel Expenses - _TC4") + expense_claim = make_expense_claim(payable_account, 300, 200, company_name, + "Travel Expenses - _TC4", cost_center="Main - _TC4") je_dict = make_bank_entry("Expense Claim", expense_claim.name) je = frappe.get_doc(je_dict) @@ -70,7 +73,8 @@ class TestExpenseClaim(unittest.TestCase): def test_expense_claim_gl_entry(self): payable_account = get_payable_account(company_name) taxes = generate_taxes() - expense_claim = make_expense_claim(payable_account, 300, 200, company_name, "Travel Expenses - _TC4", do_not_submit=True, taxes=taxes) + expense_claim = make_expense_claim(payable_account, 300, 200, company_name, + "Travel Expenses - _TC4", do_not_submit=True, taxes=taxes, cost_center="Main - _TC4") expense_claim.submit() gl_entries = frappe.db.sql("""select account, debit, credit @@ -124,9 +128,10 @@ def generate_taxes(): "total": 210 }]} -def make_expense_claim(payable_account, amount, sanctioned_amount, company, account, project=None, task_name=None, do_not_submit=False, taxes=None): +def make_expense_claim(payable_account, amount, sanctioned_amount, company, account, + project=None, task_name=None, do_not_submit=False, taxes=None, cost_center=None): employee = frappe.db.get_value("Employee", {"status": "Active"}) - currency = frappe.db.get_value('Company', company, 'default_currency') + currency, company_cost_center = frappe.db.get_value('Company', company, ['default_currency', 'cost_center']) expense_claim = { "doctype": "Expense Claim", "employee": employee, @@ -134,12 +139,15 @@ def make_expense_claim(payable_account, amount, sanctioned_amount, company, acco "approval_status": "Approved", "company": company, 'currency': currency, - "expenses": - [{"expense_type": "Travel", + "expenses": [{ + "expense_type": "Travel", "default_account": account, 'currency': currency, "amount": amount, - "sanctioned_amount": sanctioned_amount}]} + "sanctioned_amount": sanctioned_amount, + "cost_center": cost_center or company_cost_center + }] + } if taxes: expense_claim.update(taxes) diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index a800923a8c2..c870cc5d5d0 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -575,19 +575,22 @@ def get_leaves_for_period(employee, leave_type, from_date, to_date): return leave_days def skip_expiry_leaves(leave_entry, date): - ''' Checks whether the expired leaves coincide with the to_date of leave balance check ''' + ''' Checks whether the expired leaves coincide with the to_date of leave balance check. + This allows backdated leave entry creation for non carry forwarded allocation ''' end_date = frappe.db.get_value("Leave Allocation", {'name': leave_entry.transaction_name}, ['to_date']) return True if end_date == date and not leave_entry.is_carry_forward else False def get_leave_entries(employee, leave_type, from_date, to_date): - ''' Returns leave entries between from_date and to_date ''' + ''' Returns leave entries between from_date and to_date. ''' return frappe.db.sql(""" SELECT employee, leave_type, from_date, to_date, leaves, transaction_name, transaction_type, holiday_list, is_carry_forward, is_expired FROM `tabLeave Ledger Entry` WHERE employee=%(employee)s AND leave_type=%(leave_type)s - AND docstatus=1 AND leaves<0 + AND docstatus=1 + AND (leaves<0 + OR is_expired=1) AND (from_date between %(from_date)s AND %(to_date)s OR to_date between %(from_date)s AND %(to_date)s OR (from_date < %(from_date)s AND to_date > %(to_date)s)) diff --git a/erpnext/hr/doctype/leave_encashment/leave_encashment.py b/erpnext/hr/doctype/leave_encashment/leave_encashment.py index ad2cc02fd7c..7d6fd422c0f 100644 --- a/erpnext/hr/doctype/leave_encashment/leave_encashment.py +++ b/erpnext/hr/doctype/leave_encashment/leave_encashment.py @@ -8,7 +8,6 @@ from frappe import _ from frappe.model.document import Document from frappe.utils import getdate, nowdate, flt from erpnext.hr.utils import set_employee_name -from erpnext.hr.doctype.leave_application.leave_application import get_leave_balance_on from erpnext.hr.doctype.salary_structure_assignment.salary_structure_assignment import get_assigned_salary_structure from erpnext.hr.doctype.leave_ledger_entry.leave_ledger_entry import create_leave_ledger_entry from erpnext.hr.doctype.leave_allocation.leave_allocation import get_unused_leaves diff --git a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py index bfc6d95d172..9ed58c9e59b 100644 --- a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py +++ b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py @@ -141,6 +141,7 @@ def expire_allocation(allocation, expiry_date=None): leaves = get_remaining_leaves(allocation) expiry_date = expiry_date if expiry_date else allocation.to_date + # allows expired leaves entry to be created/reverted if leaves: args = dict( leaves=flt(leaves) * -1, @@ -160,6 +161,8 @@ def expire_carried_forward_allocation(allocation): from erpnext.hr.doctype.leave_application.leave_application import get_leaves_for_period leaves_taken = get_leaves_for_period(allocation.employee, allocation.leave_type, allocation.from_date, allocation.to_date) leaves = flt(allocation.leaves) + flt(leaves_taken) + + # allow expired leaves entry to be created if leaves > 0: args = frappe._dict( transaction_name=allocation.name, diff --git a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py b/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py index e967db87c4a..35c8630e8e2 100644 --- a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py +++ b/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py @@ -76,7 +76,7 @@ def get_data(filters, leave_types): opening = get_leave_balance_on(employee.name, leave_type, filters.from_date) # closing balance - closing = get_leave_balance_on(employee.name, leave_type, filters.to_date) + closing = max(opening - leaves_taken, 0) row += [opening, leaves_taken, closing] diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 742f19f3b41..7cb92d228d9 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -655,3 +655,4 @@ erpnext.patches.v12_0.set_permission_einvoicing erpnext.patches.v12_0.set_received_qty_in_material_request_as_per_stock_uom erpnext.patches.v12_0.recalculate_requested_qty_in_bin erpnext.patches.v12_0.rename_mws_settings_fields +erpnext.patches.v12_0.set_correct_status_for_expense_claim diff --git a/erpnext/patches/v12_0/set_correct_status_for_expense_claim.py b/erpnext/patches/v12_0/set_correct_status_for_expense_claim.py new file mode 100644 index 00000000000..1fefc90680e --- /dev/null +++ b/erpnext/patches/v12_0/set_correct_status_for_expense_claim.py @@ -0,0 +1,11 @@ +# Copyright (c) 2020, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +import frappe + +def execute(): + frappe.db.sql(""" + update `tabExpense Claim` + set status = 'Paid' + where total_advance_amount + total_amount_reimbursed = total_sanctioned_amount + total_taxes_and_charges + """) \ No newline at end of file diff --git a/erpnext/projects/doctype/project/project.js b/erpnext/projects/doctype/project/project.js index 3570a0f2be4..9ba9ccfb4e8 100644 --- a/erpnext/projects/doctype/project/project.js +++ b/erpnext/projects/doctype/project/project.js @@ -118,6 +118,11 @@ frappe.ui.form.on("Project", { }); }, + collect_progress: function(frm) { + if (frm.doc.collect_progress) { + frm.set_df_property("message", "reqd", 1); + } + } }); function open_form(frm, doctype, child_doctype, parentfield) { diff --git a/erpnext/projects/doctype/project/project.json b/erpnext/projects/doctype/project/project.json index 7d47db371d5..316fb1a4738 100644 --- a/erpnext/projects/doctype/project/project.json +++ b/erpnext/projects/doctype/project/project.json @@ -435,7 +435,7 @@ }, { "depends_on": "collect_progress", - "description": "Message will sent to users to get their status on the project", + "description": "Message will be sent to the users to get their status on the Project", "fieldname": "message", "fieldtype": "Text", "label": "Message" @@ -444,7 +444,7 @@ "icon": "fa fa-puzzle-piece", "idx": 29, "max_attachments": 4, - "modified": "2019-09-24 15:02:50.208301", + "modified": "2020-04-09 14:17:15.524529", "modified_by": "Administrator", "module": "Projects", "name": "Project", diff --git a/erpnext/public/js/controllers/buying.js b/erpnext/public/js/controllers/buying.js index 27a9de95e0e..afbdbc661d3 100644 --- a/erpnext/public/js/controllers/buying.js +++ b/erpnext/public/js/controllers/buying.js @@ -379,7 +379,31 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({ } }); } - } + }, + + manufacturer_part_no: function(doc, cdt, cdn) { + const row = locals[cdt][cdn]; + + if (row.manufacturer_part_no) { + frappe.model.get_value('Item Manufacturer', + { + 'item_code': row.item_code, + 'manufacturer': row.manufacturer, + 'manufacturer_part_no': row.manufacturer_part_no + }, + 'name', + function(data) { + if (!data) { + let msg = { + message: __("Manufacturer Part Number {0} is invalid", [row.manufacturer_part_no]), + title: __("Invalid Part Number") + } + frappe.throw(msg); + } + }); + + } + } }); cur_frm.add_fetch('project', 'cost_center', 'cost_center'); diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 6f0100ff7a8..5312bc06ecc 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -546,7 +546,13 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ } }, () => me.conversion_factor(doc, cdt, cdn, true), - () => me.remove_pricing_rule(item) + () => me.remove_pricing_rule(item), + () => { + if (item.apply_rule_on_other_items) { + let key = item.name; + me.apply_rule_on_other_items({key: item}); + } + } ]); } } @@ -1356,20 +1362,22 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ apply_rule_on_other_items: function(args) { const me = this; - const fields = ["discount_percentage", "discount_amount", "rate"]; + const fields = ["discount_percentage", "pricing_rules", "discount_amount", "rate"]; for(var k in args) { let data = args[k]; - me.frm.doc.items.forEach(d => { - if (in_list(data.apply_rule_on_other_items, d[data.apply_rule_on])) { - for(var k in data) { - if (in_list(fields, k)) { - frappe.model.set_value(d.doctype, d.name, k, data[k]); + if (data && data.apply_rule_on_other_items) { + me.frm.doc.items.forEach(d => { + if (in_list(data.apply_rule_on_other_items, d[data.apply_rule_on])) { + for(var k in data) { + if (in_list(fields, k) && data[k]) { + frappe.model.set_value(d.doctype, d.name, k, data[k]); + } } } - } - }); + }); + } } }, diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index 0a352648304..224f4f5525d 100755 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -436,6 +436,44 @@ erpnext.utils.update_child_items = function(opts) { const cannot_add_row = (typeof opts.cannot_add_row === 'undefined') ? true : opts.cannot_add_row; const child_docname = (typeof opts.cannot_add_row === 'undefined') ? "items" : opts.child_docname; this.data = []; + const fields = [{ + fieldtype:'Data', + fieldname:"docname", + read_only: 1, + hidden: 1, + }, { + fieldtype:'Link', + fieldname:"item_code", + options: 'Item', + in_list_view: 1, + read_only: 0, + disabled: 0, + label: __('Item Code') + }, { + fieldtype:'Float', + fieldname:"qty", + default: 0, + read_only: 0, + in_list_view: 1, + label: __('Qty') + }, { + fieldtype:'Currency', + fieldname:"rate", + default: 0, + read_only: 0, + in_list_view: 1, + label: __('Rate') + }]; + + if (frm.doc.doctype == 'Sales Order' || frm.doc.doctype == 'Purchase Order' ) { + fields.splice(2, 0, { + fieldtype: 'Date', + fieldname: frm.doc.doctype == 'Sales Order' ? "delivery_date" : "schedule_date", + in_list_view: 1, + label: frm.doc.doctype == 'Sales Order' ? __("Delivery Date") : __("Reqd by date") + }) + } + const dialog = new frappe.ui.Dialog({ title: __("Update Items"), fields: [ @@ -450,34 +488,7 @@ erpnext.utils.update_child_items = function(opts) { get_data: () => { return this.data; }, - fields: [{ - fieldtype:'Data', - fieldname:"docname", - read_only: 1, - hidden: 1, - }, { - fieldtype:'Link', - fieldname:"item_code", - options: 'Item', - in_list_view: 1, - read_only: 0, - disabled: 0, - label: __('Item Code') - }, { - fieldtype:'Float', - fieldname:"qty", - default: 0, - read_only: 0, - in_list_view: 1, - label: __('Qty') - }, { - fieldtype:'Currency', - fieldname:"rate", - default: 0, - read_only: 0, - in_list_view: 1, - label: __('Rate') - }] + fields: fields }, ], primary_action: function() { @@ -506,6 +517,8 @@ erpnext.utils.update_child_items = function(opts) { "docname": d.name, "name": d.name, "item_code": d.item_code, + "delivery_date": d.delivery_date, + "schedule_date": d.schedule_date, "qty": d.qty, "rate": d.rate, }); diff --git a/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py b/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py index 79dace7925e..15dcbd5e154 100644 --- a/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py +++ b/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py @@ -277,7 +277,7 @@ class GSTR3BReport(Document): def get_itc_details(self, reverse_charge='N'): itc_amount = frappe.db.sql(""" - select s.gst_category, sum(t.tax_amount) as tax_amount, t.account_head, s.eligibility_for_itc, s.reverse_charge + select s.gst_category, sum(t.tax_amount_after_discount_amount) as tax_amount, t.account_head, s.eligibility_for_itc, s.reverse_charge from `tabPurchase Invoice` s , `tabPurchase Taxes and Charges` t where s.docstatus = 1 and t.parent = s.name and s.reverse_charge = %s and month(s.posting_date) = %s and year(s.posting_date) = %s and s.company = %s @@ -312,7 +312,7 @@ class GSTR3BReport(Document): and s.company = %s and s.company_gstin = %s and s.gst_category in ('Unregistered', 'Registered Composition', 'UIN Holders') group by s.gst_category, s.place_of_supply""", (self.month_no, self.year, self.company, self.gst_details.get("gstin")), as_dict=1) - inter_state_supply_tax = frappe.db.sql(""" select sum(t.tax_amount) as tax_amount, s.place_of_supply, s.gst_category + inter_state_supply_tax = frappe.db.sql(""" select sum(t.tax_amount_after_discount_amount) as tax_amount, s.place_of_supply, s.gst_category from `tabSales Invoice` s, `tabSales Taxes and Charges` t where t.parent = s.name and s.docstatus = 1 and month(s.posting_date) = %s and year(s.posting_date) = %s and s.company = %s and s.company_gstin = %s and s.gst_category in ('Unregistered', 'Registered Composition', 'UIN Holders') @@ -385,7 +385,7 @@ class GSTR3BReport(Document): tax_template = 'Purchase Taxes and Charges' tax_amounts = frappe.db.sql(""" - select s.gst_category, sum(t.tax_amount) as tax_amount, t.account_head + select s.gst_category, sum(t.tax_amount_after_discount_amount) as tax_amount, t.account_head from `tab{doctype}` s , `tab{template}` t where s.docstatus = 1 and t.parent = s.name and s.reverse_charge = %s and month(s.posting_date) = %s and year(s.posting_date) = %s and s.company = %s diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index 4fca05ba7ea..493d8730602 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -150,7 +150,7 @@ class Customer(TransactionBase): contact.save() else: - lead.lead_name = lead.lead_name.split(" ") + lead.lead_name = lead.lead_name.lstrip().split(" ") lead.first_name = lead.lead_name[0] lead.last_name = " ".join(lead.lead_name[1:]) diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index 00471c762c8..1571c734b7f 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -26,7 +26,6 @@ class Quotation(SellingController): super(Quotation, self).validate() self.set_status() self.update_opportunity() - self.validate_order_type() self.validate_uom_is_integer("stock_uom", "qty") self.validate_valid_till() self.set_customer_name() @@ -40,9 +39,6 @@ class Quotation(SellingController): def has_sales_order(self): return frappe.db.get_value("Sales Order Item", {"prevdoc_docname": self.name, "docstatus": 1}) - def validate_order_type(self): - super(Quotation, self).validate_order_type() - def update_lead(self): if self.quotation_to == "Lead" and self.party_name: frappe.get_doc("Lead", self.party_name).set_status(update=True) diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 19cf65dee86..ef2d19ac546 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -34,7 +34,6 @@ class SalesOrder(SellingController): def validate(self): super(SalesOrder, self).validate() - self.validate_order_type() self.validate_delivery_date() self.validate_proj_cust() self.validate_po() @@ -100,9 +99,6 @@ class SalesOrder(SellingController): frappe.msgprint(_("Quotation {0} not of type {1}") .format(d.prevdoc_docname, self.order_type)) - def validate_order_type(self): - super(SalesOrder, self).validate_order_type() - def validate_delivery_date(self): if self.order_type == 'Sales' and not self.skip_delivery_note: delivery_date_list = [d.delivery_date for d in self.get("items") if d.delivery_date] diff --git a/erpnext/selling/page/point_of_sale/point_of_sale.js b/erpnext/selling/page/point_of_sale/point_of_sale.js index 33fbc229b6e..0b93324b19d 100644 --- a/erpnext/selling/page/point_of_sale/point_of_sale.js +++ b/erpnext/selling/page/point_of_sale/point_of_sale.js @@ -1093,7 +1093,7 @@ class POSCart { return `
+ data-batch-no="${batch_no}" title="Item: ${item.item_name} Available Qty: ${item.actual_qty} ${item.stock_uom}">
${item.item_name}
diff --git a/erpnext/selling/page/point_of_sale/point_of_sale.py b/erpnext/selling/page/point_of_sale/point_of_sale.py index bef2c53d6d4..5eb35d642b9 100644 --- a/erpnext/selling/page/point_of_sale/point_of_sale.py +++ b/erpnext/selling/page/point_of_sale/point_of_sale.py @@ -37,20 +37,33 @@ def get_items(start, page_length, price_list, item_group, search_value="", pos_p lft, rgt = frappe.db.get_value('Item Group', item_group, ['lft', 'rgt']) # locate function is used to sort by closest match from the beginning of the value - result = [] - items_data = frappe.db.sql(""" SELECT name as item_code, - item_name, image as item_image, idx as idx,is_stock_item + items_data = frappe.db.sql(""" + SELECT + name AS item_code, + item_name, + stock_uom, + image AS item_image, + idx AS idx, + is_stock_item FROM `tabItem` WHERE - disabled = 0 and has_variants = 0 and is_sales_item = 1 - and item_group in (select name from `tabItem Group` where lft >= {lft} and rgt <= {rgt}) - and {condition} order by idx desc limit {start}, {page_length}""" + disabled = 0 + AND has_variants = 0 + AND is_sales_item = 1 + AND item_group in (SELECT name FROM `tabItem Group` WHERE lft >= {lft} AND rgt <= {rgt}) + AND {condition} + ORDER BY + idx desc + LIMIT + {start}, {page_length}""" .format( - start=start, page_length=page_length, - lft=lft, rgt=rgt, + start=start, + page_length=page_length, + lft=lft, + rgt=rgt, condition=condition ), as_dict=1) diff --git a/erpnext/stock/doctype/item/item.json b/erpnext/stock/doctype/item/item.json index aa6b2fedd7c..7d2e3112fb3 100644 --- a/erpnext/stock/doctype/item/item.json +++ b/erpnext/stock/doctype/item/item.json @@ -114,6 +114,8 @@ "is_sub_contracted_item", "column_break_74", "customer_code", + "default_item_manufacturer", + "default_manufacturer_part_no", "website_section", "show_in_website", "show_variant_in_website", @@ -1038,6 +1040,18 @@ "fieldname": "auto_create_assets", "fieldtype": "Check", "label": "Auto Create Assets on Purchase" + }, + { + "fieldname": "default_item_manufacturer", + "fieldtype": "Data", + "label": "Default Item Manufacturer", + "read_only": 1 + }, + { + "fieldname": "default_manufacturer_part_no", + "fieldtype": "Data", + "label": "Default Manufacturer Part No", + "read_only": 1 } ], "has_web_view": 1, @@ -1046,7 +1060,7 @@ "image_field": "image", "links": [], "max_attachments": 1, - "modified": "2020-03-24 16:14:36.950677", + "modified": "2020-04-07 15:56:06.195722", "modified_by": "Administrator", "module": "Stock", "name": "Item", diff --git a/erpnext/stock/doctype/item_manufacturer/item_manufacturer.json b/erpnext/stock/doctype/item_manufacturer/item_manufacturer.json index 956c92e6738..0cef6eafaef 100644 --- a/erpnext/stock/doctype/item_manufacturer/item_manufacturer.json +++ b/erpnext/stock/doctype/item_manufacturer/item_manufacturer.json @@ -1,4 +1,5 @@ { + "actions": [], "allow_import": 1, "creation": "2019-06-02 04:41:37.332911", "doctype": "DocType", @@ -10,7 +11,8 @@ "manufacturer_part_no", "column_break_3", "item_name", - "description" + "description", + "is_default" ], "fields": [ { @@ -52,9 +54,17 @@ "fieldtype": "Small Text", "label": "Description", "read_only": 1 + }, + { + "default": "0", + "fieldname": "is_default", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Is Default" } ], - "modified": "2019-06-06 19:07:31.175919", + "links": [], + "modified": "2020-04-07 20:25:55.507905", "modified_by": "Administrator", "module": "Stock", "name": "Item Manufacturer", diff --git a/erpnext/stock/doctype/item_manufacturer/item_manufacturer.py b/erpnext/stock/doctype/item_manufacturer/item_manufacturer.py index 67eab82d970..c27d1be7892 100644 --- a/erpnext/stock/doctype/item_manufacturer/item_manufacturer.py +++ b/erpnext/stock/doctype/item_manufacturer/item_manufacturer.py @@ -11,6 +11,10 @@ from frappe.model.document import Document class ItemManufacturer(Document): def validate(self): self.validate_duplicate_entry() + self.manage_default_item_manufacturer() + + def on_trash(self): + self.manage_default_item_manufacturer(delete=True) def validate_duplicate_entry(self): if self.is_new(): @@ -24,6 +28,40 @@ class ItemManufacturer(Document): frappe.throw(_("Duplicate entry against the item code {0} and manufacturer {1}") .format(self.item_code, self.manufacturer)) + def manage_default_item_manufacturer(self, delete=False): + from frappe.model.utils import set_default + + item = frappe.get_doc("Item", self.item_code) + default_manufacturer = item.default_item_manufacturer + default_part_no = item.default_manufacturer_part_no + + if not self.is_default: + # if unchecked and default in Item master, clear it. + if default_manufacturer == self.manufacturer and default_part_no == self.manufacturer_part_no: + frappe.db.set_value("Item", item.name, + { + "default_item_manufacturer": None, + "default_manufacturer_part_no": None + }) + + elif self.is_default: + set_default(self, "item_code") + manufacturer, manufacturer_part_no = default_manufacturer, default_part_no + + if delete: + manufacturer, manufacturer_part_no = None, None + + elif (default_manufacturer != self.manufacturer) or \ + (default_manufacturer == self.manufacturer and default_part_no != self.manufacturer_part_no): + manufacturer = self.manufacturer + manufacturer_part_no = self.manufacturer_part_no + + frappe.db.set_value("Item", item.name, + { + "default_item_manufacturer": manufacturer, + "default_manufacturer_part_no": manufacturer_part_no + }) + @frappe.whitelist() def get_item_manufacturer_part_no(item_code, manufacturer): return frappe.db.get_value("Item Manufacturer", diff --git a/erpnext/stock/doctype/material_request_item/material_request_item.json b/erpnext/stock/doctype/material_request_item/material_request_item.json index 9d1dafb136b..30206b62d0c 100644 --- a/erpnext/stock/doctype/material_request_item/material_request_item.json +++ b/erpnext/stock/doctype/material_request_item/material_request_item.json @@ -404,14 +404,13 @@ { "fieldname": "manufacturer_part_no", "fieldtype": "Data", - "label": "Manufacturer Part Number", - "read_only": 1 + "label": "Manufacturer Part Number" } ], "idx": 1, "istable": 1, "links": [], - "modified": "2020-02-25 03:09:10.698967", + "modified": "2020-04-07 18:37:54.495112", "modified_by": "Administrator", "module": "Stock", "name": "Material Request Item", diff --git a/erpnext/stock/doctype/pick_list/test_pick_list.py b/erpnext/stock/doctype/pick_list/test_pick_list.py index 6b4f73b140a..1b9ff41cc33 100644 --- a/erpnext/stock/doctype/pick_list/test_pick_list.py +++ b/erpnext/stock/doctype/pick_list/test_pick_list.py @@ -111,6 +111,7 @@ class TestPickList(unittest.TestCase): stock_reconciliation = frappe.get_doc({ 'doctype': 'Stock Reconciliation', + 'purpose': 'Stock Reconciliation', 'company': '_Test Company', 'items': [{ 'item_code': '_Test Serialized Item', diff --git a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json index d5cf4cadb14..e1dc95be481 100644 --- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json +++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json @@ -799,8 +799,7 @@ { "fieldname": "manufacturer_part_no", "fieldtype": "Data", - "label": "Manufacturer Part Number", - "read_only": 1 + "label": "Manufacturer Part Number" }, { "depends_on": "is_fixed_asset", @@ -823,7 +822,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2020-03-05 14:19:48.799370", + "modified": "2020-04-07 18:38:21.141558", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt Item", diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js index b54233f4585..c54e3cdfb87 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.js +++ b/erpnext/stock/doctype/stock_entry/stock_entry.js @@ -220,8 +220,8 @@ frappe.ui.form.on('Stock Entry', { }, get_query_filters: { docstatus: 1, - material_request_type: "Material Transfer", - status: ['!=', 'Transferred'] + material_request_type: ["in", ["Material Transfer", "Material Issue"]], + status: ["not in", ["Transferred", "Issued"]] } }) }, __("Get items from")); diff --git a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py index 5fe89d6e227..837dfd5bd30 100644 --- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py +++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py @@ -64,7 +64,7 @@ class StockLedgerEntry(Document): frappe.throw(_("Actual Qty is mandatory")) def validate_item(self): - item_det = frappe.db.sql("""select name, has_batch_no, docstatus, + item_det = frappe.db.sql("""select name, item_name, has_batch_no, docstatus, is_stock_item, has_variants, stock_uom, create_new_batch from tabItem where name=%s""", self.item_code, as_dict=True) @@ -79,10 +79,11 @@ class StockLedgerEntry(Document): # check if batch number is required if self.voucher_type != 'Stock Reconciliation': if item_det.has_batch_no ==1: + batch_item = self.item_code if self.item_code == item_det.item_name else self.item_code + ":" + item_det.item_name if not self.batch_no: - frappe.throw(_("Batch number is mandatory for Item {0}").format(self.item_code)) + frappe.throw(_("Batch number is mandatory for Item {0}").format(batch_item)) elif not frappe.db.get_value("Batch",{"item": self.item_code, "name": self.batch_no}): - frappe.throw(_("{0} is not a valid Batch Number for Item {1}").format(self.batch_no, self.item_code)) + frappe.throw(_("{0} is not a valid Batch Number for Item {1}").format(self.batch_no, batch_item)) elif item_det.has_batch_no ==0 and self.batch_no and self.is_cancelled == "No": frappe.throw(_("The Item {0} cannot have Batch").format(self.item_code)) diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.json b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.json index 7f4efba33f8..b7d1497319f 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.json +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.json @@ -4,6 +4,7 @@ "description": "This tool helps you to update or fix the quantity and valuation of stock in the system. It is typically used to synchronise the system values and what actually exists in your warehouses.", "doctype": "DocType", "document_type": "Document", + "engine": "InnoDB", "field_order": [ "naming_series", "company", @@ -44,11 +45,11 @@ "reqd": 1 }, { - "default": "Stock Reconciliation", "fieldname": "purpose", "fieldtype": "Select", "label": "Purpose", - "options": "Opening Stock\nStock Reconciliation" + "options": "\nOpening Stock\nStock Reconciliation", + "reqd": 1 }, { "fieldname": "col1", @@ -153,7 +154,7 @@ "idx": 1, "is_submittable": 1, "max_attachments": 1, - "modified": "2019-05-26 09:03:09.542141", + "modified": "2020-04-08 17:02:47.196206", "modified_by": "Administrator", "module": "Stock", "name": "Stock Reconciliation", diff --git a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py index e6d7e3fea7d..51d027f22ef 100644 --- a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py @@ -240,6 +240,7 @@ def create_batch_or_serial_no_items(): def create_stock_reconciliation(**args): args = frappe._dict(args) sr = frappe.new_doc("Stock Reconciliation") + sr.purpose = args.purpose or "Stock Reconciliation" sr.posting_date = args.posting_date or nowdate() sr.posting_time = args.posting_time or nowtime() sr.set_posting_time = 1 diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index 54dc685e4ef..51f27fd3e78 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -339,6 +339,9 @@ def get_basic_details(args, item, overwrite_warehouse=True): else: out["manufacturer_part_no"] = None out["manufacturer"] = None + else: + out["manufacturer"], out["manufacturer_part_no"] = frappe.get_value("Item", item.name, + ["default_item_manufacturer", "default_manufacturer_part_no"] ) child_doctype = args.doctype + ' Item' meta = frappe.get_meta(child_doctype)