Merge pull request #4656 from nabinhait/gle_rounding
[fix] Round off gle due to currency conversion
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py
index a64548b..9c39f0d 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.py
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py
@@ -351,6 +351,7 @@
def set_print_format_fields(self):
total_amount = 0.0
bank_account_currency = None
+ self.pay_to_recd_from = None
for d in self.get('accounts'):
if d.party_type and d.party:
if not self.pay_to_recd_from:
@@ -360,7 +361,10 @@
elif frappe.db.get_value("Account", d.account, "account_type") in ["Bank", "Cash"]:
total_amount += (d.debit_in_account_currency or d.credit_in_account_currency)
bank_account_currency = d.account_currency
-
+
+ if not self.pay_to_recd_from:
+ total_amount = 0
+
self.set_total_amount(total_amount, bank_account_currency)
def set_total_amount(self, amt, currency):
diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py
index 494e70c..fcc1f92 100644
--- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py
+++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py
@@ -97,7 +97,10 @@
args = json.loads(args)
args = frappe._dict(args)
-
+
+ if not args.transaction_type:
+ set_transaction_type(args)
+
# list of dictionaries
out = []
@@ -134,13 +137,17 @@
if not args.item_group:
frappe.throw(_("Item Group not mentioned in item master for item {0}").format(args.item_code))
- if args.customer and not (args.customer_group and args.territory):
- customer = frappe.db.get_value("Customer", args.customer, ["customer_group", "territory"])
- if customer:
- args.customer_group, args.territory = customer
+ if args.transaction_type=="selling":
+ if args.customer and not (args.customer_group and args.territory):
+ customer = frappe.db.get_value("Customer", args.customer, ["customer_group", "territory"])
+ if customer:
+ args.customer_group, args.territory = customer
+
+ args.supplier = args.supplier_type = None
elif args.supplier and not args.supplier_type:
args.supplier_type = frappe.db.get_value("Supplier", args.supplier, "supplier_type")
+ args.customer = args.customer_group = args.territory = None
pricing_rules = get_pricing_rules(args)
pricing_rule = filter_pricing_rules(args, pricing_rules)
@@ -212,7 +219,7 @@
and {transaction_type} = 1 {conditions}
order by priority desc, name desc""".format(
item_group_condition=item_group_condition,
- transaction_type= "selling" if (args.customer or args.lead) else "buying",
+ transaction_type= args.transaction_type,
conditions=conditions), values, as_dict=1)
def filter_pricing_rules(args, pricing_rules):
@@ -270,3 +277,14 @@
if filtered_rules: break
return filtered_rules or pricing_rules
+
+def set_transaction_type(args):
+ if args.doctype in ("Opportunity", "Quotation", "Sales Order", "Delivery Note", "Sales Invoice"):
+ args.transaction_type = "selling"
+ elif args.doctype in ("Material Request", "Supplier Quotation", "Purchase Order",
+ "Purchase Receipt", "Purchase Invoice"):
+ args.transaction_type = "buying"
+ elif args.customer:
+ args.transaction_type = "selling"
+ else:
+ args.transaction_type = "buying"
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py
index 6edfaff..bdd7431 100644
--- a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py
+++ b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py
@@ -31,21 +31,22 @@
"company": "_Test Company",
"price_list": "_Test Price List",
"currency": "_Test Currency",
- "parenttype": "Sales Order",
+ "doctype": "Sales Order",
"conversion_rate": 1,
"price_list_currency": "_Test Currency",
"plc_conversion_rate": 1,
"order_type": "Sales",
"customer": "_Test Customer",
- "doctype": "Sales Order Item",
"name": None
})
details = get_item_details(args)
self.assertEquals(details.get("discount_percentage"), 10)
-
+
prule = frappe.get_doc(test_record.copy())
prule.applicable_for = "Customer"
+ prule.title = "_Test Pricing Rule for Customer"
self.assertRaises(MandatoryError, prule.insert)
+
prule.customer = "_Test Customer"
prule.discount_percentage = 20
prule.insert()
@@ -55,16 +56,18 @@
prule = frappe.get_doc(test_record.copy())
prule.apply_on = "Item Group"
prule.item_group = "All Item Groups"
+ prule.title = "_Test Pricing Rule for Item Group"
prule.discount_percentage = 15
prule.insert()
-
- args.customer = None
+
+ args.customer = "_Test Customer 1"
details = get_item_details(args)
self.assertEquals(details.get("discount_percentage"), 10)
prule = frappe.get_doc(test_record.copy())
prule.applicable_for = "Campaign"
prule.campaign = "_Test Campaign"
+ prule.title = "_Test Pricing Rule for Campaign"
prule.discount_percentage = 5
prule.priority = 8
prule.insert()
diff --git a/erpnext/accounts/report/gross_profit/gross_profit.py b/erpnext/accounts/report/gross_profit/gross_profit.py
index 8d9684c..3c90fe2 100644
--- a/erpnext/accounts/report/gross_profit/gross_profit.py
+++ b/erpnext/accounts/report/gross_profit/gross_profit.py
@@ -7,7 +7,9 @@
from frappe.utils import flt
def execute(filters=None):
- if not filters: filters = {}
+ if not filters: filters = frappe._dict()
+ company_currency = frappe.db.get_value("Company", filters.company, "default_currency")
+
gross_profit_data = GrossProfitGenerator(filters)
data = []
@@ -43,6 +45,8 @@
row = []
for col in group_wise_columns.get(scrub(filters.group_by)):
row.append(src.get(col))
+
+ row.append(company_currency)
data.append(row)
return columns, data
@@ -60,15 +64,15 @@
"description": _("Description"),
"warehouse": _("Warehouse") + ":Link/Warehouse",
"qty": _("Qty") + ":Float",
- "base_rate": _("Avg. Selling Rate") + ":Currency",
- "buying_rate": _("Avg. Buying Rate") + ":Currency",
- "base_amount": _("Selling Amount") + ":Currency",
- "buying_amount": _("Buying Amount") + ":Currency",
- "gross_profit": _("Gross Profit") + ":Currency",
+ "base_rate": _("Avg. Selling Rate") + ":Currency/currency",
+ "buying_rate": _("Avg. Buying Rate") + ":Currency/currency",
+ "base_amount": _("Selling Amount") + ":Currency/currency",
+ "buying_amount": _("Buying Amount") + ":Currency/currency",
+ "gross_profit": _("Gross Profit") + ":Currency/currency",
"gross_profit_percent": _("Gross Profit %") + ":Percent",
"project": _("Project") + ":Link/Project",
"sales_person": _("Sales person"),
- "allocated_amount": _("Allocated Amount") + ":Currency",
+ "allocated_amount": _("Allocated Amount") + ":Currency/currency",
"customer": _("Customer") + ":Link/Customer",
"customer_group": _("Customer Group") + ":Link/Customer Group",
"territory": _("Territory") + ":Link/Territory"
@@ -76,6 +80,13 @@
for col in group_wise_columns.get(scrub(filters.group_by)):
columns.append(column_map.get(col))
+
+ columns.append({
+ "fieldname": "currency",
+ "label" : _("Currency"),
+ "fieldtype": "Link",
+ "options": "Currency"
+ })
return columns
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index c3da6b2..5c84112 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -152,8 +152,8 @@
args = parent_dict.copy()
args.update(item.as_dict())
- args["doctype"] = parent_dict.get("doctype")
- args["name"] = parent_dict.get("name")
+ args["doctype"] = self.doctype
+ args["name"] = self.name
if not args.get("transaction_date"):
args["transaction_date"] = args.get("posting_date")
diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py
index c6c7111..868720a 100644
--- a/erpnext/controllers/sales_and_purchase_return.py
+++ b/erpnext/controllers/sales_and_purchase_return.py
@@ -96,6 +96,9 @@
if s not in ref_serial_nos:
frappe.throw(_("Row # {0}: Serial No {1} does not match with {2} {3}")
.format(d.idx, s, doc.doctype, doc.return_against))
+
+ if not d.warehouse:
+ frappe.throw(_("Warehouse is mandatory"))
items_returned = True
diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py
index 842faec..4c6a320 100644
--- a/erpnext/controllers/stock_controller.py
+++ b/erpnext/controllers/stock_controller.py
@@ -218,14 +218,14 @@
return serialized_items
- def get_incoming_rate_for_sales_return(self, item_code, warehouse, against_document):
+ def get_incoming_rate_for_sales_return(self, item_code, against_document):
incoming_rate = 0.0
if against_document and item_code:
incoming_rate = frappe.db.sql("""select abs(stock_value_difference / actual_qty)
from `tabStock Ledger Entry`
where voucher_type = %s and voucher_no = %s
- and item_code = %s and warehouse=%s limit 1""",
- (self.doctype, against_document, item_code, warehouse))
+ and item_code = %s limit 1""",
+ (self.doctype, against_document, item_code))
incoming_rate = incoming_rate[0][0] if incoming_rate else 0.0
return incoming_rate
@@ -257,8 +257,7 @@
if frappe.db.get_value("Item", d.item_code, "is_stock_item") == 1 and flt(d.qty):
return_rate = 0
if cint(self.is_return) and self.return_against and self.docstatus==1:
- return_rate = self.get_incoming_rate_for_sales_return(d.item_code,
- d.warehouse, self.return_against)
+ return_rate = self.get_incoming_rate_for_sales_return(d.item_code, self.return_against)
# On cancellation or if return entry submission, make stock ledger entry for
# target warehouse first, to update serial no values properly
diff --git a/erpnext/crm/doctype/opportunity/opportunity.py b/erpnext/crm/doctype/opportunity/opportunity.py
index a21ebde..866b982 100644
--- a/erpnext/crm/doctype/opportunity/opportunity.py
+++ b/erpnext/crm/doctype/opportunity/opportunity.py
@@ -9,7 +9,6 @@
from erpnext.setup.utils import get_exchange_rate
from erpnext.utilities.transaction_base import TransactionBase
from erpnext.accounts.party import get_party_account_currency
-from erpnext.stock.get_item_details import apply_price_list
subject_field = "title"
sender_field = "contact_email"
@@ -193,8 +192,6 @@
quotation.currency = party_account_currency or company_currency
quotation.conversion_rate = exchange_rate
- quotation.update(apply_price_list(quotation.as_dict(), as_doc = True))
-
quotation.run_method("set_missing_values")
quotation.run_method("calculate_taxes_and_totals")
diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py
index 7c47cb8..6cfbc99 100644
--- a/erpnext/manufacturing/doctype/bom/bom.py
+++ b/erpnext/manufacturing/doctype/bom/bom.py
@@ -4,17 +4,15 @@
from __future__ import unicode_literals
import frappe
from frappe.utils import cint, cstr, flt
-
from frappe import _
from frappe.model.document import Document
from operator import itemgetter
class BOM(Document):
-
def autoname(self):
last_name = frappe.db.sql("""select max(name) from `tabBOM`
- where name like "BOM/%s/%%" """ % frappe.db.escape(self.item))
+ where name like "BOM/{0}/%%" and item=%s""".format(frappe.db.escape(self.item)), self.item)
if last_name:
idx = cint(cstr(last_name[0][0]).split('/')[-1].split('-')[0]) + 1
else:
diff --git a/erpnext/selling/doctype/quotation/quotation.js b/erpnext/selling/doctype/quotation/quotation.js
index aad4628..83cec04 100644
--- a/erpnext/selling/doctype/quotation/quotation.js
+++ b/erpnext/selling/doctype/quotation/quotation.js
@@ -25,7 +25,8 @@
cur_frm.add_custom_button(__('Lost'),
cur_frm.cscript['Declare Order Lost'], __("Status"));
}
-
+
+ cur_frm.page.set_inner_btn_group_as_primary(__("Make"));
}
if (this.frm.doc.docstatus===0) {
diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py
index 3501f52..c6ea618 100644
--- a/erpnext/selling/doctype/sales_order/test_sales_order.py
+++ b/erpnext/selling/doctype/sales_order/test_sales_order.py
@@ -334,15 +334,15 @@
existing_ordered_qty = bin[0].ordered_qty if bin else 0.0
existing_reserved_qty = bin[0].reserved_qty if bin else 0.0
- bin = frappe.get_all("Bin", filters={"item_code": dn_item.item_code, "warehouse": "_Test Warehouse - _TC"},
- fields=["reserved_qty"])
+ bin = frappe.get_all("Bin", filters={"item_code": dn_item.item_code,
+ "warehouse": "_Test Warehouse - _TC"}, fields=["reserved_qty"])
existing_reserved_qty_for_dn_item = bin[0].reserved_qty if bin else 0.0
#create so, po and partial dn
so = make_sales_order(item_list=so_items, do_not_submit=True)
so.submit()
-
+
po = make_purchase_order_for_drop_shipment(so.name, '_Test Supplier')
po.submit()
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.js b/erpnext/stock/doctype/delivery_note/delivery_note.js
index d4e7932..0b6f47e 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.js
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.js
@@ -50,14 +50,12 @@
if (cint(frappe.defaults.get_default("auto_accounting_for_stock"))) {
this.show_general_ledger();
}
- if (this.frm.has_perm("submit") && (doc.status !== "Closed")
- && this.frm.doc.__onload && this.frm.doc.__onload.has_return_entry) {
- cur_frm.add_custom_button(__("Close"), this.close_delivery_note, __("Status"))
+ if (this.frm.has_perm("submit") && doc.status !== "Closed") {
+ cur_frm.add_custom_button(__("Close"), this.close_delivery_note, __("Status"))
}
}
- if(doc.__onload && !doc.__onload.billing_complete && doc.docstatus==1
- && !doc.is_return && doc.status!="Closed") {
+ if(doc.docstatus==1 && !doc.is_return && doc.status!="Closed" && flt(doc.per_billed) < 100) {
// show Make Invoice button only if Delivery Note is not created from Sales Invoice
var from_sales_invoice = false;
from_sales_invoice = cur_frm.doc.items.some(function(item) {
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py
index b553054..7cb855f 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.py
@@ -61,10 +61,6 @@
'extra_cond': """ and exists (select name from `tabDelivery Note` where name=`tabDelivery Note Item`.parent and is_return=1)"""
}]
- def onload(self):
- self.set_onload("has_return_entry", len(frappe.db.exists({"doctype": "Delivery Note",
- "is_return": 1, "return_against": self.name, "docstatus": 1})))
-
def before_print(self):
def toggle_print_hide(meta, fieldname):
df = meta.get_field(fieldname)
diff --git a/erpnext/stock/doctype/item/test_item.py b/erpnext/stock/doctype/item/test_item.py
index 7538260..e5392d4 100644
--- a/erpnext/stock/doctype/item/test_item.py
+++ b/erpnext/stock/doctype/item/test_item.py
@@ -93,13 +93,14 @@
"price_list_currency": "_Test Currency",
"plc_conversion_rate": 1,
"order_type": "Sales",
+ "customer": "_Test Customer"
})
for key, value in to_check.iteritems():
self.assertEquals(value, details.get(key))
def test_make_item_variant(self):
- frappe.delete_doc_if_exists("Item", "_Test Variant Item-L")
+ frappe.delete_doc_if_exists("Item", "_Test Variant Item-L", force=1)
variant = create_variant("_Test Variant Item", {"Test Size": "Large"})
variant.save()
diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py
index 19c7415..74a8fa1 100644
--- a/erpnext/stock/doctype/material_request/material_request.py
+++ b/erpnext/stock/doctype/material_request/material_request.py
@@ -11,7 +11,6 @@
from frappe import _
from frappe.model.mapper import get_mapped_doc
from erpnext.stock.stock_balance import update_bin_qty, get_indented_qty
-from erpnext.stock.get_item_details import apply_price_list
from erpnext.controllers.buying_controller import BuyingController
@@ -24,7 +23,7 @@
return _("{0}: {1}").format(self.status, self.material_request_type)
def check_if_already_pulled(self):
- pass#if self.[d.sales_order_no for d in self.get('items')]
+ pass
def validate_qty_against_so(self):
so_items = {} # Format --> {'SO/00001': {'Item/001': 120, 'Item/002': 24}}
@@ -183,7 +182,6 @@
@frappe.whitelist()
def make_purchase_order(source_name, target_doc=None):
def postprocess(source, target_doc):
- target_doc.update(apply_price_list(target_doc.as_dict(), as_doc = True))
set_missing_values(source, target_doc)
doclist = get_mapped_doc("Material Request", source_name, {
@@ -225,9 +223,7 @@
target_doc.set("items", [d for d in target_doc.get("items")
if d.get("item_code") in supplier_items and d.get("qty") > 0])
-
- target_doc.update(apply_price_list(target_doc.as_dict(), as_doc = True))
-
+
set_missing_values(source, target_doc)
for mr in material_requests:
@@ -271,7 +267,6 @@
@frappe.whitelist()
def make_supplier_quotation(source_name, target_doc=None):
def postprocess(source, target_doc):
- target_doc.update(apply_price_list(target_doc.as_dict(), as_doc = True))
set_missing_values(source, target_doc)
doclist = get_mapped_doc("Material Request", source_name, {
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
index d66f1a1..7cf7ae9 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
@@ -55,14 +55,13 @@
}
if(this.frm.doc.docstatus == 1 && this.frm.doc.status!="Closed") {
- if (this.frm.has_perm("submit") &&
- this.frm.doc.__onload && this.frm.doc.__onload.has_return_entry) {
- cur_frm.add_custom_button(__("Close"), this.close_purchase_receipt, __("Status"))
+ if (this.frm.has_perm("submit")) {
+ cur_frm.add_custom_button(__("Close"), this.close_purchase_receipt, __("Status"))
}
cur_frm.add_custom_button(__('Return'), this.make_purchase_return, __("Make"));
- if(this.frm.doc.__onload && !this.frm.doc.__onload.billing_complete) {
+ if(flt(this.frm.doc.per_billed) < 100) {
cur_frm.add_custom_button(__('Invoice'), this.make_purchase_invoice, __("Make"));
}
cur_frm.page.set_inner_btn_group_as_primary(__("Make"));
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
index df62f7d..1d87238 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
@@ -46,16 +46,6 @@
'extra_cond': """ and exists (select name from `tabPurchase Receipt` where name=`tabPurchase Receipt Item`.parent and is_return=1)"""
}]
- def onload(self):
- billed_qty = frappe.db.sql("""select sum(qty) from `tabPurchase Invoice Item`
- where purchase_receipt=%s and docstatus=1""", self.name)
- if billed_qty:
- total_qty = sum((item.qty for item in self.get("items")))
- self.set_onload("billing_complete", (billed_qty[0][0] == total_qty))
-
- self.set_onload("has_return_entry", len(frappe.db.exists({"doctype": "Purchase Receipt",
- "is_return": 1, "return_against": self.name, "docstatus": 1})))
-
def validate(self):
super(PurchaseReceipt, self).validate()
diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py
index 3819337..cc80d1e 100644
--- a/erpnext/stock/get_item_details.py
+++ b/erpnext/stock/get_item_details.py
@@ -6,7 +6,7 @@
from frappe import _, throw
from frappe.utils import flt, cint, add_days, cstr
import json
-from erpnext.accounts.doctype.pricing_rule.pricing_rule import get_pricing_rule_for_item
+from erpnext.accounts.doctype.pricing_rule.pricing_rule import get_pricing_rule_for_item, set_transaction_type
from erpnext.setup.utils import get_exchange_rate
from frappe.model.meta import get_field_precision
@@ -84,6 +84,8 @@
args.item_code = get_item_code(barcode=args.barcode)
elif not args.item_code and args.serial_no:
args.item_code = get_item_code(serial_no=args.serial_no)
+
+ set_transaction_type(args)
return args
@@ -107,7 +109,7 @@
from erpnext.stock.doctype.item.item import validate_end_of_life
validate_end_of_life(item.name, item.end_of_life, item.disabled)
- if args.customer or args.doctype=="Opportunity":
+ if args.transaction_type=="selling":
# validate if sales item or service item
if args.get("order_type") == "Maintenance":
if item.is_service_item != 1:
@@ -119,7 +121,7 @@
if cint(item.has_variants):
throw(_("Item {0} is a template, please select one of its variants").format(item.name))
- elif args.supplier and args.doctype != "Material Request":
+ elif args.transaction_type=="buying" and args.doctype != "Material Request":
# validate if purchase item or subcontracted item
if item.is_purchase_item != 1:
throw(_("Item {0} must be a Purchase Item").format(item.name))
@@ -219,7 +221,7 @@
out.price_list_rate = flt(price_list_rate) * flt(args.plc_conversion_rate) \
/ flt(args.conversion_rate)
- if not out.price_list_rate and args.supplier:
+ if not out.price_list_rate and args.transaction_type=="buying":
from erpnext.stock.doctype.item.item import get_last_purchase_details
out.update(get_last_purchase_details(item_doc.name,
args.name, args.conversion_rate))
@@ -251,7 +253,7 @@
def validate_price_list(args):
if args.get("price_list"):
if not frappe.db.get_value("Price List",
- {"name": args.price_list, "selling" if (args.customer or args.lead) else "buying": 1, "enabled": 1}):
+ {"name": args.price_list, args.transaction_type: 1, "enabled": 1}):
throw(_("Price List {0} is disabled").format(args.price_list))
else:
throw(_("Price List not selected"))
@@ -283,10 +285,11 @@
frappe._dict({"fields": args})))
def get_party_item_code(args, item_doc, out):
- if args.customer:
+ if args.transaction_type=="selling" and args.customer:
customer_item_code = item_doc.get("customer_items", {"customer_name": args.customer})
out.customer_item_code = customer_item_code[0].ref_code if customer_item_code else None
- else:
+
+ if args.transaction_type=="buying" and args.supplier:
item_supplier = item_doc.get("supplier_items", {"supplier": args.supplier})
out.supplier_part_no = item_supplier[0].supplier_part_no if item_supplier else None