Merge pull request #26458 from rohitwaghchaure/fixed-multi-currency-issue-pre
fix: multi-currency issue
diff --git a/erpnext/buying/doctype/supplier/supplier.js b/erpnext/buying/doctype/supplier/supplier.js
index 4ddc458..1766c2c 100644
--- a/erpnext/buying/doctype/supplier/supplier.js
+++ b/erpnext/buying/doctype/supplier/supplier.js
@@ -60,10 +60,23 @@
erpnext.utils.make_pricing_rule(frm.doc.doctype, frm.doc.name);
}, __('Create'));
+ frm.add_custom_button(__('Get Supplier Group Details'), function () {
+ frm.trigger("get_supplier_group_details");
+ }, __('Actions'));
+
// indicators
erpnext.utils.set_party_dashboard_indicators(frm);
}
},
+ get_supplier_group_details: function(frm) {
+ frappe.call({
+ method: "get_supplier_group_details",
+ doc: frm.doc,
+ callback: function() {
+ frm.refresh();
+ }
+ });
+ },
is_internal_supplier: function(frm) {
if (frm.doc.is_internal_supplier == 1) {
diff --git a/erpnext/buying/doctype/supplier/supplier.py b/erpnext/buying/doctype/supplier/supplier.py
index edeb135..fd16b23 100644
--- a/erpnext/buying/doctype/supplier/supplier.py
+++ b/erpnext/buying/doctype/supplier/supplier.py
@@ -51,6 +51,23 @@
validate_party_accounts(self)
self.validate_internal_supplier()
+ @frappe.whitelist()
+ def get_supplier_group_details(self):
+ doc = frappe.get_doc('Supplier Group', self.supplier_group)
+ self.payment_terms = ""
+ self.accounts = []
+
+ if doc.accounts:
+ for account in doc.accounts:
+ child = self.append('accounts')
+ child.company = account.company
+ child.account = account.account
+
+ if doc.payment_terms:
+ self.payment_terms = doc.payment_terms
+
+ self.save()
+
def validate_internal_supplier(self):
internal_supplier = frappe.db.get_value("Supplier",
{"is_internal_supplier": 1, "represents_company": self.represents_company, "name": ("!=", self.name)}, "name")
@@ -86,4 +103,4 @@
create_contact(supplier, 'Supplier',
doc.name, args.get('supplier_email_' + str(i)))
except frappe.NameError:
- pass
\ No newline at end of file
+ pass
diff --git a/erpnext/buying/doctype/supplier/test_supplier.py b/erpnext/buying/doctype/supplier/test_supplier.py
index f9c8d35..8980466 100644
--- a/erpnext/buying/doctype/supplier/test_supplier.py
+++ b/erpnext/buying/doctype/supplier/test_supplier.py
@@ -13,6 +13,30 @@
class TestSupplier(unittest.TestCase):
+ def test_get_supplier_group_details(self):
+ doc = frappe.new_doc("Supplier Group")
+ doc.supplier_group_name = "_Testing Supplier Group"
+ doc.payment_terms = "_Test Payment Term Template 3"
+ doc.accounts = []
+ test_account_details = {
+ "company": "_Test Company",
+ "account": "Creditors - _TC",
+ }
+ doc.append("accounts", test_account_details)
+ doc.save()
+ s_doc = frappe.new_doc("Supplier")
+ s_doc.supplier_name = "Testing Supplier"
+ s_doc.supplier_group = "_Testing Supplier Group"
+ s_doc.payment_terms = ""
+ s_doc.accounts = []
+ s_doc.insert()
+ s_doc.get_supplier_group_details()
+ self.assertEqual(s_doc.payment_terms, "_Test Payment Term Template 3")
+ self.assertEqual(s_doc.accounts[0].company, "_Test Company")
+ self.assertEqual(s_doc.accounts[0].account, "Creditors - _TC")
+ s_doc.delete()
+ doc.delete()
+
def test_supplier_default_payment_terms(self):
# Payment Term based on Days after invoice date
frappe.db.set_value(
@@ -136,4 +160,4 @@
return doc
except frappe.DuplicateEntryError:
- return frappe.get_doc("Supplier", args.supplier_name)
\ No newline at end of file
+ return frappe.get_doc("Supplier", args.supplier_name)
diff --git a/erpnext/selling/doctype/customer/customer.js b/erpnext/selling/doctype/customer/customer.js
index 825b170..2849466 100644
--- a/erpnext/selling/doctype/customer/customer.js
+++ b/erpnext/selling/doctype/customer/customer.js
@@ -130,6 +130,10 @@
erpnext.utils.make_pricing_rule(frm.doc.doctype, frm.doc.name);
}, __('Create'));
+ frm.add_custom_button(__('Get Customer Group Details'), function () {
+ frm.trigger("get_customer_group_details");
+ }, __('Actions'));
+
// indicator
erpnext.utils.set_party_dashboard_indicators(frm);
@@ -145,4 +149,15 @@
if(frm.doc.lead_name) frappe.model.clear_doc("Lead", frm.doc.lead_name);
},
-});
\ No newline at end of file
+ get_customer_group_details: function(frm) {
+ frappe.call({
+ method: "get_customer_group_details",
+ doc: frm.doc,
+ callback: function() {
+ frm.refresh();
+ }
+ });
+
+ }
+});
+
diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py
index 818888c..3b62081 100644
--- a/erpnext/selling/doctype/customer/customer.py
+++ b/erpnext/selling/doctype/customer/customer.py
@@ -78,6 +78,29 @@
if sum(member.allocated_percentage or 0 for member in self.sales_team) != 100:
frappe.throw(_("Total contribution percentage should be equal to 100"))
+ @frappe.whitelist()
+ def get_customer_group_details(self):
+ doc = frappe.get_doc('Customer Group', self.customer_group)
+ self.accounts = self.credit_limits = []
+ self.payment_terms = self.default_price_list = ""
+
+ tables = [["accounts", "account"], ["credit_limits", "credit_limit"]]
+ fields = ["payment_terms", "default_price_list"]
+
+ for row in tables:
+ table, field = row[0], row[1]
+ if not doc.get(table): continue
+
+ for entry in doc.get(table):
+ child = self.append(table)
+ child.update({"company": entry.company, field: entry.get(field)})
+
+ for field in fields:
+ if not doc.get(field): continue
+ self.update({field: doc.get(field)})
+
+ self.save()
+
def check_customer_group_change(self):
frappe.flags.customer_group_changed = False
diff --git a/erpnext/selling/doctype/customer/test_customer.py b/erpnext/selling/doctype/customer/test_customer.py
index 7761aa7..b1a5b52 100644
--- a/erpnext/selling/doctype/customer/test_customer.py
+++ b/erpnext/selling/doctype/customer/test_customer.py
@@ -27,6 +27,42 @@
def tearDown(self):
set_credit_limit('_Test Customer', '_Test Company', 0)
+ def test_get_customer_group_details(self):
+ doc = frappe.new_doc("Customer Group")
+ doc.customer_group_name = "_Testing Customer Group"
+ doc.payment_terms = "_Test Payment Term Template 3"
+ doc.accounts = []
+ doc.default_price_list = "Standard Buying"
+ doc.credit_limits = []
+ test_account_details = {
+ "company": "_Test Company",
+ "account": "Creditors - _TC",
+ }
+ test_credit_limits = {
+ "company": "_Test Company",
+ "credit_limit": 350000
+ }
+ doc.append("accounts", test_account_details)
+ doc.append("credit_limits", test_credit_limits)
+ doc.insert()
+
+ c_doc = frappe.new_doc("Customer")
+ c_doc.customer_name = "Testing Customer"
+ c_doc.customer_group = "_Testing Customer Group"
+ c_doc.payment_terms = c_doc.default_price_list = ""
+ c_doc.accounts = c_doc.credit_limits= []
+ c_doc.insert()
+ c_doc.get_customer_group_details()
+ self.assertEqual(c_doc.payment_terms, "_Test Payment Term Template 3")
+
+ self.assertEqual(c_doc.accounts[0].company, "_Test Company")
+ self.assertEqual(c_doc.accounts[0].account, "Creditors - _TC")
+
+ self.assertEqual(c_doc.credit_limits[0].company, "_Test Company")
+ self.assertEqual(c_doc.credit_limits[0].credit_limit, 350000)
+ c_doc.delete()
+ doc.delete()
+
def test_party_details(self):
from erpnext.accounts.party import get_party_details
diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py
index 915e6a4..36a7d20 100644
--- a/erpnext/setup/doctype/company/company.py
+++ b/erpnext/setup/doctype/company/company.py
@@ -395,7 +395,7 @@
@frappe.whitelist()
def enqueue_replace_abbr(company, old, new):
- kwargs = dict(company=company, old=old, new=new)
+ kwargs = dict(queue="long", company=company, old=old, new=new)
frappe.enqueue('erpnext.setup.doctype.company.company.replace_abbr', **kwargs)
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index 8f27ef4..90b81dd 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -529,7 +529,7 @@
scrap_items_cost = sum([flt(d.basic_amount) for d in self.get("items") if d.is_scrap_item])
# Get raw materials cost from BOM if multiple material consumption entries
- if frappe.db.get_single_value("Manufacturing Settings", "material_consumption"):
+ if frappe.db.get_single_value("Manufacturing Settings", "material_consumption", cache=True):
bom_items = self.get_bom_raw_materials(finished_item_qty)
outgoing_items_cost = sum([flt(row.qty)*flt(row.rate) for row in bom_items.values()])
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 0febcb6..cb939e6 100644
--- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
+++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
@@ -178,3 +178,4 @@
frappe.db.add_index("Stock Ledger Entry", ["voucher_no", "voucher_type"])
frappe.db.add_index("Stock Ledger Entry", ["batch_no", "item_code", "warehouse"])
+ frappe.db.add_index("Stock Ledger Entry", ["voucher_detail_no"])
diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py
index 4e9c768..c15d1ed 100644
--- a/erpnext/stock/stock_ledger.py
+++ b/erpnext/stock/stock_ledger.py
@@ -6,13 +6,14 @@
import erpnext
import copy
from frappe import _
-from frappe.utils import cint, flt, cstr, now, get_link_to_form
+from frappe.utils import cint, flt, cstr, now, get_link_to_form, getdate
from frappe.model.meta import get_field_precision
from erpnext.stock.utils import get_valuation_method, get_incoming_outgoing_rate_for_cancel
from erpnext.stock.utils import get_bin
import json
from six import iteritems
+
# future reposting
class NegativeStockError(frappe.ValidationError): pass
class SerialNoExistsInFutureTransaction(frappe.ValidationError):
@@ -130,7 +131,13 @@
if not args and voucher_type and voucher_no:
args = get_args_for_voucher(voucher_type, voucher_no)
- distinct_item_warehouses = [(d.item_code, d.warehouse) for d in args]
+ distinct_item_warehouses = {}
+ for i, d in enumerate(args):
+ distinct_item_warehouses.setdefault((d.item_code, d.warehouse), frappe._dict({
+ "reposting_status": False,
+ "sle": d,
+ "args_idx": i
+ }))
i = 0
while i < len(args):
@@ -139,13 +146,21 @@
"warehouse": args[i].warehouse,
"posting_date": args[i].posting_date,
"posting_time": args[i].posting_time,
- "creation": args[i].get("creation")
+ "creation": args[i].get("creation"),
+ "distinct_item_warehouses": distinct_item_warehouses
}, allow_negative_stock=allow_negative_stock, via_landed_cost_voucher=via_landed_cost_voucher)
- for item_wh, new_sle in iteritems(obj.new_items):
- if item_wh not in distinct_item_warehouses:
- args.append(new_sle)
+ distinct_item_warehouses[(args[i].item_code, args[i].warehouse)].reposting_status = True
+ if obj.new_items_found:
+ for item_wh, data in iteritems(distinct_item_warehouses):
+ if ('args_idx' not in data and not data.reposting_status) or (data.sle_changed and data.reposting_status):
+ data.args_idx = len(args)
+ args.append(data.sle)
+ elif data.sle_changed and not data.reposting_status:
+ args[data.args_idx] = data.sle
+
+ data.sle_changed = False
i += 1
def get_args_for_voucher(voucher_type, voucher_no):
@@ -186,11 +201,12 @@
self.company = frappe.get_cached_value("Warehouse", self.args.warehouse, "company")
self.get_precision()
self.valuation_method = get_valuation_method(self.item_code)
- self.new_items = {}
+
+ self.new_items_found = False
+ self.distinct_item_warehouses = args.get("distinct_item_warehouses", frappe._dict())
self.data = frappe._dict()
self.initialize_previous_data(self.args)
-
self.build()
def get_precision(self):
@@ -296,11 +312,29 @@
elif dependant_sle.item_code == self.item_code and dependant_sle.warehouse == self.args.warehouse:
return entries_to_fix
elif dependant_sle.item_code != self.item_code:
- if (dependant_sle.item_code, dependant_sle.warehouse) not in self.new_items:
- self.new_items[(dependant_sle.item_code, dependant_sle.warehouse)] = dependant_sle
+ self.update_distinct_item_warehouses(dependant_sle)
return entries_to_fix
elif dependant_sle.item_code == self.item_code and dependant_sle.warehouse in self.data:
return entries_to_fix
+ else:
+ return self.append_future_sle_for_dependant(dependant_sle, entries_to_fix)
+
+ def update_distinct_item_warehouses(self, dependant_sle):
+ key = (dependant_sle.item_code, dependant_sle.warehouse)
+ val = frappe._dict({
+ "sle": dependant_sle
+ })
+ if key not in self.distinct_item_warehouses:
+ self.distinct_item_warehouses[key] = val
+ self.new_items_found = True
+ else:
+ existing_sle_posting_date = self.distinct_item_warehouses[key].get("sle", {}).get("posting_date")
+ if getdate(dependant_sle.posting_date) < getdate(existing_sle_posting_date):
+ val.sle_changed = True
+ self.distinct_item_warehouses[key] = val
+ self.new_items_found = True
+
+ def append_future_sle_for_dependant(self, dependant_sle, entries_to_fix):
self.initialize_previous_data(dependant_sle)
args = self.data[dependant_sle.warehouse].previous_sle \
@@ -393,6 +427,7 @@
rate = 0
# Material Transfer, Repack, Manufacturing
if sle.voucher_type == "Stock Entry":
+ self.recalculate_amounts_in_stock_entry(sle.voucher_no)
rate = frappe.db.get_value("Stock Entry Detail", sle.voucher_detail_no, "valuation_rate")
# Sales and Purchase Return
elif sle.voucher_type in ("Purchase Receipt", "Purchase Invoice", "Delivery Note", "Sales Invoice"):
@@ -442,7 +477,11 @@
frappe.db.set_value("Stock Entry Detail", sle.voucher_detail_no, "basic_rate", outgoing_rate)
# Update outgoing item's rate, recalculate FG Item's rate and total incoming/outgoing amount
- stock_entry = frappe.get_doc("Stock Entry", sle.voucher_no, for_update=True)
+ if not sle.dependant_sle_voucher_detail_no:
+ self.recalculate_amounts_in_stock_entry(sle.voucher_no)
+
+ def recalculate_amounts_in_stock_entry(self, voucher_no):
+ stock_entry = frappe.get_doc("Stock Entry", voucher_no, for_update=True)
stock_entry.calculate_rate_and_amount(reset_outgoing_rate=False, raise_error_if_no_rate=False)
stock_entry.db_update()
for d in stock_entry.items: