diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py index 7cd1e7736c8..fac28c92397 100644 --- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py +++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py @@ -19,7 +19,7 @@ class AccountingDimension(Document): def validate(self): if self.document_type in core_doctypes_list + ('Accounting Dimension', 'Project', - 'Cost Center', 'Accounting Dimension Detail', 'Company') : + 'Cost Center', 'Accounting Dimension Detail', 'Company', 'Account') : msg = _("Not allowed to create accounting dimension for {0}").format(self.document_type) frappe.throw(msg) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.json b/erpnext/accounts/doctype/payment_entry/payment_entry.json index 54623dd6cd5..51f18a5a4e3 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.json +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.json @@ -690,7 +690,7 @@ "options": "Account" }, { - "depends_on": "eval:doc.received_amount", + "depends_on": "eval:doc.received_amount && doc.payment_type != 'Internal Transfer'", "fieldname": "received_amount_after_tax", "fieldtype": "Currency", "label": "Received Amount After Tax", @@ -707,7 +707,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2021-06-09 11:55:04.215050", + "modified": "2021-06-22 20:37:06.154206", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Entry", diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index b6b2bef9633..adaf99a7900 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -706,7 +706,7 @@ class PaymentEntry(AccountsController): if account_currency != self.company_currency: frappe.throw(_("Currency for {0} must be {1}").format(d.account_head, self.company_currency)) - if self.payment_type == 'Pay': + if self.payment_type in ('Pay', 'Internal Transfer'): dr_or_cr = "debit" if d.add_deduct_tax == "Add" else "credit" elif self.payment_type == 'Receive': dr_or_cr = "credit" if d.add_deduct_tax == "Add" else "debit" @@ -761,7 +761,7 @@ class PaymentEntry(AccountsController): return self.advance_tax_account elif self.payment_type == 'Receive': return self.paid_from - elif self.payment_type == 'Pay': + elif self.payment_type in ('Pay', 'Internal Transfer'): return self.paid_to def update_advance_paid(self): diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index ff433b962ff..2f5d36c8fab 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -966,7 +966,7 @@ class TestPurchaseInvoice(unittest.TestCase): update_tax_witholding_category('_Test Company', 'TDS Payable - _TC', nowdate()) # Create Purchase Order with TDS applied - po = create_purchase_order(do_not_save=1, supplier=supplier.name, rate=3000) + po = create_purchase_order(do_not_save=1, supplier=supplier.name, rate=3000, item='_Test Non Stock Item') po.apply_tds = 1 po.tax_withholding_category = 'TDS - 194 - Dividends - Individual' po.save() @@ -1002,6 +1002,7 @@ class TestPurchaseInvoice(unittest.TestCase): # Create Purchase Invoice against Purchase Order purchase_invoice = get_mapped_purchase_invoice(po.name) purchase_invoice.allocate_advances_automatically = 1 + purchase_invoice.items[0].item_code = '_Test Non Stock Item' purchase_invoice.items[0].expense_account = '_Test Account Cost for Goods Sold - _TC' purchase_invoice.save() purchase_invoice.submit() diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py index 03808c3640c..744ada9e558 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.py +++ b/erpnext/accounts/report/general_ledger/general_ledger.py @@ -222,7 +222,7 @@ def get_gl_entries(filters, accounting_dimensions): def get_conditions(filters): conditions = [] - if filters.get("account") and not filters.get("include_dimensions"): + if filters.get("account"): filters.account = get_accounts_with_children(filters.account) conditions.append("account in %(account)s") diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js index e5a5fcfe3b7..1de9ec1a7df 100644 --- a/erpnext/public/js/controllers/taxes_and_totals.js +++ b/erpnext/public/js/controllers/taxes_and_totals.js @@ -270,11 +270,14 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ let me = this; let item_codes = []; let item_rates = {}; + let item_tax_templates = {}; + $.each(this.frm.doc.items || [], function(i, item) { if (item.item_code) { // Use combination of name and item code in case same item is added multiple times item_codes.push([item.item_code, item.name]); item_rates[item.name] = item.net_rate; + item_tax_templates[item.name] = item.item_tax_template; } }); @@ -285,18 +288,16 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ company: me.frm.doc.company, tax_category: cstr(me.frm.doc.tax_category), item_codes: item_codes, - item_rates: item_rates + item_rates: item_rates, + item_tax_templates: item_tax_templates }, callback: function(r) { if (!r.exc) { $.each(me.frm.doc.items || [], function(i, item) { - if (item.name && r.message.hasOwnProperty(item.name)) { + if (item.name && r.message.hasOwnProperty(item.name) && r.message[item.name].item_tax_template) { item.item_tax_template = r.message[item.name].item_tax_template; item.item_tax_rate = r.message[item.name].item_tax_rate; me.add_taxes_from_item_tax_template(item.item_tax_rate); - } else { - item.item_tax_template = ""; - item.item_tax_rate = "{}"; } }); } diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py index 27e023c1e5b..0427abe558d 100644 --- a/erpnext/setup/doctype/company/company.py +++ b/erpnext/setup/doctype/company/company.py @@ -407,8 +407,6 @@ def replace_abbr(company, old, new): frappe.only_for("System Manager") - frappe.db.set_value("Company", company, "abbr", new) - def _rename_record(doc): parts = doc[0].rsplit(" - ", 1) if len(parts) == 1 or parts[1].lower() == old.lower(): @@ -419,11 +417,18 @@ def replace_abbr(company, old, new): doc = (d for d in frappe.db.sql("select name from `tab%s` where company=%s" % (dt, '%s'), company)) for d in doc: _rename_record(d) + try: + frappe.db.auto_commit_on_many_writes = 1 + frappe.db.set_value("Company", company, "abbr", new) + for dt in ["Warehouse", "Account", "Cost Center", "Department", + "Sales Taxes and Charges Template", "Purchase Taxes and Charges Template"]: + _rename_records(dt) + frappe.db.commit() - for dt in ["Warehouse", "Account", "Cost Center", "Department", - "Sales Taxes and Charges Template", "Purchase Taxes and Charges Template"]: - _rename_records(dt) - frappe.db.commit() + except Exception: + frappe.log_error(title=_('Abbreviation Rename Error')) + finally: + frappe.db.auto_commit_on_many_writes = 0 def get_name_with_abbr(name, company): diff --git a/erpnext/stock/doctype/batch/batch.py b/erpnext/stock/doctype/batch/batch.py index bb5ad5c6fee..cd441b5958d 100644 --- a/erpnext/stock/doctype/batch/batch.py +++ b/erpnext/stock/doctype/batch/batch.py @@ -226,9 +226,9 @@ def split_batch(batch_no, item_code, warehouse, qty, new_batch_id=None): return batch.name -def set_batch_nos(doc, warehouse_field, throw=False): +def set_batch_nos(doc, warehouse_field, throw=False, child_table="items"): """Automatically select `batch_no` for outgoing items in item table""" - for d in doc.items: + for d in doc.get(child_table): qty = d.get('stock_qty') or d.get('transfer_qty') or d.get('qty') or 0 has_batch_no = frappe.db.get_value('Item', d.item_code, 'has_batch_no') warehouse = d.get(warehouse_field, None) diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.js b/erpnext/stock/doctype/delivery_note/delivery_note.js index 7875b9cd87f..74cb3fcb1f0 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.js +++ b/erpnext/stock/doctype/delivery_note/delivery_note.js @@ -78,6 +78,9 @@ frappe.ui.form.on("Delivery Note", { }); erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype); + + frm.set_df_property('packed_items', 'cannot_add_rows', true); + frm.set_df_property('packed_items', 'cannot_delete_rows', true); }, print_without_amount: function(frm) { diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.json b/erpnext/stock/doctype/delivery_note/delivery_note.json index 280fde158f5..f20e76f5bf6 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.json +++ b/erpnext/stock/doctype/delivery_note/delivery_note.json @@ -554,8 +554,7 @@ "oldfieldname": "packing_details", "oldfieldtype": "Table", "options": "Packed Item", - "print_hide": 1, - "read_only": 1 + "print_hide": 1 }, { "fieldname": "product_bundle_help", @@ -1289,7 +1288,7 @@ "idx": 146, "is_submittable": 1, "links": [], - "modified": "2021-04-15 23:55:49.620641", + "modified": "2021-06-11 19:27:30.901112", "modified_by": "Administrator", "module": "Stock", "name": "Delivery Note", diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py index dd31965face..fcdb5f3b190 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/delivery_note.py @@ -129,12 +129,13 @@ class DeliveryNote(SellingController): self.validate_uom_is_integer("uom", "qty") self.validate_with_previous_doc() - if self._action != 'submit' and not self.is_return: - set_batch_nos(self, 'warehouse', True) - from erpnext.stock.doctype.packed_item.packed_item import make_packing_list make_packing_list(self) + if self._action != 'submit' and not self.is_return: + set_batch_nos(self, 'warehouse', throw=True) + set_batch_nos(self, 'warehouse', throw=True, child_table="packed_items") + self.update_current_stock() if not self.installation_status: self.installation_status = 'Not Installed' diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py index 0c63df0e225..f981aeb13bb 100644 --- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py @@ -7,7 +7,7 @@ import unittest import frappe import json import frappe.defaults -from frappe.utils import cint, nowdate, nowtime, cstr, add_days, flt, today +from frappe.utils import nowdate, nowtime, cstr, flt from erpnext.stock.stock_ledger import get_previous_sle from erpnext.accounts.utils import get_balance_on from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import get_gl_entries @@ -18,9 +18,11 @@ from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos, SerialNoWa from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation \ import create_stock_reconciliation, set_valuation_method from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order, create_dn_against_so -from erpnext.accounts.doctype.account.test_account import get_inventory_account, create_account +from erpnext.accounts.doctype.account.test_account import get_inventory_account from erpnext.stock.doctype.warehouse.test_warehouse import get_warehouse -from erpnext.stock.doctype.item.test_item import create_item +from erpnext.stock.doctype.item.test_item import make_item +from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle + class TestDeliveryNote(unittest.TestCase): def test_over_billing_against_dn(self): @@ -277,8 +279,6 @@ class TestDeliveryNote(unittest.TestCase): dn.cancel() def test_sales_return_for_non_bundled_items_full(self): - from erpnext.stock.doctype.item.test_item import make_item - company = frappe.db.get_value('Warehouse', 'Stores - TCP1', 'company') make_item("Box", {'is_stock_item': 1}) @@ -741,6 +741,25 @@ class TestDeliveryNote(unittest.TestCase): self.assertEqual(si2.items[0].qty, 2) self.assertEqual(si2.items[1].qty, 1) + + def test_delivery_note_bundle_with_batched_item(self): + batched_bundle = make_item("_Test Batched bundle", {"is_stock_item": 0}) + batched_item = make_item("_Test Batched Item", + {"is_stock_item": 1, "has_batch_no": 1, "create_new_batch": 1, "batch_number_series": "TESTBATCH.#####"} + ) + make_product_bundle(parent=batched_bundle.name, items=[batched_item.name]) + make_stock_entry(item_code=batched_item.name, target="_Test Warehouse - _TC", qty=10, basic_rate=42) + + try: + dn = create_delivery_note(item_code=batched_bundle.name, qty=1) + except frappe.ValidationError as e: + if "batch" in str(e).lower(): + self.fail("Batch numbers not getting added to bundled items in DN.") + raise e + + self.assertTrue("TESTBATCH" in dn.packed_items[0].batch_no, "Batch number not added in packed item") + + def create_delivery_note(**args): dn = frappe.new_doc("Delivery Note") args = frappe._dict(args) diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index 746cbbf601d..c64084fe340 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -436,20 +436,28 @@ def get_barcode_data(items_list): return itemwise_barcode @frappe.whitelist() -def get_item_tax_info(company, tax_category, item_codes, item_rates=None): +def get_item_tax_info(company, tax_category, item_codes, item_rates=None, item_tax_templates=None): out = {} - if isinstance(item_codes, string_types): + if isinstance(item_codes, (str,)): item_codes = json.loads(item_codes) - if isinstance(item_rates, string_types): + if isinstance(item_rates, (str,)): item_rates = json.loads(item_rates) + if isinstance(item_tax_templates, (str,)): + item_tax_templates = json.loads(item_tax_templates) + for item_code in item_codes: - if not item_code or item_code[1] in out: + if not item_code or item_code[1] in out or not item_tax_templates.get(item_code[1]): continue + out[item_code[1]] = {} item = frappe.get_cached_doc("Item", item_code[0]) args = {"company": company, "tax_category": tax_category, "net_rate": item_rates[item_code[1]]} + + if item_tax_templates: + args.update({"item_tax_template": item_tax_templates.get(item_code[1])}) + get_item_tax_template(args, item, out[item_code[1]]) out[item_code[1]]["item_tax_rate"] = get_item_tax_map(company, out[item_code[1]].get("item_tax_template"), as_json=True) @@ -463,9 +471,7 @@ def get_item_tax_template(args, item, out): } """ item_tax_template = args.get("item_tax_template") - - if not item_tax_template: - item_tax_template = _get_item_tax_template(args, item.taxes, out) + item_tax_template = _get_item_tax_template(args, item.taxes, out) if not item_tax_template: item_group = item.item_group @@ -508,7 +514,8 @@ def _get_item_tax_template(args, taxes, out=None, for_validate=False): return None # do not change if already a valid template - if args.get('item_tax_template') in taxes: + if args.get('item_tax_template') in {t.item_tax_template for t in taxes}: + out["item_tax_template"] = args.get('item_tax_template') return args.get('item_tax_template') for tax in taxes: