Merge branch 'develop' into po_supplier_skip
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index d661bcb..76eb56f 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -19,6 +19,7 @@
from erpnext.exceptions import InvalidCurrency
from six import text_type
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions
+from erpnext.stock.get_item_details import get_item_warehouse
force_item_fields = ("item_group", "brand", "stock_uom", "is_fixed_asset", "item_tax_rate", "pricing_rules")
@@ -1126,16 +1127,16 @@
"""
Returns a Sales Order Item child item containing the default values
"""
- p_doctype = frappe.get_doc(parent_doctype, parent_doctype_name)
- child_item = frappe.new_doc('Sales Order Item', p_doctype, child_docname)
+ 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)
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_doctype.delivery_date
+ child_item.reqd_by_date = 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.warehouse = p_doctype.set_warehouse or p_doctype.items[0].warehouse
+ child_item.warehouse = get_item_warehouse(item, p_doc, overwrite_warehouse=True)
return child_item
@@ -1143,13 +1144,13 @@
"""
Returns a Purchase Order Item child item containing the default values
"""
- p_doctype = frappe.get_doc(parent_doctype, parent_doctype_name)
- child_item = frappe.new_doc('Purchase Order Item', p_doctype, child_docname)
+ 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)
child_item.item_code = item.item_code
child_item.item_name = item.item_name
child_item.description = item.description
- child_item.schedule_date = p_doctype.schedule_date
+ child_item.schedule_date = 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.base_rate = 1 # Initiallize value will update in parent validation
diff --git a/erpnext/manufacturing/doctype/blanket_order/blanket_order.js b/erpnext/manufacturing/doctype/blanket_order/blanket_order.js
index 1cd9446..4c31bd0 100644
--- a/erpnext/manufacturing/doctype/blanket_order/blanket_order.js
+++ b/erpnext/manufacturing/doctype/blanket_order/blanket_order.js
@@ -14,27 +14,37 @@
refresh: function(frm) {
erpnext.hide_company();
if (frm.doc.customer && frm.doc.docstatus === 1) {
- frm.add_custom_button(__('View Orders'), function() {
- frappe.set_route('List', 'Sales Order', {blanket_order: frm.doc.name});
- });
- frm.add_custom_button(__("Create Sales Order"), function(){
+ frm.add_custom_button(__("Sales Order"), function() {
frappe.model.open_mapped_doc({
- method: "erpnext.manufacturing.doctype.blanket_order.blanket_order.make_sales_order",
- frm: frm
+ method: "erpnext.manufacturing.doctype.blanket_order.blanket_order.make_order",
+ frm: frm,
+ args: {
+ doctype: 'Sales Order'
+ }
});
- }).addClass("btn-primary");
+ }, __('Create'));
+
+ frm.add_custom_button(__("Quotation"), function() {
+ frappe.model.open_mapped_doc({
+ method: "erpnext.manufacturing.doctype.blanket_order.blanket_order.make_order",
+ frm: frm,
+ args: {
+ doctype: 'Quotation'
+ }
+ });
+ }, __('Create'));
}
if (frm.doc.supplier && frm.doc.docstatus === 1) {
- frm.add_custom_button(__('View Orders'), function() {
- frappe.set_route('List', 'Purchase Order', {blanket_order: frm.doc.name});
- });
- frm.add_custom_button(__("Create Purchase Order"), function(){
+ frm.add_custom_button(__("Purchase Order"), function(){
frappe.model.open_mapped_doc({
- method: "erpnext.manufacturing.doctype.blanket_order.blanket_order.make_purchase_order",
- frm: frm
+ method: "erpnext.manufacturing.doctype.blanket_order.blanket_order.make_order",
+ frm: frm,
+ args: {
+ doctype: 'Purchase Order'
+ }
});
- }).addClass("btn-primary");
+ }, __('Create'));
}
},
diff --git a/erpnext/manufacturing/doctype/blanket_order/blanket_order.py b/erpnext/manufacturing/doctype/blanket_order/blanket_order.py
index 38118bd..d7556ad 100644
--- a/erpnext/manufacturing/doctype/blanket_order/blanket_order.py
+++ b/erpnext/manufacturing/doctype/blanket_order/blanket_order.py
@@ -14,10 +14,18 @@
class BlanketOrder(Document):
def validate(self):
self.validate_dates()
+ self.validate_duplicate_items()
def validate_dates(self):
if getdate(self.from_date) > getdate(self.to_date):
- frappe.throw(_("From date cannot be greater than To date"))
+ frappe.throw(_("From date cannot be greater than To date"))
+
+ def validate_duplicate_items(self):
+ item_list = []
+ for item in self.items:
+ if item.item_code in item_list:
+ frappe.throw(_("Note: Item {0} added multiple times").format(frappe.bold(item.item_code)))
+ item_list.append(item.item_code)
def update_ordered_qty(self):
ref_doctype = "Sales Order" if self.blanket_order_type == "Selling" else "Purchase Order"
@@ -35,7 +43,14 @@
d.db_set("ordered_qty", item_ordered_qty.get(d.item_code, 0))
@frappe.whitelist()
-def make_sales_order(source_name):
+def make_order(source_name):
+ doctype = frappe.flags.args.doctype
+
+ def update_doc(source_doc, target_doc, source_parent):
+ if doctype == 'Quotation':
+ target_doc.quotation_to = 'Customer'
+ target_doc.party_name = source_doc.customer
+
def update_item(source, target, source_parent):
target_qty = source.get("qty") - source.get("ordered_qty")
target.qty = target_qty if not flt(target_qty) < 0 else 0
@@ -49,39 +64,11 @@
target_doc = get_mapped_doc("Blanket Order", source_name, {
"Blanket Order": {
- "doctype": "Sales Order"
+ "doctype": doctype,
+ "postprocess": update_doc
},
"Blanket Order Item": {
- "doctype": "Sales Order Item",
- "field_map": {
- "rate": "blanket_order_rate",
- "parent": "blanket_order"
- },
- "postprocess": update_item
- }
- })
- return target_doc
-
-@frappe.whitelist()
-def make_purchase_order(source_name):
- def update_item(source, target, source_parent):
- target_qty = source.get("qty") - source.get("ordered_qty")
- target.qty = target_qty if not flt(target_qty) < 0 else 0
- item = get_item_defaults(target.item_code, source_parent.company)
- if item:
- target.item_name = item.get("item_name")
- target.description = item.get("description")
- target.uom = item.get("stock_uom")
- target.warehouse = item.get("default_warehouse")
- target.against_blanket_order = 1
- target.blanket_order = source_name
-
- target_doc = get_mapped_doc("Blanket Order", source_name, {
- "Blanket Order": {
- "doctype": "Purchase Order"
- },
- "Blanket Order Item": {
- "doctype": "Purchase Order Item",
+ "doctype": doctype + " Item",
"field_map": {
"rate": "blanket_order_rate",
"parent": "blanket_order"
diff --git a/erpnext/manufacturing/doctype/blanket_order/blanket_order_dashboard.py b/erpnext/manufacturing/doctype/blanket_order/blanket_order_dashboard.py
index ed319a0..d9aa0ca 100644
--- a/erpnext/manufacturing/doctype/blanket_order/blanket_order_dashboard.py
+++ b/erpnext/manufacturing/doctype/blanket_order/blanket_order_dashboard.py
@@ -6,7 +6,7 @@
'fieldname': 'blanket_order',
'transactions': [
{
- 'items': ['Purchase Order', 'Sales Order']
+ 'items': ['Purchase Order', 'Sales Order', 'Quotation']
}
]
}
diff --git a/erpnext/manufacturing/doctype/blanket_order/test_blanket_order.py b/erpnext/manufacturing/doctype/blanket_order/test_blanket_order.py
index 455ea06..3171def 100644
--- a/erpnext/manufacturing/doctype/blanket_order/test_blanket_order.py
+++ b/erpnext/manufacturing/doctype/blanket_order/test_blanket_order.py
@@ -7,13 +7,17 @@
import unittest
from frappe.utils import add_months, today
from erpnext import get_company_currency
-from .blanket_order import make_sales_order, make_purchase_order
+from .blanket_order import make_order
class TestBlanketOrder(unittest.TestCase):
+ def setUp(self):
+ frappe.flags.args = frappe._dict()
+
def test_sales_order_creation(self):
bo = make_blanket_order(blanket_order_type="Selling")
- so = make_sales_order(bo.name)
+ frappe.flags.args.doctype = 'Sales Order'
+ so = make_order(bo.name)
so.currency = get_company_currency(so.company)
so.delivery_date = today()
so.items[0].qty = 10
@@ -29,7 +33,8 @@
self.assertEqual(so.items[0].qty, bo.items[0].ordered_qty)
# test the quantity
- so1 = make_sales_order(bo.name)
+ frappe.flags.args.doctype = 'Sales Order'
+ so1 = make_order(bo.name)
so1.currency = get_company_currency(so1.company)
self.assertEqual(so1.items[0].qty, (bo.items[0].qty-bo.items[0].ordered_qty))
@@ -37,7 +42,8 @@
def test_purchase_order_creation(self):
bo = make_blanket_order(blanket_order_type="Purchasing")
- po = make_purchase_order(bo.name)
+ frappe.flags.args.doctype = 'Purchase Order'
+ po = make_order(bo.name)
po.currency = get_company_currency(po.company)
po.schedule_date = today()
po.items[0].qty = 10
@@ -53,7 +59,8 @@
self.assertEqual(po.items[0].qty, bo.items[0].ordered_qty)
# test the quantity
- po1 = make_sales_order(bo.name)
+ frappe.flags.args.doctype = 'Purchase Order'
+ po1 = make_order(bo.name)
po1.currency = get_company_currency(po1.company)
self.assertEqual(po1.items[0].qty, (bo.items[0].qty-bo.items[0].ordered_qty))
@@ -78,7 +85,7 @@
"qty": args.quantity or 1000,
"rate": args.rate or 100
})
-
+
bo.insert()
bo.submit()
return bo
\ No newline at end of file
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 8aec8bd..b6e6cb3 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -626,10 +626,11 @@
erpnext.patches.v12_0.create_accounting_dimensions_in_missing_doctypes
erpnext.patches.v11_1.set_status_for_material_request_type_manufacture
erpnext.patches.v12_0.move_plaid_settings_to_doctype
-execute:frappe.reload_doc('desk', 'doctype','dashboard_chart_link')
-execute:frappe.reload_doc('desk', 'doctype','dashboard')
-execute:frappe.reload_doc('desk', 'doctype','dashboard_chart_source')
-execute:frappe.reload_doc('desk', 'doctype','dashboard_chart')
+execute:frappe.reload_doc('desk', 'doctype', 'dashboard_chart_link')
+execute:frappe.reload_doc('desk', 'doctype', 'dashboard')
+execute:frappe.reload_doc('desk', 'doctype', 'dashboard_chart_source')
+execute:frappe.reload_doc('desk', 'doctype', 'dashboard_chart')
+execute:frappe.reload_doc('desk', 'doctype', 'dashboard_chart_field')
erpnext.patches.v12_0.add_default_dashboards
erpnext.patches.v12_0.remove_bank_remittance_custom_fields
erpnext.patches.v12_0.generate_leave_ledger_entries
@@ -660,3 +661,4 @@
erpnext.patches.v12_0.create_irs_1099_field_united_states
erpnext.patches.v12_0.move_bank_account_swift_number_to_bank
erpnext.patches.v12_0.rename_bank_reconciliation_fields # 2020-01-22
+erpnext.patches.v12_0.set_received_qty_in_material_request_as_per_stock_uom
\ No newline at end of file
diff --git a/erpnext/patches/v12_0/set_received_qty_in_material_request_as_per_stock_uom.py b/erpnext/patches/v12_0/set_received_qty_in_material_request_as_per_stock_uom.py
new file mode 100644
index 0000000..88c3e2e
--- /dev/null
+++ b/erpnext/patches/v12_0/set_received_qty_in_material_request_as_per_stock_uom.py
@@ -0,0 +1,30 @@
+from __future__ import unicode_literals
+import frappe
+
+def execute():
+ purchase_receipts = frappe.db.sql("""
+ SELECT
+ parent from `tabPurchase Receipt Item`
+ WHERE
+ material_request is not null
+ AND docstatus=1
+ """,as_dict=1)
+
+ purchase_receipts = set([d.parent for d in purchase_receipts])
+
+ for pr in purchase_receipts:
+ doc = frappe.get_doc("Purchase Receipt", pr)
+ doc.status_updater = [
+ {
+ 'source_dt': 'Purchase Receipt Item',
+ 'target_dt': 'Material Request Item',
+ 'join_field': 'material_request_item',
+ 'target_field': 'received_qty',
+ 'target_parent_dt': 'Material Request',
+ 'target_parent_field': 'per_received',
+ 'target_ref_field': 'stock_qty',
+ 'source_field': 'stock_qty',
+ 'percent_join_field': 'material_request'
+ }
+ ]
+ doc.update_qty()
diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js
index 9a5b750..dbe48ec 100644
--- a/erpnext/public/js/controllers/taxes_and_totals.js
+++ b/erpnext/public/js/controllers/taxes_and_totals.js
@@ -6,7 +6,7 @@
apply_pricing_rule_on_item: function(item){
let effective_item_rate = item.price_list_rate;
- if (item.parenttype === "Sales Order" && item.blanket_order_rate) {
+ if (in_list(["Sales Order", "Quotation"], item.parenttype) && item.blanket_order_rate) {
effective_item_rate = item.blanket_order_rate;
}
if(item.margin_type == "Percentage"){
diff --git a/erpnext/public/js/education/lms/quiz.js b/erpnext/public/js/education/lms/quiz.js
index 5248129..91cbbf4 100644
--- a/erpnext/public/js/education/lms/quiz.js
+++ b/erpnext/public/js/education/lms/quiz.js
@@ -29,7 +29,7 @@
this.questions.push(question)
this.wrapper.appendChild(question_wrapper);
})
- if (data.activity.is_complete) {
+ if (data.activity && data.activity.is_complete) {
this.disable()
let indicator = 'red'
let message = 'Your are not allowed to attempt the quiz again.'
diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js
index 4eb3175..4d44eae 100755
--- a/erpnext/public/js/utils.js
+++ b/erpnext/public/js/utils.js
@@ -453,7 +453,8 @@
fields: [{
fieldtype:'Data',
fieldname:"docname",
- hidden: 0,
+ read_only: 1,
+ hidden: 1,
}, {
fieldtype:'Link',
fieldname:"item_code",
diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py
index b79c91c..7c47b8a 100644
--- a/erpnext/selling/doctype/quotation/quotation.py
+++ b/erpnext/selling/doctype/quotation/quotation.py
@@ -155,6 +155,11 @@
def update_item(obj, target, source_parent):
target.stock_qty = flt(obj.qty) * flt(obj.conversion_factor)
+ if obj.against_blanket_order:
+ target.against_blanket_order = obj.against_blanket_order
+ target.blanket_order = obj.blanket_order
+ target.blanket_order_rate = obj.blanket_order_rate
+
doclist = get_mapped_doc("Quotation", source_name, {
"Quotation": {
"doctype": "Sales Order",
diff --git a/erpnext/selling/doctype/quotation_item/quotation_item.json b/erpnext/selling/doctype/quotation_item/quotation_item.json
index 3ff5555..d50397c 100644
--- a/erpnext/selling/doctype/quotation_item/quotation_item.json
+++ b/erpnext/selling/doctype/quotation_item/quotation_item.json
@@ -55,6 +55,9 @@
"weight_uom",
"reference",
"warehouse",
+ "against_blanket_order",
+ "blanket_order",
+ "blanket_order_rate",
"column_break_30",
"prevdoc_doctype",
"prevdoc_docname",
@@ -573,12 +576,38 @@
"fieldname": "image_section",
"fieldtype": "Section Break",
"label": "Image"
+ },
+ {
+ "depends_on": "eval:doc.against_blanket_order",
+ "fieldname": "blanket_order",
+ "fieldtype": "Link",
+ "label": "Blanket Order",
+ "no_copy": 1,
+ "options": "Blanket Order",
+ "print_hide": 1
+ },
+ {
+ "depends_on": "eval:doc.against_blanket_order",
+ "fieldname": "blanket_order_rate",
+ "fieldtype": "Currency",
+ "label": "Blanket Order Rate",
+ "no_copy": 1,
+ "print_hide": 1,
+ "read_only": 1
+ },
+ {
+ "default": "0",
+ "fieldname": "against_blanket_order",
+ "fieldtype": "Check",
+ "label": "Against Blanket Order",
+ "no_copy": 1,
+ "print_hide": 1
}
],
"idx": 1,
"istable": 1,
"links": [],
- "modified": "2020-03-05 14:18:58.783751",
+ "modified": "2020-03-30 18:40:28.782720",
"modified_by": "Administrator",
"module": "Selling",
"name": "Quotation Item",
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
index 3b43690..c2b3892 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
@@ -50,8 +50,8 @@
'target_field': 'received_qty',
'target_parent_dt': 'Material Request',
'target_parent_field': 'per_received',
- 'target_ref_field': 'qty',
- 'source_field': 'qty',
+ 'target_ref_field': 'stock_qty',
+ 'source_field': 'stock_qty',
'percent_join_field': 'material_request'
}]
if cint(self.is_return):
@@ -357,7 +357,7 @@
if warehouse_with_no_account:
frappe.msgprint(_("No accounting entries for the following warehouses") + ": \n" +
"\n".join(warehouse_with_no_account))
-
+
return process_gl_map(gl_entries)
def get_asset_gl_entry(self, gl_entries):
@@ -628,7 +628,7 @@
if not landed_cost_vouchers:
return
-
+
item_account_wise_cost = {}
for lcv in landed_cost_vouchers:
diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py
index 53964f2..9c5a8e1 100644
--- a/erpnext/stock/get_item_details.py
+++ b/erpnext/stock/get_item_details.py
@@ -240,26 +240,13 @@
item_group_defaults = get_item_group_defaults(item.name, args.company)
brand_defaults = get_brand_defaults(item.name, args.company)
- if overwrite_warehouse or not args.warehouse:
- warehouse = (
- args.get("set_warehouse") or
- item_defaults.get("default_warehouse") or
- item_group_defaults.get("default_warehouse") or
- brand_defaults.get("default_warehouse") or
- args.warehouse
- )
-
- if not warehouse:
- defaults = frappe.defaults.get_defaults() or {}
- warehouse_exists = frappe.db.exists("Warehouse", {
- 'name': defaults.default_warehouse,
- 'company': args.company
- })
- if defaults.get("default_warehouse") and warehouse_exists:
- warehouse = defaults.default_warehouse
-
- else:
- warehouse = args.warehouse
+ defaults = frappe._dict({
+ 'item_defaults': item_defaults,
+ 'item_group_defaults': item_group_defaults,
+ 'brand_defaults': brand_defaults
+ })
+
+ warehouse = get_item_warehouse(item, args, overwrite_warehouse, defaults)
if args.get('doctype') == "Material Request" and not args.get('material_request_type'):
args['material_request_type'] = frappe.db.get_value('Material Request',
@@ -272,7 +259,7 @@
expense_account = get_asset_category_account(fieldname = "fixed_asset_account", item = args.item_code, company= args.company)
#Set the UOM to the Default Sales UOM or Default Purchase UOM if configured in the Item Master
- if not args.uom:
+ if not args.get('uom'):
if args.get('doctype') in sales_doctypes:
args.uom = item.sales_uom if item.sales_uom else item.stock_uom
elif (args.get('doctype') in ['Purchase Order', 'Purchase Receipt', 'Purchase Invoice']) or \
@@ -362,6 +349,37 @@
return out
+def get_item_warehouse(item, args, overwrite_warehouse, defaults={}):
+ if not defaults:
+ defaults = frappe._dict({
+ 'item_defaults' : get_item_defaults(item.name, args.company),
+ 'item_group_defaults' : get_item_group_defaults(item.name, args.company),
+ 'brand_defaults' : get_brand_defaults(item.name, args.company)
+ })
+
+ if overwrite_warehouse or not args.warehouse:
+ warehouse = (
+ args.get("set_warehouse") or
+ defaults.item_defaults.get("default_warehouse") or
+ defaults.item_group_defaults.get("default_warehouse") or
+ defaults.brand_defaults.get("default_warehouse") or
+ args.get('warehouse')
+ )
+
+ if not warehouse:
+ defaults = frappe.defaults.get_defaults() or {}
+ warehouse_exists = frappe.db.exists("Warehouse", {
+ 'name': defaults.default_warehouse,
+ 'company': args.company
+ })
+ if defaults.get("default_warehouse") and warehouse_exists:
+ warehouse = defaults.default_warehouse
+
+ else:
+ warehouse = args.get('warehouse')
+
+ return warehouse
+
def update_barcode_value(out):
from erpnext.accounts.doctype.sales_invoice.pos import get_barcode_data
barcode_data = get_barcode_data([out])
diff --git a/erpnext/www/lms/content.html b/erpnext/www/lms/content.html
index 5607c08..cdc7141 100644
--- a/erpnext/www/lms/content.html
+++ b/erpnext/www/lms/content.html
@@ -63,7 +63,7 @@
</a>
</div>
<div>
- <h1>{{ content.name }} <span class="small text-muted">({{ position + 1 }}/{{length}})</span></h1>
+ <h2>{{ content.name }} <span class="small text-muted">({{ position + 1 }}/{{length}})</span></h2>
</div>
{% endmacro %}