diff --git a/erpnext/accounts/doctype/bank_account/bank_account.py b/erpnext/accounts/doctype/bank_account/bank_account.py
index 20ce7ca9a4..3e08c2812e 100644
--- a/erpnext/accounts/doctype/bank_account/bank_account.py
+++ b/erpnext/accounts/doctype/bank_account/bank_account.py
@@ -48,7 +48,10 @@ class BankAccount(Document):
# Encode characters as numbers
encoded = [encode_char(c) if ord(c) >= 65 and ord(c) <= 90 else c for c in flipped]
- to_check = int(''.join(encoded))
+ try:
+ to_check = int(''.join(encoded))
+ except ValueError:
+ frappe.throw(_('IBAN is not valid'))
if to_check % 97 != 1:
frappe.throw(_('IBAN is not valid'))
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js
index b6c48c81b9..3dbf4d40eb 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.js
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js
@@ -223,7 +223,10 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({
if(in_list(["Sales Invoice", "Purchase Invoice"], jvd.reference_type)) {
out.filters.push([jvd.reference_type, "outstanding_amount", "!=", 0]);
-
+ // Filter by cost center
+ if(jvd.cost_center) {
+ out.filters.push([jvd.reference_type, "cost_center", "in", ["", jvd.cost_center]]);
+ }
// account filter
frappe.model.validate_missing(jvd, "account");
var party_account_field = jvd.reference_type==="Sales Invoice" ? "debit_to": "credit_to";
diff --git a/erpnext/accounts/doctype/promotional_scheme_product_discount/promotional_scheme_product_discount.json b/erpnext/accounts/doctype/promotional_scheme_product_discount/promotional_scheme_product_discount.json
index b87725ff54..72d53bfa01 100644
--- a/erpnext/accounts/doctype/promotional_scheme_product_discount/promotional_scheme_product_discount.json
+++ b/erpnext/accounts/doctype/promotional_scheme_product_discount/promotional_scheme_product_discount.json
@@ -1,751 +1,166 @@
{
- "allow_copy": 0,
- "allow_events_in_timeline": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "beta": 0,
"creation": "2019-03-24 14:48:59.649168",
- "custom": 0,
- "docstatus": 0,
"doctype": "DocType",
- "document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
+ "field_order": [
+ "disable",
+ "column_break_2",
+ "rule_description",
+ "section_break_1",
+ "min_qty",
+ "max_qty",
+ "column_break_3",
+ "min_amount",
+ "max_amount",
+ "section_break_6",
+ "same_item",
+ "free_item",
+ "free_qty",
+ "column_break_9",
+ "free_item_uom",
+ "free_item_rate",
+ "section_break_12",
+ "warehouse",
+ "threshold_percentage",
+ "column_break_15",
+ "priority",
+ "apply_multiple_pricing_rules"
+ ],
"fields": [
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
+ "default": "0",
"fieldname": "disable",
- "fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Disable",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "fieldtype": "Check",
+ "label": "Disable"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "column_break_2",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "fieldtype": "Column Break"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "rule_description",
"fieldtype": "Small Text",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
"label": "Rule Description",
- "length": 0,
"no_copy": 1,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "reqd": 1
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "section_break_1",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "fieldtype": "Section Break"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"default": "0",
"fieldname": "min_qty",
"fieldtype": "Float",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
"in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Min Qty",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "label": "Min Qty"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"default": "0",
"fieldname": "max_qty",
"fieldtype": "Float",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
"in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Max Qty",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "label": "Max Qty"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "column_break_3",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "fieldtype": "Column Break"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"default": "0",
"fieldname": "min_amount",
"fieldtype": "Currency",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
"in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Min Amount",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "label": "Min Amount"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"default": "0",
"fieldname": "max_amount",
"fieldtype": "Currency",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
"in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Max Amount",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "label": "Max Amount"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "section_break_6",
"fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Free Item",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "label": "Free Item"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
+ "default": "0",
"depends_on": "eval:!parent.mixed_conditions",
"fieldname": "same_item",
"fieldtype": "Check",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Same Item",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "label": "Same Item"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"depends_on": "eval:!doc.same_item || parent.mixed_conditions",
"fieldname": "free_item",
"fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
"in_list_view": 1,
- "in_standard_filter": 0,
"label": "Item Code",
- "length": 0,
- "no_copy": 0,
- "options": "Item",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "options": "Item"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "free_qty",
"fieldtype": "Float",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
"in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Qty",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "label": "Qty"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "column_break_9",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "fieldtype": "Column Break"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "free_item_uom",
"fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
"label": "UOM",
- "length": 0,
- "no_copy": 0,
- "options": "UOM",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "options": "UOM"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "free_item_rate",
"fieldtype": "Currency",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Rate",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "label": "Rate"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "section_break_12",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "fieldtype": "Section Break"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "warehouse",
"fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
"label": "Warehouse",
- "length": 0,
- "no_copy": 0,
- "options": "Warehouse",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "options": "Warehouse"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "threshold_percentage",
"fieldtype": "Percent",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Threshold for Suggestion",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "label": "Threshold for Suggestion"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "column_break_15",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "fieldtype": "Column Break"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "priority",
"fieldtype": "Select",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
"label": "Priority",
- "length": 0,
- "no_copy": 0,
- "options": "\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "options": "\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
+ "default": "0",
"fieldname": "apply_multiple_pricing_rules",
"fieldtype": "Check",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Apply Multiple Pricing Rules",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "label": "Apply Multiple Pricing Rules"
}
],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
- "idx": 0,
- "image_view": 0,
- "in_create": 0,
- "is_submittable": 0,
- "issingle": 0,
"istable": 1,
- "max_attachments": 0,
- "modified": "2019-03-24 14:48:59.649168",
+ "modified": "2019-07-21 00:00:56.674284",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Promotional Scheme Product Discount",
- "name_case": "",
"owner": "Administrator",
"permissions": [],
- "quick_entry": 0,
- "read_only": 0,
- "read_only_onload": 0,
- "show_name_in_global_search": 0,
"sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 0,
- "track_seen": 0,
- "track_views": 0
+ "sort_order": "DESC"
}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
index 219d9899d2..bc2ddffbcf 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -336,6 +336,7 @@ class PurchaseInvoice(BuyingController):
if not self.is_return:
self.update_against_document_in_jv()
+ self.update_billing_status_for_zero_amount_refdoc("Purchase Receipt")
self.update_billing_status_for_zero_amount_refdoc("Purchase Order")
self.update_billing_status_in_pr()
@@ -774,6 +775,7 @@ class PurchaseInvoice(BuyingController):
self.update_prevdoc_status()
if not self.is_return:
+ self.update_billing_status_for_zero_amount_refdoc("Purchase Receipt")
self.update_billing_status_for_zero_amount_refdoc("Purchase Order")
self.update_billing_status_in_pr()
diff --git a/erpnext/accounts/doctype/sales_invoice/pos.py b/erpnext/accounts/doctype/sales_invoice/pos.py
index 3e013f5d6b..e2f99d6ea3 100755
--- a/erpnext/accounts/doctype/sales_invoice/pos.py
+++ b/erpnext/accounts/doctype/sales_invoice/pos.py
@@ -451,6 +451,10 @@ def make_customer_and_address(customers):
def add_customer(data):
+ customer = data.get('full_name') or data.get('customer')
+ if frappe.db.exists("Customer", customer.strip()):
+ return customer.strip()
+
customer_doc = frappe.new_doc('Customer')
customer_doc.customer_name = data.get('full_name') or data.get('customer')
customer_doc.customer_pos_id = data.get('customer_pos_id')
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index 88ad6b37db..874230052a 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -166,6 +166,7 @@ class SalesInvoice(SellingController):
self.make_gl_entries()
if not self.is_return:
+ self.update_billing_status_for_zero_amount_refdoc("Delivery Note")
self.update_billing_status_for_zero_amount_refdoc("Sales Order")
self.check_credit_limit()
@@ -220,6 +221,7 @@ class SalesInvoice(SellingController):
self.update_billing_status_in_dn()
if not self.is_return:
+ self.update_billing_status_for_zero_amount_refdoc("Delivery Note")
self.update_billing_status_for_zero_amount_refdoc("Sales Order")
self.update_serial_no(in_cancel=True)
@@ -395,14 +397,17 @@ class SalesInvoice(SellingController):
if pos.get('account_for_change_amount'):
self.account_for_change_amount = pos.get('account_for_change_amount')
- for fieldname in ('territory', 'naming_series', 'currency', 'taxes_and_charges', 'letter_head', 'tc_name',
- 'company', 'select_print_heading', 'cash_bank_account', 'company_address',
- 'write_off_account', 'write_off_cost_center', 'apply_discount_on', 'cost_center'):
+ for fieldname in ('territory', 'naming_series', 'currency', 'letter_head', 'tc_name',
+ 'company', 'select_print_heading', 'cash_bank_account', 'write_off_account', 'taxes_and_charges',
+ 'write_off_cost_center', 'apply_discount_on', 'cost_center'):
if (not for_validate) or (for_validate and not self.get(fieldname)):
self.set(fieldname, pos.get(fieldname))
customer_price_list = frappe.get_value("Customer", self.customer, 'default_price_list')
+ if pos.get("company_address"):
+ self.company_address = pos.get("company_address")
+
if not customer_price_list:
self.set('selling_price_list', pos.get('selling_price_list'))
@@ -1255,9 +1260,8 @@ def validate_inter_company_party(doctype, party, company, inter_company_referenc
frappe.throw(_("Invalid Company for Inter Company Transaction."))
elif frappe.db.get_value(partytype, {"name": party, internal: 1}, "name") == party:
- companies = frappe.db.sql("""select company from `tabAllowed To Transact With`
- where parenttype = '{0}' and parent = '{1}'""".format(partytype, party), as_list = 1)
- companies = [d[0] for d in companies]
+ companies = frappe.get_all("Allowed To Transact With", fields=["company"], filters={"parenttype": partytype, "parent": party})
+ companies = [d.company for d in companies]
if not company in companies:
frappe.throw(_("{0} not allowed to transact with {1}. Please change the Company.").format(partytype, company))
diff --git a/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py b/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py
index eca59750d5..1923f78cf8 100644
--- a/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py
+++ b/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py
@@ -12,11 +12,11 @@ def execute(filters=None):
columns = get_columns()
if not filters.get("account"): return columns, []
-
+
account_currency = frappe.db.get_value("Account", filters.account, "account_currency")
data = get_entries(filters)
-
+
from erpnext.accounts.utils import get_balance_on
balance_as_per_system = get_balance_on(filters["account"], filters["report_date"])
@@ -24,7 +24,7 @@ def execute(filters=None):
for d in data:
total_debit += flt(d.debit)
total_credit += flt(d.credit)
-
+
amounts_not_reflected_in_system = get_amounts_not_reflected_in_system(filters)
bank_bal = flt(balance_as_per_system) - flt(total_debit) + flt(total_credit) \
@@ -39,7 +39,7 @@ def execute(filters=None):
"credit": total_credit,
"account_currency": account_currency
},
- get_balance_row(_("Cheques and Deposits incorrectly cleared"), amounts_not_reflected_in_system,
+ get_balance_row(_("Cheques and Deposits incorrectly cleared"), amounts_not_reflected_in_system,
account_currency),
{},
get_balance_row(_("Calculated Bank Statement balance"), bank_bal, account_currency)
@@ -55,9 +55,16 @@ def get_columns():
"fieldtype": "Date",
"width": 90
},
+ {
+ "fieldname": "payment_document",
+ "label": _("Payment Document Type"),
+ "fieldtype": "Link",
+ "options": "DocType",
+ "width": 220
+ },
{
"fieldname": "payment_entry",
- "label": _("Payment Entry"),
+ "label": _("Payment Document"),
"fieldtype": "Dynamic Link",
"options": "payment_document",
"width": 220
@@ -100,7 +107,7 @@ def get_columns():
"label": _("Clearance Date"),
"fieldtype": "Date",
"width": 110
- },
+ },
{
"fieldname": "account_currency",
"label": _("Currency"),
@@ -112,9 +119,9 @@ def get_columns():
def get_entries(filters):
journal_entries = frappe.db.sql("""
- select "Journal Entry" as payment_document, jv.posting_date,
- jv.name as payment_entry, jvd.debit_in_account_currency as debit,
- jvd.credit_in_account_currency as credit, jvd.against_account,
+ select "Journal Entry" as payment_document, jv.posting_date,
+ jv.name as payment_entry, jvd.debit_in_account_currency as debit,
+ jvd.credit_in_account_currency as credit, jvd.against_account,
jv.cheque_no as reference_no, jv.cheque_date as ref_date, jv.clearance_date, jvd.account_currency
from
`tabJournal Entry Account` jvd, `tabJournal Entry` jv
@@ -122,13 +129,13 @@ def get_entries(filters):
and jvd.account = %(account)s and jv.posting_date <= %(report_date)s
and ifnull(jv.clearance_date, '4000-01-01') > %(report_date)s
and ifnull(jv.is_opening, 'No') = 'No'""", filters, as_dict=1)
-
+
payment_entries = frappe.db.sql("""
- select
- "Payment Entry" as payment_document, name as payment_entry,
- reference_no, reference_date as ref_date,
- if(paid_to=%(account)s, received_amount, 0) as debit,
- if(paid_from=%(account)s, paid_amount, 0) as credit,
+ select
+ "Payment Entry" as payment_document, name as payment_entry,
+ reference_no, reference_date as ref_date,
+ if(paid_to=%(account)s, received_amount, 0) as debit,
+ if(paid_from=%(account)s, paid_amount, 0) as credit,
posting_date, ifnull(party,if(paid_from=%(account)s,paid_to,paid_from)) as against_account, clearance_date,
if(paid_to=%(account)s, paid_to_account_currency, paid_from_account_currency) as account_currency
from `tabPayment Entry`
@@ -156,25 +163,25 @@ def get_entries(filters):
return sorted(list(payment_entries)+list(journal_entries+list(pos_entries)),
key=lambda k: k['posting_date'] or getdate(nowdate()))
-
+
def get_amounts_not_reflected_in_system(filters):
je_amount = frappe.db.sql("""
select sum(jvd.debit_in_account_currency - jvd.credit_in_account_currency)
from `tabJournal Entry Account` jvd, `tabJournal Entry` jv
where jvd.parent = jv.name and jv.docstatus=1 and jvd.account=%(account)s
- and jv.posting_date > %(report_date)s and jv.clearance_date <= %(report_date)s
+ and jv.posting_date > %(report_date)s and jv.clearance_date <= %(report_date)s
and ifnull(jv.is_opening, 'No') = 'No' """, filters)
je_amount = flt(je_amount[0][0]) if je_amount else 0.0
-
+
pe_amount = frappe.db.sql("""
select sum(if(paid_from=%(account)s, paid_amount, received_amount))
from `tabPayment Entry`
- where (paid_from=%(account)s or paid_to=%(account)s) and docstatus=1
+ where (paid_from=%(account)s or paid_to=%(account)s) and docstatus=1
and posting_date > %(report_date)s and clearance_date <= %(report_date)s""", filters)
pe_amount = flt(pe_amount[0][0]) if pe_amount else 0.0
-
+
return je_amount + pe_amount
def get_balance_row(label, amount, account_currency):
diff --git a/erpnext/accounts/report/cash_flow/cash_flow.py b/erpnext/accounts/report/cash_flow/cash_flow.py
index cd3d8dc125..98c25b7514 100644
--- a/erpnext/accounts/report/cash_flow/cash_flow.py
+++ b/erpnext/accounts/report/cash_flow/cash_flow.py
@@ -125,8 +125,9 @@ def get_account_type_based_data(company, account_type, period_list, accumulated_
data["total"] = total
return data
-def get_account_type_based_gl_data(company, start_date, end_date, account_type, filters):
+def get_account_type_based_gl_data(company, start_date, end_date, account_type, filters={}):
cond = ""
+ filters = frappe._dict(filters)
if filters.finance_book:
cond = " and finance_book = %s" %(frappe.db.escape(filters.finance_book))
@@ -187,7 +188,7 @@ def get_chart_data(columns, data):
},
"type": "bar"
}
-
+
chart["fieldtype"] = "Currency"
return chart
diff --git a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py
index c40310b193..418a23c2d7 100644
--- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py
+++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py
@@ -130,7 +130,7 @@ def get_cash_flow_data(fiscal_year, companies, filters):
section_data.append(net_profit_loss)
for account in cash_flow_account['account_types']:
- account_data = get_account_type_based_data(account['account_type'], companies, fiscal_year)
+ account_data = get_account_type_based_data(account['account_type'], companies, fiscal_year, filters)
account_data.update({
"account_name": account['label'],
"account": account['label'],
@@ -148,12 +148,12 @@ def get_cash_flow_data(fiscal_year, companies, filters):
return data
-def get_account_type_based_data(account_type, companies, fiscal_year):
+def get_account_type_based_data(account_type, companies, fiscal_year, filters):
data = {}
total = 0
for company in companies:
amount = get_account_type_based_gl_data(company,
- fiscal_year.year_start_date, fiscal_year.year_end_date, account_type)
+ fiscal_year.year_start_date, fiscal_year.year_end_date, account_type, filters)
if amount and account_type == "Depreciation":
amount *= -1
diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py
index e41c74cac8..e1ed642e73 100644
--- a/erpnext/accounts/utils.py
+++ b/erpnext/accounts/utils.py
@@ -121,7 +121,12 @@ def get_balance_on(account=None, date=None, party_type=None, party=None, company
allow_cost_center_in_entry_of_bs_account = get_allow_cost_center_in_entry_of_bs_account()
- if cost_center and (allow_cost_center_in_entry_of_bs_account or acc.report_type =='Profit and Loss'):
+ if account:
+ report_type = acc.report_type
+ else:
+ report_type = ""
+
+ if cost_center and (allow_cost_center_in_entry_of_bs_account or report_type =='Profit and Loss'):
cc = frappe.get_doc("Cost Center", cost_center)
if cc.is_group:
cond.append(""" exists (
@@ -138,7 +143,7 @@ def get_balance_on(account=None, date=None, party_type=None, party=None, company
if not frappe.flags.ignore_account_permission:
acc.check_permission("read")
- if acc.report_type == 'Profit and Loss':
+ if report_type == 'Profit and Loss':
# for pl accounts, get balance within a fiscal year
cond.append("posting_date >= '%s' and voucher_type != 'Period Closing Voucher'" \
% year_start_date)
@@ -685,7 +690,7 @@ def get_outstanding_invoices(party_type, party, account, condition=None, filters
payment_amount = pe_map.get((d.voucher_type, d.voucher_no), 0)
outstanding_amount = flt(d.invoice_amount - payment_amount, precision)
if outstanding_amount > 0.5 / (10**precision):
- if (filters.get("outstanding_amt_greater_than") and
+ if (filters and filters.get("outstanding_amt_greater_than") and
not (outstanding_amount >= filters.get("outstanding_amt_greater_than") and
outstanding_amount <= filters.get("outstanding_amt_less_than"))):
continue
@@ -730,7 +735,6 @@ def get_children(doctype, parent, company, is_root=False):
parent_fieldname = 'parent_' + doctype.lower().replace(' ', '_')
fields = [
'name as value',
- 'root_type',
'is_group as expandable'
]
filters = [['docstatus', '<', 2]]
@@ -738,11 +742,11 @@ def get_children(doctype, parent, company, is_root=False):
filters.append(['ifnull(`{0}`,"")'.format(parent_fieldname), '=', '' if is_root else parent])
if is_root:
- fields += ['report_type', 'account_currency'] if doctype == 'Account' else []
+ fields += ['root_type', 'report_type', 'account_currency'] if doctype == 'Account' else []
filters.append(['company', '=', company])
else:
- fields += ['account_currency'] if doctype == 'Account' else []
+ fields += ['root_type', 'account_currency'] if doctype == 'Account' else []
fields += [parent_fieldname + ' as parent']
acc = frappe.get_list(doctype, fields=fields, filters=filters)
diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py
index 45f7b30ae8..c398a7342a 100644
--- a/erpnext/assets/doctype/asset/asset.py
+++ b/erpnext/assets/doctype/asset/asset.py
@@ -388,7 +388,8 @@ class Asset(AccountsController):
"remarks": self.get("remarks") or _("Accounting Entry for Asset"),
"posting_date": self.available_for_use_date,
"credit": self.purchase_receipt_amount,
- "credit_in_account_currency": self.purchase_receipt_amount
+ "credit_in_account_currency": self.purchase_receipt_amount,
+ "cost_center": self.cost_center
}))
gl_entries.append(self.get_gl_dict({
@@ -397,7 +398,8 @@ class Asset(AccountsController):
"remarks": self.get("remarks") or _("Accounting Entry for Asset"),
"posting_date": self.available_for_use_date,
"debit": self.purchase_receipt_amount,
- "debit_in_account_currency": self.purchase_receipt_amount
+ "debit_in_account_currency": self.purchase_receipt_amount,
+ "cost_center": self.cost_center
}))
if gl_entries:
diff --git a/erpnext/buying/report/procurement_tracker/procurement_tracker.json b/erpnext/buying/report/procurement_tracker/procurement_tracker.json
index 028736c7c4..7e1b165a46 100644
--- a/erpnext/buying/report/procurement_tracker/procurement_tracker.json
+++ b/erpnext/buying/report/procurement_tracker/procurement_tracker.json
@@ -7,7 +7,7 @@
"doctype": "Report",
"idx": 0,
"is_standard": "Yes",
- "modified": "2019-03-29 17:18:06.678728",
+ "modified": "2019-07-21 23:24:21.094269",
"modified_by": "Administrator",
"module": "Buying",
"name": "Procurement Tracker",
@@ -16,5 +16,12 @@
"ref_doctype": "Purchase Order",
"report_name": "Procurement Tracker",
"report_type": "Script Report",
- "roles": []
+ "roles": [
+ {
+ "role": "Purchase Manager"
+ },
+ {
+ "role": "Purchase User"
+ }
+ ]
}
\ No newline at end of file
diff --git a/erpnext/buying/report/procurement_tracker/procurement_tracker.py b/erpnext/buying/report/procurement_tracker/procurement_tracker.py
index d3ee447545..48295bee26 100644
--- a/erpnext/buying/report/procurement_tracker/procurement_tracker.py
+++ b/erpnext/buying/report/procurement_tracker/procurement_tracker.py
@@ -150,10 +150,10 @@ def get_conditions(filters):
"""% (filters.get('cost_center'), filters.get('project'))
if filters.get("from_date"):
- conditions += "AND transaction_date>=%s"% filters.get('from_date')
+ conditions += " AND transaction_date>=%s"% filters.get('from_date')
if filters.get("to_date"):
- conditions += "AND transaction_date<=%s"% filters.get('to_date')
+ conditions += " AND transaction_date<=%s"% filters.get('to_date')
return conditions
def get_data(filters):
diff --git a/erpnext/config/buying.py b/erpnext/config/buying.py
index e0f4be9233..f5d8da74c5 100644
--- a/erpnext/config/buying.py
+++ b/erpnext/config/buying.py
@@ -157,6 +157,13 @@ def get_data():
"reference_doctype": "Purchase Order",
"onboard": 1,
},
+ {
+ "type": "report",
+ "is_query_report": True,
+ "name": "Procurement Tracker",
+ "reference_doctype": "Purchase Order",
+ "onboard": 1,
+ },
{
"type": "report",
"is_query_report": True,
@@ -228,5 +235,5 @@ def get_data():
}
]
},
-
+
]
diff --git a/erpnext/config/crm.py b/erpnext/config/crm.py
index e49fc60f63..70784f3d5f 100644
--- a/erpnext/config/crm.py
+++ b/erpnext/config/crm.py
@@ -141,6 +141,11 @@ def get_data():
"name": "Campaign",
"description": _("Sales campaigns."),
},
+ {
+ "type": "doctype",
+ "name": "Email Campaign",
+ "description": _("Sends Mails to lead or contact based on a Campaign schedule"),
+ },
{
"type": "doctype",
"name": "SMS Center",
diff --git a/erpnext/config/hr.py b/erpnext/config/hr.py
index 261edaf942..0367755595 100644
--- a/erpnext/config/hr.py
+++ b/erpnext/config/hr.py
@@ -219,6 +219,11 @@ def get_data():
"name": "Employee Onboarding",
"dependencies": ["Job Applicant"],
},
+ {
+ "type": "doctype",
+ "name": "Employee Skill Map",
+ "dependencies": ["Employee"],
+ },
{
"type": "doctype",
"name": "Employee Promotion",
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index 47f56cbfb1..ca59a396b8 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -861,7 +861,7 @@ class AccountsController(TransactionBase):
if self.doctype in ("Sales Invoice", "Purchase Invoice"):
grand_total = grand_total - flt(self.write_off_amount)
- if total != grand_total:
+ if total != flt(grand_total, self.precision("grand_total")):
frappe.throw(_("Total Payment Amount in Payment Schedule must be equal to Grand / Rounded Total"))
def is_rounded_total_disabled(self):
diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py
index 2c46db0935..0b4d38ce5c 100644
--- a/erpnext/controllers/buying_controller.py
+++ b/erpnext/controllers/buying_controller.py
@@ -636,7 +636,8 @@ class BuyingController(StockController):
asset.set_missing_values()
asset.insert()
- frappe.msgprint(_("Asset {0} created").format(asset.name))
+ asset_link = frappe.utils.get_link_to_form('Asset', asset.name)
+ frappe.msgprint(_("Asset {0} created").format(asset_link))
return asset.name
def make_asset_movement(self, row):
diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py
index 47c9f0a4ce..57c063a72a 100644
--- a/erpnext/controllers/queries.py
+++ b/erpnext/controllers/queries.py
@@ -207,10 +207,10 @@ def bom(doctype, txt, searchfield, start, page_len, filters):
idx desc, name
limit %(start)s, %(page_len)s """.format(
fcond=get_filters_cond(doctype, filters, conditions).replace('%', '%%'),
- mcond=get_match_cond(doctype),
- key=frappe.db.escape(searchfield)),
+ mcond=get_match_cond(doctype).replace('%', '%%'),
+ key=searchfield),
{
- 'txt': "%"+frappe.db.escape(txt)+"%",
+ 'txt': '%' + txt + '%',
'_txt': txt.replace("%", ""),
'start': start or 0,
'page_len': page_len or 20
diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py
index 2da0f0381b..b2057ca40f 100644
--- a/erpnext/controllers/status_updater.py
+++ b/erpnext/controllers/status_updater.py
@@ -40,7 +40,6 @@ status_map = {
["To Bill", "eval:self.per_delivered == 100 and self.per_billed < 100 and self.docstatus == 1"],
["To Deliver", "eval:self.per_delivered < 100 and self.per_billed == 100 and self.docstatus == 1"],
["Completed", "eval:self.per_delivered == 100 and self.per_billed == 100 and self.docstatus == 1"],
- ["Completed", "eval:self.order_type == 'Maintenance' and self.per_billed == 100 and self.docstatus == 1"],
["Cancelled", "eval:self.docstatus==2"],
["Closed", "eval:self.status=='Closed'"],
["On Hold", "eval:self.status=='On Hold'"],
@@ -90,7 +89,8 @@ status_map = {
["Transferred", "eval:self.status != 'Stopped' and self.per_ordered == 100 and self.docstatus == 1 and self.material_request_type == 'Material Transfer'"],
["Issued", "eval:self.status != 'Stopped' and self.per_ordered == 100 and self.docstatus == 1 and self.material_request_type == 'Material Issue'"],
["Received", "eval:self.status != 'Stopped' and self.per_received == 100 and self.docstatus == 1 and self.material_request_type == 'Purchase'"],
- ["Partially Received", "eval:self.status != 'Stopped' and self.per_received > 0 and self.per_received < 100 and self.docstatus == 1 and self.material_request_type == 'Purchase'"]
+ ["Partially Received", "eval:self.status != 'Stopped' and self.per_received > 0 and self.per_received < 100 and self.docstatus == 1 and self.material_request_type == 'Purchase'"],
+ ["Manufactured", "eval:self.status != 'Stopped' and self.per_ordered == 100 and self.docstatus == 1 and self.material_request_type == 'Manufacture'"]
],
"Bank Transaction": [
["Unreconciled", "eval:self.docstatus == 1 and self.unallocated_amount>0"],
diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py
index 8e1510a836..8d24e7a316 100644
--- a/erpnext/controllers/taxes_and_totals.py
+++ b/erpnext/controllers/taxes_and_totals.py
@@ -323,7 +323,7 @@ class calculate_taxes_and_totals(object):
self._set_in_company_currency(self.doc, ["total_taxes_and_charges", "rounding_adjustment"])
if self.doc.doctype in ["Quotation", "Sales Order", "Delivery Note", "Sales Invoice"]:
- self.doc.base_grand_total = flt(self.doc.grand_total * self.doc.conversion_rate) \
+ self.doc.base_grand_total = flt(self.doc.grand_total * self.doc.conversion_rate, self.doc.precision("base_grand_total")) \
if self.doc.total_taxes_and_charges else self.doc.base_net_total
else:
self.doc.taxes_and_charges_added = self.doc.taxes_and_charges_deducted = 0.0
diff --git a/erpnext/crm/doctype/campaign_email_schedule/__init__.py b/erpnext/crm/doctype/campaign_email_schedule/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/erpnext/crm/doctype/campaign_email_schedule/campaign_email_schedule.json b/erpnext/crm/doctype/campaign_email_schedule/campaign_email_schedule.json
new file mode 100644
index 0000000000..1481a32d5b
--- /dev/null
+++ b/erpnext/crm/doctype/campaign_email_schedule/campaign_email_schedule.json
@@ -0,0 +1,38 @@
+{
+ "creation": "2019-06-30 15:56:20.306901",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "email_template",
+ "send_after_days"
+ ],
+ "fields": [
+ {
+ "fieldname": "send_after_days",
+ "fieldtype": "Int",
+ "in_list_view": 1,
+ "label": "Send After (days)",
+ "reqd": 1
+ },
+ {
+ "fieldname": "email_template",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Email Template",
+ "options": "Email Template",
+ "reqd": 1
+ }
+ ],
+ "istable": 1,
+ "modified": "2019-07-12 11:46:43.184123",
+ "modified_by": "Administrator",
+ "module": "CRM",
+ "name": "Campaign Email Schedule",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/crm/doctype/campaign_email_schedule/campaign_email_schedule.py b/erpnext/crm/doctype/campaign_email_schedule/campaign_email_schedule.py
new file mode 100644
index 0000000000..8445b8a397
--- /dev/null
+++ b/erpnext/crm/doctype/campaign_email_schedule/campaign_email_schedule.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+# import frappe
+from frappe.model.document import Document
+
+class CampaignEmailSchedule(Document):
+ pass
diff --git a/erpnext/crm/doctype/email_campaign/__init__.py b/erpnext/crm/doctype/email_campaign/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/erpnext/crm/doctype/email_campaign/email_campaign.js b/erpnext/crm/doctype/email_campaign/email_campaign.js
new file mode 100644
index 0000000000..b0e9353609
--- /dev/null
+++ b/erpnext/crm/doctype/email_campaign/email_campaign.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Email Campaign', {
+ email_campaign_for: function(frm) {
+ frm.set_value('recipient', '');
+ }
+});
diff --git a/erpnext/crm/doctype/email_campaign/email_campaign.json b/erpnext/crm/doctype/email_campaign/email_campaign.json
new file mode 100644
index 0000000000..3259136275
--- /dev/null
+++ b/erpnext/crm/doctype/email_campaign/email_campaign.json
@@ -0,0 +1,95 @@
+{
+ "autoname": "format:MAIL-CAMP-{YYYY}-{#####}",
+ "creation": "2019-06-30 16:05:30.015615",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "campaign_name",
+ "email_campaign_for",
+ "recipient",
+ "sender",
+ "column_break_4",
+ "start_date",
+ "end_date",
+ "status"
+ ],
+ "fields": [
+ {
+ "fieldname": "campaign_name",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Campaign",
+ "options": "Campaign",
+ "reqd": 1
+ },
+ {
+ "fieldname": "status",
+ "fieldtype": "Select",
+ "label": "Status",
+ "options": "\nScheduled\nIn Progress\nCompleted\nUnsubscribed",
+ "read_only": 1
+ },
+ {
+ "fieldname": "column_break_4",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "start_date",
+ "fieldtype": "Date",
+ "label": "Start Date",
+ "reqd": 1
+ },
+ {
+ "fieldname": "end_date",
+ "fieldtype": "Date",
+ "label": "End Date",
+ "read_only": 1
+ },
+ {
+ "default": "Lead",
+ "fieldname": "email_campaign_for",
+ "fieldtype": "Select",
+ "in_list_view": 1,
+ "label": "Email Campaign For ",
+ "options": "\nLead\nContact"
+ },
+ {
+ "fieldname": "recipient",
+ "fieldtype": "Dynamic Link",
+ "label": "Recipient",
+ "options": "email_campaign_for",
+ "reqd": 1
+ },
+ {
+ "default": "__user",
+ "fieldname": "sender",
+ "fieldtype": "Link",
+ "label": "Sender",
+ "options": "User"
+ }
+ ],
+ "modified": "2019-07-12 13:47:37.261213",
+ "modified_by": "Administrator",
+ "module": "CRM",
+ "name": "Email Campaign",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "System Manager",
+ "share": 1,
+ "write": 1
+ }
+ ],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/crm/doctype/email_campaign/email_campaign.py b/erpnext/crm/doctype/email_campaign/email_campaign.py
new file mode 100644
index 0000000000..98e4927beb
--- /dev/null
+++ b/erpnext/crm/doctype/email_campaign/email_campaign.py
@@ -0,0 +1,102 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe import _
+from frappe.utils import getdate, add_days, today, nowdate, cstr
+from frappe.model.document import Document
+from frappe.core.doctype.communication.email import make
+
+class EmailCampaign(Document):
+ def validate(self):
+ self.set_date()
+ #checking if email is set for lead. Not checking for contact as email is a mandatory field for contact.
+ if self.email_campaign_for == "Lead":
+ self.validate_lead()
+ self.validate_email_campaign_already_exists()
+ self.update_status()
+
+ def set_date(self):
+ if getdate(self.start_date) < getdate(today()):
+ frappe.throw(_("Start Date cannot be before the current date"))
+ #set the end date as start date + max(send after days) in campaign schedule
+ send_after_days = []
+ campaign = frappe.get_doc("Campaign", self.campaign_name)
+ for entry in campaign.get("campaign_schedules"):
+ send_after_days.append(entry.send_after_days)
+ try:
+ end_date = add_days(getdate(self.start_date), max(send_after_days))
+ except ValueError:
+ frappe.throw(_("Please set up the Campaign Schedule in the Campaign {0}").format(self.campaign_name))
+
+ def validate_lead(self):
+ lead_email_id = frappe.db.get_value("Lead", self.recipient, 'email_id')
+ if not lead_email_id:
+ lead_name = frappe.db.get_value("Lead", self.recipient, 'lead_name')
+ frappe.throw(_("Please set an email id for the Lead {0}").format(lead_name))
+
+ def validate_email_campaign_already_exists(self):
+ email_campaign_exists = frappe.db.exists("Email Campaign", {
+ "campaign_name": self.campaign_name,
+ "recipient": self.recipient,
+ "status": ("in", ["In Progress", "Scheduled"])
+ })
+ if email_campaign_exists:
+ frappe.throw(_("The Campaign '{0}' already exists for the {1} '{2}'").format(self.campaign_name, self.email_campaign_for, self.recipient))
+
+ def update_status(self):
+ start_date = getdate(self.start_date)
+ end_date = getdate(self.end_date)
+ today_date = getdate(today())
+ if start_date > today_date:
+ self.status = "Scheduled"
+ elif end_date >= today_date:
+ self.status = "In Progress"
+ elif end_date < today_date:
+ self.status = "Completed"
+
+#called through hooks to send campaign mails to leads
+def send_email_to_leads_or_contacts():
+ email_campaigns = frappe.get_all("Email Campaign", filters = { 'status': ('not in', ['Unsubscribed', 'Completed', 'Scheduled']) })
+ for camp in email_campaigns:
+ email_campaign = frappe.get_doc("Email Campaign", camp.name)
+ campaign = frappe.get_cached_doc("Campaign", email_campaign.campaign_name)
+ for entry in campaign.get("campaign_schedules"):
+ scheduled_date = add_days(email_campaign.get('start_date'), entry.get('send_after_days'))
+ if scheduled_date == getdate(today()):
+ send_mail(entry, email_campaign)
+
+def send_mail(entry, email_campaign):
+ recipient = frappe.db.get_value(email_campaign.email_campaign_for, email_campaign.get("recipient"), 'email_id')
+
+ email_template = frappe.get_doc("Email Template", entry.get("email_template"))
+ sender = frappe.db.get_value("User", email_campaign.get("sender"), 'email')
+
+ # send mail and link communication to document
+ comm = make(
+ doctype = "Email Campaign",
+ name = email_campaign.name,
+ subject = email_template.get("subject"),
+ content = email_template.get("response"),
+ sender = sender,
+ recipients = recipient,
+ communication_medium = "Email",
+ sent_or_received = "Sent",
+ send_email = True,
+ email_template = email_template.name
+ )
+ return comm
+
+#called from hooks on doc_event Email Unsubscribe
+def unsubscribe_recipient(unsubscribe, method):
+ if unsubscribe.reference_doctype == 'Email Campaign':
+ frappe.db.set_value("Email Campaign", unsubscribe.reference_name, "status", "Unsubscribed")
+
+#called through hooks to update email campaign status daily
+def set_email_campaign_status():
+ email_campaigns = frappe.get_all("Email Campaign", filters = { 'status': ('!=', 'Unsubscribed')})
+ for entry in email_campaigns:
+ email_campaign = frappe.get_doc("Email Campaign", entry.name)
+ email_campaign.update_status()
diff --git a/erpnext/crm/doctype/email_campaign/email_campaign_list.js b/erpnext/crm/doctype/email_campaign/email_campaign_list.js
new file mode 100644
index 0000000000..adc399da0f
--- /dev/null
+++ b/erpnext/crm/doctype/email_campaign/email_campaign_list.js
@@ -0,0 +1,11 @@
+frappe.listview_settings['Email Campaign'] = {
+ get_indicator: function(doc) {
+ var colors = {
+ "Unsubscribed": "red",
+ "Scheduled": "blue",
+ "In Progress": "orange",
+ "Completed": "green"
+ };
+ return [__(doc.status), colors[doc.status], "status,=," + doc.status];
+ }
+};
diff --git a/erpnext/crm/doctype/email_campaign/test_email_campaign.py b/erpnext/crm/doctype/email_campaign/test_email_campaign.py
new file mode 100644
index 0000000000..f5eab48333
--- /dev/null
+++ b/erpnext/crm/doctype/email_campaign/test_email_campaign.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+# import frappe
+import unittest
+
+class TestEmailCampaign(unittest.TestCase):
+ pass
diff --git a/erpnext/crm/doctype/lead/lead.js b/erpnext/crm/doctype/lead/lead.js
index cfa7290110..122e2b4eee 100644
--- a/erpnext/crm/doctype/lead/lead.js
+++ b/erpnext/crm/doctype/lead/lead.js
@@ -7,14 +7,8 @@ cur_frm.email_field = "email_id";
erpnext.LeadController = frappe.ui.form.Controller.extend({
setup: function () {
this.frm.make_methods = {
- 'Quotation': () => erpnext.utils.create_new_doc('Quotation', {
- 'quotation_to': this.frm.doc.doctype,
- 'party_name': this.frm.doc.name
- }),
- 'Opportunity': () => erpnext.utils.create_new_doc('Opportunity', {
- 'opportunity_from': this.frm.doc.doctype,
- 'party_name': this.frm.doc.name
- })
+ 'Quotation': this.make_quotation,
+ 'Opportunity': this.create_opportunity
}
this.frm.fields_dict.customer.get_query = function (doc, cdt, cdn) {
diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py
index 12b646dad7..01eee5b61f 100644
--- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py
+++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py
@@ -296,7 +296,9 @@ class TallyMigration(Document):
else:
function = voucher_to_journal_entry
try:
- vouchers.append(function(voucher))
+ processed_voucher = function(voucher)
+ if processed_voucher:
+ vouchers.append(processed_voucher)
except:
self.log(voucher)
return vouchers
@@ -342,6 +344,10 @@ class TallyMigration(Document):
account_field = "credit_to"
account_name = encode_company_abbr(self.tally_creditors_account, self.erpnext_company)
price_list_field = "buying_price_list"
+ else:
+ # Do not handle vouchers other than "Purchase", "Debit Note", "Sales" and "Credit Note"
+ # Do not handle Custom Vouchers either
+ return
invoice = {
"doctype": doctype,
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index d814700a45..47d1a68efc 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -233,6 +233,9 @@ doc_events = {
},
"Contact":{
"on_trash": "erpnext.support.doctype.issue.issue.update_issue"
+ },
+ "Email Unsubscribe": {
+ "after_insert": "erpnext.crm.doctype.email_campaign.email_campaign.unsubscribe_recipient"
}
}
@@ -272,6 +275,8 @@ scheduler_events = {
"erpnext.projects.doctype.project.project.send_project_status_email_to_users",
"erpnext.quality_management.doctype.quality_review.quality_review.review",
"erpnext.support.doctype.service_level_agreement.service_level_agreement.check_agreement_status",
+ "erpnext.crm.doctype.email_campaign.email_campaign.send_email_to_leads_or_contacts",
+ "erpnext.crm.doctype.email_campaign.email_campaign.set_email_campaign_status"
],
"daily_long": [
"erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool.update_latest_price_in_all_boms"
diff --git a/erpnext/hr/doctype/designation/test_designation.py b/erpnext/hr/doctype/designation/test_designation.py
index 3b00bd327a..3b300941a6 100644
--- a/erpnext/hr/doctype/designation/test_designation.py
+++ b/erpnext/hr/doctype/designation/test_designation.py
@@ -4,4 +4,17 @@ from __future__ import unicode_literals
import frappe
-test_records = frappe.get_test_records('Designation')
\ No newline at end of file
+# test_records = frappe.get_test_records('Designation')
+
+def create_designation(**args):
+ args = frappe._dict(args)
+ if frappe.db.exists("Designation", args.designation_name or "_Test designation"):
+ return frappe.get_doc("Designation", args.designation_name or "_Test designation")
+
+ designation = frappe.get_doc({
+ "doctype": "Designation",
+ "designation_name": args.designation_name or "_Test designation",
+ "description": args.description or "_Test description"
+ })
+ designation.save()
+ return designation
\ No newline at end of file
diff --git a/erpnext/hr/doctype/driver/driver.json b/erpnext/hr/doctype/driver/driver.json
index 32822b2a34..0a670c0601 100644
--- a/erpnext/hr/doctype/driver/driver.json
+++ b/erpnext/hr/doctype/driver/driver.json
@@ -1,516 +1,124 @@
{
- "allow_copy": 0,
- "allow_guest_to_view": 0,
"allow_import": 1,
"allow_rename": 1,
"autoname": "naming_series:",
- "beta": 0,
"creation": "2017-10-17 08:21:50.489773",
- "custom": 0,
- "docstatus": 0,
"doctype": "DocType",
"document_type": "Setup",
"editable_grid": 1,
"engine": "InnoDB",
+ "field_order": [
+ "naming_series",
+ "full_name",
+ "status",
+ "transporter",
+ "column_break_2",
+ "employee",
+ "cell_number",
+ "address",
+ "license_details",
+ "license_number",
+ "column_break_8",
+ "issuing_date",
+ "column_break_10",
+ "expiry_date",
+ "driving_license_categories",
+ "driving_license_category"
+ ],
"fields": [
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "default": "",
"fieldname": "naming_series",
"fieldtype": "Select",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
"label": "Series",
- "length": 0,
- "no_copy": 0,
- "options": "HR-DRI-.YYYY.-",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "options": "HR-DRI-.YYYY.-"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "full_name",
"fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
"in_list_view": 1,
- "in_standard_filter": 0,
"label": "Full Name",
- "length": 0,
- "no_copy": 0,
- "options": "",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "reqd": 1
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "status",
"fieldtype": "Select",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
"in_list_view": 1,
- "in_standard_filter": 0,
"label": "Status",
- "length": 0,
- "no_copy": 0,
"options": "Active\nSuspended\nLeft",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "reqd": 1
},
{
- "allow_bulk_edit": 0,
"allow_in_quick_entry": 1,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"description": "Applicable for external driver",
"fieldname": "transporter",
"fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
"label": "Transporter",
- "length": 0,
- "no_copy": 0,
- "options": "Supplier",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "options": "Supplier"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "column_break_2",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "fieldtype": "Column Break"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "employee",
"fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
"in_list_view": 1,
- "in_standard_filter": 0,
"label": "Employee",
- "length": 0,
- "no_copy": 0,
- "options": "Employee",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "options": "Employee"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "cell_number",
"fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Cellphone Number",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "label": "Cellphone Number"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "license_details",
"fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "License Details",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "label": "License Details"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "license_number",
"fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "License Number",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "label": "License Number"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "column_break_8",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "fieldtype": "Column Break"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "issuing_date",
"fieldtype": "Date",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Issuing Date",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "label": "Issuing Date"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "column_break_10",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "fieldtype": "Column Break"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "expiry_date",
"fieldtype": "Date",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Expiry Date",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "label": "Expiry Date"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "driving_license_categories",
"fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Driving License Categories",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "label": "Driving License Categories"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "driving_license_category",
"fieldtype": "Table",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
"label": "Driving License Category",
- "length": 0,
- "no_copy": 0,
- "options": "Driving License Category",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "options": "Driving License Category"
+ },
+ {
+ "fieldname": "address",
+ "fieldtype": "Link",
+ "label": "Address",
+ "options": "Address"
}
],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
"icon": "fa fa-user",
- "idx": 0,
- "image_view": 0,
- "in_create": 0,
- "is_submittable": 0,
- "issingle": 0,
- "istable": 0,
- "max_attachments": 0,
- "modified": "2018-10-03 19:53:50.924391",
+ "modified": "2019-07-18 16:29:14.151380",
"modified_by": "Administrator",
"module": "HR",
"name": "Driver",
@@ -518,72 +126,44 @@
"owner": "Administrator",
"permissions": [
{
- "amend": 0,
- "cancel": 0,
- "create": 0,
- "delete": 0,
"email": 1,
"export": 1,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Fleet Manager",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 0,
- "write": 0
+ "share": 1
},
{
- "amend": 0,
- "cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "HR User",
- "set_user_permissions": 0,
"share": 1,
- "submit": 0,
"write": 1
},
{
- "amend": 0,
- "cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "HR Manager",
- "set_user_permissions": 0,
"share": 1,
- "submit": 0,
"write": 1
}
],
"quick_entry": 1,
- "read_only": 0,
- "read_only_onload": 0,
"search_fields": "full_name",
"show_name_in_global_search": 1,
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "full_name",
- "track_changes": 1,
- "track_seen": 0,
- "track_views": 0
+ "track_changes": 1
}
\ No newline at end of file
diff --git a/erpnext/hr/doctype/employee/employee.py b/erpnext/hr/doctype/employee/employee.py
index af87f85743..cf418b0e8f 100755
--- a/erpnext/hr/doctype/employee/employee.py
+++ b/erpnext/hr/doctype/employee/employee.py
@@ -12,6 +12,7 @@ from frappe.permissions import add_user_permission, remove_user_permission, \
from frappe.model.document import Document
from erpnext.utilities.transaction_base import delete_events
from frappe.utils.nestedset import NestedSet
+from erpnext.hr.doctype.job_offer.job_offer import get_staffing_plan_detail
class EmployeeUserDisabledError(frappe.ValidationError): pass
class EmployeeLeftValidationError(frappe.ValidationError): pass
diff --git a/erpnext/hr/doctype/hr_settings/hr_settings.json b/erpnext/hr/doctype/hr_settings/hr_settings.json
index 3f5a2ab333..8dd0acf455 100644
--- a/erpnext/hr/doctype/hr_settings/hr_settings.json
+++ b/erpnext/hr/doctype/hr_settings/hr_settings.json
@@ -3,6 +3,7 @@
"doctype": "DocType",
"document_type": "Other",
"editable_grid": 1,
+ "engine": "InnoDB",
"field_order": [
"employee_settings",
"retirement_age",
@@ -22,7 +23,9 @@
"leave_status_notification_template",
"column_break_18",
"leave_approver_mandatory_in_leave_application",
- "show_leaves_of_all_department_members_in_calendar"
+ "show_leaves_of_all_department_members_in_calendar",
+ "hiring_settings",
+ "check_vacancies"
],
"fields": [
{
@@ -44,18 +47,6 @@
"label": "Employee Records to be created by",
"options": "Naming Series\nEmployee Number\nFull Name"
},
- {
- "fieldname": "leave_approval_notification_template",
- "fieldtype": "Link",
- "label": "Leave Approval Notification Template",
- "options": "Email Template"
- },
- {
- "fieldname": "leave_status_notification_template",
- "fieldtype": "Link",
- "label": "Leave Status Notification Template",
- "options": "Email Template"
- },
{
"fieldname": "column_break_4",
"fieldtype": "Column Break"
@@ -67,12 +58,6 @@
"fieldtype": "Check",
"label": "Stop Birthday Reminders"
},
- {
- "default": "1",
- "fieldname": "leave_approver_mandatory_in_leave_application",
- "fieldtype": "Check",
- "label": "Leave Approver Mandatory In Leave Application"
- },
{
"default": "1",
"fieldname": "expense_approver_mandatory_in_expense_claim",
@@ -91,6 +76,15 @@
"fieldtype": "Check",
"label": "Include holidays in Total no. of Working Days"
},
+ {
+ "fieldname": "max_working_hours_against_timesheet",
+ "fieldtype": "Float",
+ "label": "Max working hours against Timesheet"
+ },
+ {
+ "fieldname": "column_break_11",
+ "fieldtype": "Column Break"
+ },
{
"default": "1",
"description": "Emails salary slip to employee based on preferred email selected in Employee",
@@ -115,15 +109,33 @@
"label": "Password Policy"
},
{
- "fieldname": "max_working_hours_against_timesheet",
- "fieldtype": "Float",
- "label": "Max working hours against Timesheet"
- },
- {
+ "collapsible": 1,
"fieldname": "leave_settings",
"fieldtype": "Section Break",
"label": "Leave Settings"
},
+ {
+ "fieldname": "leave_approval_notification_template",
+ "fieldtype": "Link",
+ "label": "Leave Approval Notification Template",
+ "options": "Email Template"
+ },
+ {
+ "fieldname": "leave_status_notification_template",
+ "fieldtype": "Link",
+ "label": "Leave Status Notification Template",
+ "options": "Email Template"
+ },
+ {
+ "fieldname": "column_break_18",
+ "fieldtype": "Column Break"
+ },
+ {
+ "default": "1",
+ "fieldname": "leave_approver_mandatory_in_leave_application",
+ "fieldtype": "Check",
+ "label": "Leave Approver Mandatory In Leave Application"
+ },
{
"default": "0",
"fieldname": "show_leaves_of_all_department_members_in_calendar",
@@ -131,18 +143,22 @@
"label": "Show Leaves Of All Department Members In Calendar"
},
{
- "fieldname": "column_break_11",
- "fieldtype": "Column Break"
+ "collapsible": 1,
+ "fieldname": "hiring_settings",
+ "fieldtype": "Section Break",
+ "label": "Hiring Settings"
},
{
- "fieldname": "column_break_18",
- "fieldtype": "Column Break"
+ "default": "0",
+ "fieldname": "check_vacancies",
+ "fieldtype": "Check",
+ "label": "Check Vacancies On Job Offer Creation"
}
],
"icon": "fa fa-cog",
"idx": 1,
"issingle": 1,
- "modified": "2019-05-31 16:18:50.245872",
+ "modified": "2019-07-01 18:59:55.256878",
"modified_by": "Administrator",
"module": "HR",
"name": "HR Settings",
@@ -158,5 +174,6 @@
"write": 1
}
],
+ "sort_field": "modified",
"sort_order": "ASC"
}
\ No newline at end of file
diff --git a/erpnext/hr/doctype/job_applicant/job_applicant.json b/erpnext/hr/doctype/job_applicant/job_applicant.json
index a78c9b2ddd..e9de393bc4 100644
--- a/erpnext/hr/doctype/job_applicant/job_applicant.json
+++ b/erpnext/hr/doctype/job_applicant/job_applicant.json
@@ -39,7 +39,7 @@
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
- "reqd": 0,
+ "reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
@@ -71,7 +71,7 @@
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
- "reqd": 0,
+ "reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
@@ -96,7 +96,7 @@
"label": "Status",
"length": 0,
"no_copy": 0,
- "options": "Open\nReplied\nRejected\nHold",
+ "options": "Open\nReplied\nRejected\nHold\nAccepted",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
@@ -346,7 +346,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2018-08-21 16:15:43.552049",
+ "modified": "2019-06-21 16:15:43.552049",
"modified_by": "Administrator",
"module": "HR",
"name": "Job Applicant",
diff --git a/erpnext/hr/doctype/job_applicant/test_job_applicant.py b/erpnext/hr/doctype/job_applicant/test_job_applicant.py
index 3ca862be85..6d275c82d9 100644
--- a/erpnext/hr/doctype/job_applicant/test_job_applicant.py
+++ b/erpnext/hr/doctype/job_applicant/test_job_applicant.py
@@ -10,3 +10,14 @@ import unittest
class TestJobApplicant(unittest.TestCase):
pass
+
+def create_job_applicant(**args):
+ args = frappe._dict(args)
+ job_applicant = frappe.get_doc({
+ "doctype": "Job Applicant",
+ "applicant_name": args.applicant_name or "_Test Applicant",
+ "email_id": args.email_id or "test_applicant@example.com",
+ "status": args.status or "Open"
+ })
+ job_applicant.save()
+ return job_applicant
\ No newline at end of file
diff --git a/erpnext/hr/doctype/job_offer/job_offer.py b/erpnext/hr/doctype/job_offer/job_offer.py
index 7e3014b38c..ef8004eedb 100644
--- a/erpnext/hr/doctype/job_offer/job_offer.py
+++ b/erpnext/hr/doctype/job_offer/job_offer.py
@@ -5,12 +5,56 @@ from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
from frappe.model.mapper import get_mapped_doc
+from frappe import _
+from frappe.utils.data import get_link_to_form
class JobOffer(Document):
def onload(self):
employee = frappe.db.get_value("Employee", {"job_applicant": self.job_applicant}, "name") or ""
self.set_onload("employee", employee)
+ def validate(self):
+ self.validate_vacancies()
+
+ def validate_vacancies(self):
+ staffing_plan = get_staffing_plan_detail(self.designation, self.company, self.offer_date)
+ check_vacancies = frappe.get_single("HR Settings").check_vacancies
+ if staffing_plan and check_vacancies:
+ vacancies = frappe.db.get_value("Staffing Plan Detail", filters={"name": staffing_plan.name}, fieldname=['vacancies'])
+ job_offers = len(self.get_job_offer(staffing_plan.from_date, staffing_plan.to_date))
+ if vacancies - job_offers <= 0:
+ frappe.throw(_("There are no vacancies under staffing plan {0}").format(get_link_to_form("Staffing Plan", staffing_plan.parent)))
+
+ def on_change(self):
+ update_job_applicant(self.status, self.job_applicant)
+
+ def get_job_offer(self, from_date, to_date):
+ ''' Returns job offer created during a time period '''
+ return frappe.get_all("Job Offer", filters={
+ "offer_date": ['between', (from_date, to_date)],
+ "designation": self.designation,
+ "company": self.company
+ }, fields=['name'])
+
+def update_job_applicant(status, job_applicant):
+ if status in ("Accepted", "Rejected"):
+ frappe.set_value("Job Applicant", job_applicant, "status", status)
+
+def get_staffing_plan_detail(designation, company, offer_date):
+ detail = frappe.db.sql("""
+ SELECT spd.name as name,
+ sp.from_date as from_date,
+ sp.to_date as to_date,
+ sp.name as parent
+ FROM `tabStaffing Plan Detail` spd, `tabStaffing Plan` sp
+ WHERE
+ sp.docstatus=1
+ AND spd.designation=%s
+ AND sp.company=%s
+ AND %s between sp.from_date and sp.to_date
+ """, (designation, company, offer_date), as_dict=1)
+ return detail[0] if detail else None
+
@frappe.whitelist()
def make_employee(source_name, target_doc=None):
def set_missing_values(source, target):
@@ -23,4 +67,3 @@ def make_employee(source_name, target_doc=None):
}}
}, target_doc, set_missing_values)
return doc
-
diff --git a/erpnext/hr/doctype/job_offer/test_job_offer.py b/erpnext/hr/doctype/job_offer/test_job_offer.py
index c3aeb2b368..8886596450 100644
--- a/erpnext/hr/doctype/job_offer/test_job_offer.py
+++ b/erpnext/hr/doctype/job_offer/test_job_offer.py
@@ -4,8 +4,78 @@ from __future__ import unicode_literals
import frappe
import unittest
+from frappe.utils import nowdate, add_days
+from erpnext.hr.doctype.job_applicant.test_job_applicant import create_job_applicant
+from erpnext.hr.doctype.designation.test_designation import create_designation
+from erpnext.hr.doctype.staffing_plan.test_staffing_plan import make_company
# test_records = frappe.get_test_records('Job Offer')
class TestJobOffer(unittest.TestCase):
- pass
+ def test_job_offer_creation_against_vacancies(self):
+ create_staffing_plan(staffing_details=[{
+ "designation": "Designer",
+ "vacancies": 0,
+ "estimated_cost_per_position": 5000
+ }])
+ frappe.db.set_value("HR Settings", None, "check_vacancies", 1)
+ job_applicant = create_job_applicant(email_id="test_job_offer@example.com")
+ job_offer = create_job_offer(job_applicant=job_applicant.name, designation="Researcher")
+ self.assertRaises(frappe.ValidationError, job_offer.submit)
+
+ # test creation of job offer when vacancies are not present
+ frappe.db.set_value("HR Settings", None, "check_vacancies", 0)
+ job_offer.submit()
+ self.assertTrue(frappe.db.exists("Job Offer", job_offer.name))
+
+ def test_job_applicant_update(self):
+ create_staffing_plan()
+ job_applicant = create_job_applicant(email_id="test_job_applicants@example.com")
+ job_offer = create_job_offer(job_applicant=job_applicant.name)
+ job_offer.submit()
+ job_applicant.reload()
+ self.assertEquals(job_applicant.status, "Accepted")
+
+ # status update after rejection
+ job_offer.status = "Rejected"
+ job_offer.submit()
+ job_applicant.reload()
+ self.assertEquals(job_applicant.status, "Rejected")
+
+def create_job_offer(**args):
+ args = frappe._dict(args)
+ if not args.job_applicant:
+ job_applicant = create_job_applicant()
+
+ if not frappe.db.exists("Designation", args.designation):
+ designation = create_designation(designation_name=args.designation)
+
+ job_offer = frappe.get_doc({
+ "doctype": "Job Offer",
+ "job_applicant": args.job_applicant or job_applicant.name,
+ "offer_date": args.offer_date or nowdate(),
+ "designation": args.designation or "Researcher",
+ "status": args.status or "Accepted"
+ })
+ return job_offer
+
+def create_staffing_plan(**args):
+ args = frappe._dict(args)
+ make_company()
+ frappe.db.set_value("Company", "_Test Company", "is_group", 1)
+ if frappe.db.exists("Staffing Plan", args.name or "Test"):
+ return
+ staffing_plan = frappe.get_doc({
+ "doctype": "Staffing Plan",
+ "name": args.name or "Test",
+ "from_date": args.from_date or nowdate(),
+ "to_date": args.to_date or add_days(nowdate(), 10),
+ "staffing_details": args.staffing_details or [{
+ "designation": "Researcher",
+ "vacancies": 1,
+ "estimated_cost_per_position": 50000
+ }]
+ })
+ staffing_plan.insert()
+ staffing_plan.submit()
+ return staffing_plan
\ No newline at end of file
diff --git a/erpnext/hr/doctype/loan_application/loan_application.py b/erpnext/hr/doctype/loan_application/loan_application.py
index 67be9f2a1c..28d9c43f8e 100644
--- a/erpnext/hr/doctype/loan_application/loan_application.py
+++ b/erpnext/hr/doctype/loan_application/loan_application.py
@@ -30,11 +30,11 @@ class LoanApplication(Document):
monthly_interest_rate = flt(self.rate_of_interest) / (12 *100)
if monthly_interest_rate:
min_repayment_amount = self.loan_amount*monthly_interest_rate
- if self.repayment_amount - min_repayment_amount < 0:
+ if self.repayment_amount - min_repayment_amount <= 0:
frappe.throw(_("Repayment Amount must be greater than " \
+ str(flt(min_repayment_amount, 2))))
- self.repayment_periods = math.ceil(math.log(self.repayment_amount) -
- math.log(self.repayment_amount - min_repayment_amount) /(math.log(1 + monthly_interest_rate)))
+ self.repayment_periods = math.ceil((math.log(self.repayment_amount) -
+ math.log(self.repayment_amount - min_repayment_amount)) /(math.log(1 + monthly_interest_rate)))
else:
self.repayment_periods = self.loan_amount / self.repayment_amount
diff --git a/erpnext/hr/doctype/payroll_entry/payroll_entry.py b/erpnext/hr/doctype/payroll_entry/payroll_entry.py
index 4ce2513bea..d8dd5c644b 100644
--- a/erpnext/hr/doctype/payroll_entry/payroll_entry.py
+++ b/erpnext/hr/doctype/payroll_entry/payroll_entry.py
@@ -119,6 +119,8 @@ class PayrollEntry(Document):
frappe.enqueue(create_salary_slips_for_employees, timeout=600, employees=emp_list, args=args)
else:
create_salary_slips_for_employees(emp_list, args, publish_progress=False)
+ # since this method is called via frm.call this doc needs to be updated manually
+ self.reload()
def get_sal_slip_list(self, ss_status, as_dict=False):
"""
diff --git a/erpnext/hr/doctype/staffing_plan/staffing_plan.js b/erpnext/hr/doctype/staffing_plan/staffing_plan.js
index 4fbc6b3089..04af2323c7 100644
--- a/erpnext/hr/doctype/staffing_plan/staffing_plan.js
+++ b/erpnext/hr/doctype/staffing_plan/staffing_plan.js
@@ -5,7 +5,7 @@ frappe.ui.form.on('Staffing Plan', {
setup: function(frm) {
frm.set_query("designation", "staffing_details", function() {
let designations = [];
- $.each(frm.doc.staffing_details, function(index, staff_detail) {
+ (frm.doc.staffing_details || []).forEach(function(staff_detail) {
if(staff_detail.designation){
designations.push(staff_detail.designation)
}
@@ -25,69 +25,63 @@ frappe.ui.form.on('Staffing Plan', {
}
};
});
- }
+ },
});
frappe.ui.form.on('Staffing Plan Detail', {
designation: function(frm, cdt, cdn) {
- let child = locals[cdt][cdn]
- if(frm.doc.company && child.designation){
- frappe.call({
- "method": "erpnext.hr.doctype.staffing_plan.staffing_plan.get_designation_counts",
- args: {
- designation: child.designation,
- company: frm.doc.company
- },
- callback: function (data) {
- if(data.message){
- frappe.model.set_value(cdt, cdn, 'current_count', data.message.employee_count);
- frappe.model.set_value(cdt, cdn, 'current_openings', data.message.job_openings);
- if (child.number_of_positions < (data.message.employee_count + data.message.job_openings)){
- frappe.model.set_value(cdt, cdn, 'number_of_positions', data.message.employee_count + data.message.job_openings);
- }
- }
- else{ // No employees for this designation
- frappe.model.set_value(cdt, cdn, 'current_count', 0);
- frappe.model.set_value(cdt, cdn, 'current_openings', 0);
- }
- }
- });
+ let child = locals[cdt][cdn];
+ if(frm.doc.company && child.designation) {
+ set_number_of_positions(frm, cdt, cdn);
}
},
- number_of_positions: function(frm, cdt, cdn) {
- set_vacancies(frm, cdt, cdn);
+ vacancies: function(frm, cdt, cdn) {
+ let child = locals[cdt][cdn];
+ if(child.vacancies < child.current_openings) {
+ frappe.throw(__("Vacancies cannot be lower than the current openings"));
+ }
+ set_number_of_positions(frm, cdt, cdn);
},
current_count: function(frm, cdt, cdn) {
- set_vacancies(frm, cdt, cdn);
+ set_number_of_positions(frm, cdt, cdn);
},
estimated_cost_per_position: function(frm, cdt, cdn) {
- let child = locals[cdt][cdn];
set_total_estimated_cost(frm, cdt, cdn);
}
-
});
-var set_vacancies = function(frm, cdt, cdn) {
- let child = locals[cdt][cdn]
- if (child.number_of_positions < (child.current_count + child.current_openings)){
- frappe.throw(__("Number of positions cannot be less then current count of employees"))
- }
-
- if(child.number_of_positions > 0) {
- frappe.model.set_value(cdt, cdn, 'vacancies', child.number_of_positions - (child.current_count + child.current_openings));
- }
- else{
- frappe.model.set_value(cdt, cdn, 'vacancies', 0);
- }
-
+var set_number_of_positions = function(frm, cdt, cdn) {
+ let child = locals[cdt][cdn];
+ if (!child.designation) frappe.throw(__("Please enter the designation"));
+ frappe.call({
+ "method": "erpnext.hr.doctype.staffing_plan.staffing_plan.get_designation_counts",
+ args: {
+ designation: child.designation,
+ company: frm.doc.company
+ },
+ callback: function (data) {
+ if(data.message){
+ frappe.model.set_value(cdt, cdn, 'current_count', data.message.employee_count);
+ frappe.model.set_value(cdt, cdn, 'current_openings', data.message.job_openings);
+ let total_positions = cint(data.message.employee_count) + cint(child.vacancies);
+ if (cint(child.number_of_positions) < total_positions){
+ frappe.model.set_value(cdt, cdn, 'number_of_positions', total_positions);
+ }
+ }
+ else{ // No employees for this designation
+ frappe.model.set_value(cdt, cdn, 'current_count', 0);
+ frappe.model.set_value(cdt, cdn, 'current_openings', 0);
+ }
+ }
+ });
+ refresh_field("staffing_details");
set_total_estimated_cost(frm, cdt, cdn);
}
// Note: Estimated Cost is calculated on number of Vacancies
-// Validate for > 0 ?
var set_total_estimated_cost = function(frm, cdt, cdn) {
let child = locals[cdt][cdn]
if(child.vacancies > 0 && child.estimated_cost_per_position) {
@@ -102,11 +96,11 @@ var set_total_estimated_cost = function(frm, cdt, cdn) {
var set_total_estimated_budget = function(frm) {
let estimated_budget = 0.0
if(frm.doc.staffing_details) {
- $.each(frm.doc.staffing_details, function(index, staff_detail) {
+ (frm.doc.staffing_details || []).forEach(function(staff_detail) {
if(staff_detail.total_estimated_cost){
estimated_budget += staff_detail.total_estimated_cost
}
})
frm.set_value('total_estimated_budget', estimated_budget);
}
-}
+}
\ No newline at end of file
diff --git a/erpnext/hr/doctype/staffing_plan/staffing_plan.py b/erpnext/hr/doctype/staffing_plan/staffing_plan.py
index 83e53135ef..e6afbcc220 100644
--- a/erpnext/hr/doctype/staffing_plan/staffing_plan.py
+++ b/erpnext/hr/doctype/staffing_plan/staffing_plan.py
@@ -13,41 +13,39 @@ class ParentCompanyError(frappe.ValidationError): pass
class StaffingPlan(Document):
def validate(self):
+ self.validate_period()
+ self.validate_details()
+ self.set_total_estimated_budget()
+
+ def validate_period(self):
# Validate Dates
if self.from_date and self.to_date and self.from_date > self.to_date:
frappe.throw(_("From Date cannot be greater than To Date"))
- self.total_estimated_budget = 0
-
+ def validate_details(self):
for detail in self.get("staffing_details"):
- self.set_vacancies(detail)
self.validate_overlap(detail)
self.validate_with_subsidiary_plans(detail)
self.validate_with_parent_plan(detail)
+ def set_total_estimated_budget(self):
+ self.total_estimated_budget = 0
+
+ for detail in self.get("staffing_details"):
#Set readonly fields
+ self.set_number_of_positions(detail)
designation_counts = get_designation_counts(detail.designation, self.company)
detail.current_count = designation_counts['employee_count']
detail.current_openings = designation_counts['job_openings']
- if detail.number_of_positions < (detail.current_count + detail.current_openings):
- frappe.throw(_("Number of positions cannot be less then current count of employees"))
- elif detail.number_of_positions > 0:
- detail.vacancies = detail.number_of_positions - (detail.current_count + detail.current_openings)
+ if detail.number_of_positions > 0:
if detail.vacancies > 0 and detail.estimated_cost_per_position:
- detail.total_estimated_cost = detail.vacancies * detail.estimated_cost_per_position
- else: detail.total_estimated_cost = 0
- else: detail.vacancies = detail.number_of_positions = detail.total_estimated_cost = 0
+ detail.total_estimated_cost = cint(detail.vacancies) * flt(detail.estimated_cost_per_position)
+
self.total_estimated_budget += detail.total_estimated_cost
- def set_vacancies(self, row):
- if not row.vacancies:
- current_openings = 0
- for field in ['current_count', 'current_openings']:
- if row.get(field):
- current_openings += row.get(field)
-
- row.vacancies = row.number_of_positions - current_openings
+ def set_number_of_positions(self, detail):
+ detail.number_of_positions = cint(detail.vacancies) + cint(detail.current_count)
def validate_overlap(self, staffing_plan_detail):
# Validate if any submitted Staffing Plan exist for any Designations in this plan
@@ -132,19 +130,24 @@ def get_designation_counts(designation, company):
if not designation:
return False
- employee_counts_dict = {}
- lft, rgt = frappe.get_cached_value('Company', company, ["lft", "rgt"])
- employee_counts_dict["employee_count"] = frappe.db.sql("""select count(*) from `tabEmployee`
- where designation = %s and status='Active'
- and company in (select name from tabCompany where lft>=%s and rgt<=%s)
- """, (designation, lft, rgt))[0][0]
+ employee_counts = {}
+ company_set = get_company_set(company)
- employee_counts_dict['job_openings'] = frappe.db.sql("""select count(*) from `tabJob Opening` \
- where designation=%s and status='Open'
- and company in (select name from tabCompany where lft>=%s and rgt<=%s)
- """, (designation, lft, rgt))[0][0]
+ employee_counts["employee_count"] = frappe.db.get_value("Employee",
+ filters={
+ 'designation': designation,
+ 'status': 'Active',
+ 'company': ('in', company_set)
+ }, fieldname=['count(name)'])
- return employee_counts_dict
+ employee_counts['job_openings'] = frappe.db.get_value("Job Opening",
+ filters={
+ 'designation': designation,
+ 'status': 'Open',
+ 'company': ('in', company_set)
+ }, fieldname=['count(name)'])
+
+ return employee_counts
@frappe.whitelist()
def get_active_staffing_plan_details(company, designation, from_date=getdate(nowdate()), to_date=getdate(nowdate())):
@@ -165,3 +168,13 @@ def get_active_staffing_plan_details(company, designation, from_date=getdate(now
# Only a single staffing plan can be active for a designation on given date
return staffing_plan if staffing_plan else None
+
+def get_company_set(company):
+ return frappe.db.sql_list("""
+ SELECT
+ name
+ FROM `tabCompany`
+ WHERE
+ parent_company=%(company)s
+ OR name=%(company)s
+ """, (dict(company=company)))
\ No newline at end of file
diff --git a/erpnext/hr/doctype/staffing_plan/test_staffing_plan.py b/erpnext/hr/doctype/staffing_plan/test_staffing_plan.py
index 22dba99af0..4a0ce1800a 100644
--- a/erpnext/hr/doctype/staffing_plan/test_staffing_plan.py
+++ b/erpnext/hr/doctype/staffing_plan/test_staffing_plan.py
@@ -24,7 +24,7 @@ class TestStaffingPlan(unittest.TestCase):
staffing_plan.to_date = add_days(nowdate(), 10)
staffing_plan.append("staffing_details", {
"designation": "Designer",
- "number_of_positions": 6,
+ "vacancies": 6,
"estimated_cost_per_position": 50000
})
staffing_plan.insert()
@@ -42,7 +42,7 @@ class TestStaffingPlan(unittest.TestCase):
staffing_plan.to_date = add_days(nowdate(), 10)
staffing_plan.append("staffing_details", {
"designation": "Designer",
- "number_of_positions": 3,
+ "vacancies": 3,
"estimated_cost_per_position": 45000
})
self.assertRaises(SubsidiaryCompanyError, staffing_plan.insert)
@@ -58,7 +58,7 @@ class TestStaffingPlan(unittest.TestCase):
staffing_plan.to_date = add_days(nowdate(), 10)
staffing_plan.append("staffing_details", {
"designation": "Designer",
- "number_of_positions": 7,
+ "vacancies": 7,
"estimated_cost_per_position": 50000
})
staffing_plan.insert()
@@ -73,7 +73,7 @@ class TestStaffingPlan(unittest.TestCase):
staffing_plan.to_date = add_days(nowdate(), 10)
staffing_plan.append("staffing_details", {
"designation": "Designer",
- "number_of_positions": 7,
+ "vacancies": 7,
"estimated_cost_per_position": 60000
})
staffing_plan.insert()
@@ -93,4 +93,4 @@ def make_company():
company.parent_company = "_Test Company"
company.default_currency = "INR"
company.country = "India"
- company.insert()
+ company.insert()
\ No newline at end of file
diff --git a/erpnext/hr/doctype/staffing_plan_detail/staffing_plan_detail.json b/erpnext/hr/doctype/staffing_plan_detail/staffing_plan_detail.json
index f1d16096c0..77164c4e67 100644
--- a/erpnext/hr/doctype/staffing_plan_detail/staffing_plan_detail.json
+++ b/erpnext/hr/doctype/staffing_plan_detail/staffing_plan_detail.json
@@ -1,297 +1,79 @@
{
- "allow_copy": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "beta": 0,
"creation": "2018-04-13 18:04:20.978931",
- "custom": 0,
- "docstatus": 0,
"doctype": "DocType",
- "document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
+ "field_order": [
+ "designation",
+ "vacancies",
+ "estimated_cost_per_position",
+ "total_estimated_cost",
+ "column_break_5",
+ "current_count",
+ "current_openings",
+ "number_of_positions"
+ ],
"fields": [
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "designation",
"fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
"in_list_view": 1,
- "in_standard_filter": 0,
"label": "Designation",
- "length": 0,
- "no_copy": 0,
"options": "Designation",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "reqd": 1
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "number_of_positions",
"fieldtype": "Int",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
"in_list_view": 1,
- "in_standard_filter": 0,
"label": "Number Of Positions",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "read_only": 1
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "estimated_cost_per_position",
"fieldtype": "Currency",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
"in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Estimated Cost Per Position",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "label": "Estimated Cost Per Position"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "column_break_5",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "fieldtype": "Column Break"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "current_count",
"fieldtype": "Int",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
"label": "Current Count",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "read_only": 1
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "current_openings",
"fieldtype": "Int",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
"label": "Current Openings",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "read_only": 1
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "vacancies",
"fieldtype": "Int",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
"in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Vacancies",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "total_estimated_cost",
- "fieldtype": "Currency",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Total Estimated Cost",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- }
+ "label": "Vacancies"
+ },
+ {
+ "fieldname": "total_estimated_cost",
+ "fieldtype": "Currency",
+ "in_list_view": 1,
+ "label": "Total Estimated Cost",
+ "read_only": 1
+ }
],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
- "idx": 0,
- "image_view": 0,
- "in_create": 0,
- "is_submittable": 0,
- "issingle": 0,
"istable": 1,
- "max_attachments": 0,
- "modified": "2018-06-01 17:03:38.020993",
+ "modified": "2019-06-24 18:40:37.140178",
"modified_by": "Administrator",
"module": "HR",
"name": "Staffing Plan Detail",
- "name_case": "",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
- "read_only": 0,
- "read_only_onload": 0,
- "show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
- "track_changes": 1,
- "track_seen": 0
-}
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/hr/report/loan_repayment/loan_repayment.py b/erpnext/hr/report/loan_repayment/loan_repayment.py
index 9e310de48c..beca776964 100644
--- a/erpnext/hr/report/loan_repayment/loan_repayment.py
+++ b/erpnext/hr/report/loan_repayment/loan_repayment.py
@@ -73,7 +73,7 @@ def create_columns():
def get_record():
data = []
loans = frappe.get_all("Loan",
- filters=[("status", "=", "Fully Disbursed")],
+ filters=[("status", "=", "Disbursed")],
fields=["applicant", "applicant_name", "name", "loan_amount", "rate_of_interest",
"total_payment", "monthly_repayment_amount", "total_amount_paid"]
)
diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py
index 6f63dcf1ec..a7162933bf 100644
--- a/erpnext/manufacturing/doctype/bom/bom.py
+++ b/erpnext/manufacturing/doctype/bom/bom.py
@@ -578,6 +578,8 @@ class BOM(WebsiteGenerator):
for d in self.operations:
if not d.description:
d.description = frappe.db.get_value('Operation', d.operation, 'description')
+ if not d.batch_size > 0:
+ d.batch_size = 1
def get_list_context(context):
context.title = _("Bill of Materials")
diff --git a/erpnext/manufacturing/doctype/bom_operation/bom_operation.json b/erpnext/manufacturing/doctype/bom_operation/bom_operation.json
index 08c4f4fce6..3ca851d783 100644
--- a/erpnext/manufacturing/doctype/bom_operation/bom_operation.json
+++ b/erpnext/manufacturing/doctype/bom_operation/bom_operation.json
@@ -1,361 +1,119 @@
{
- "allow_copy": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "beta": 0,
- "creation": "2013-02-22 01:27:49",
- "custom": 0,
- "docstatus": 0,
- "doctype": "DocType",
- "document_type": "Setup",
- "editable_grid": 1,
- "engine": "InnoDB",
+ "creation": "2013-02-22 01:27:49",
+ "doctype": "DocType",
+ "document_type": "Setup",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "operation",
+ "workstation",
+ "description",
+ "col_break1",
+ "hour_rate",
+ "time_in_mins",
+ "batch_size",
+ "operating_cost",
+ "base_hour_rate",
+ "base_operating_cost",
+ "image"
+ ],
"fields": [
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "operation",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Operation",
- "length": 0,
- "no_copy": 0,
- "oldfieldname": "operation_no",
- "oldfieldtype": "Data",
- "options": "Operation",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "operation",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Operation",
+ "oldfieldname": "operation_no",
+ "oldfieldtype": "Data",
+ "options": "Operation",
+ "reqd": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "workstation",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Workstation",
- "length": 0,
- "no_copy": 0,
- "oldfieldname": "workstation",
- "oldfieldtype": "Link",
- "options": "Workstation",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "workstation",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Workstation",
+ "oldfieldname": "workstation",
+ "oldfieldtype": "Link",
+ "options": "Workstation"
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "description",
- "fieldtype": "Text Editor",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Description",
- "length": 0,
- "no_copy": 0,
- "oldfieldname": "opn_description",
- "oldfieldtype": "Text",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "description",
+ "fieldtype": "Text Editor",
+ "in_list_view": 1,
+ "label": "Description",
+ "oldfieldname": "opn_description",
+ "oldfieldtype": "Text"
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "col_break1",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "col_break1",
+ "fieldtype": "Column Break"
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "hour_rate",
- "fieldtype": "Currency",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Hour Rate",
- "length": 0,
- "no_copy": 0,
- "oldfieldname": "hour_rate",
- "oldfieldtype": "Currency",
- "options": "currency",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "hour_rate",
+ "fieldtype": "Currency",
+ "label": "Hour Rate",
+ "oldfieldname": "hour_rate",
+ "oldfieldtype": "Currency",
+ "options": "currency"
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "description": "In minutes",
- "fieldname": "time_in_mins",
- "fieldtype": "Float",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Operation Time ",
- "length": 0,
- "no_copy": 0,
- "oldfieldname": "time_in_mins",
- "oldfieldtype": "Currency",
- "options": "",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "description": "In minutes",
+ "fieldname": "time_in_mins",
+ "fieldtype": "Float",
+ "in_list_view": 1,
+ "label": "Operation Time ",
+ "oldfieldname": "time_in_mins",
+ "oldfieldtype": "Currency",
+ "reqd": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "operating_cost",
- "fieldtype": "Currency",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Operating Cost",
- "length": 0,
- "no_copy": 0,
- "oldfieldname": "operating_cost",
- "oldfieldtype": "Currency",
- "options": "currency",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "operating_cost",
+ "fieldtype": "Currency",
+ "in_list_view": 1,
+ "label": "Operating Cost",
+ "oldfieldname": "operating_cost",
+ "oldfieldtype": "Currency",
+ "options": "currency",
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "base_hour_rate",
- "fieldtype": "Currency",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Base Hour Rate(Company Currency)",
- "length": 0,
- "no_copy": 0,
- "options": "Company:company:default_currency",
- "permlevel": 0,
- "precision": "",
- "print_hide": 1,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "base_hour_rate",
+ "fieldtype": "Currency",
+ "label": "Base Hour Rate(Company Currency)",
+ "options": "Company:company:default_currency",
+ "print_hide": 1,
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "default": "5",
- "fieldname": "base_operating_cost",
- "fieldtype": "Currency",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Operating Cost(Company Currency)",
- "length": 0,
- "no_copy": 0,
- "options": "Company:company:default_currency",
- "permlevel": 0,
- "precision": "",
- "print_hide": 1,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "default": "5",
+ "fieldname": "base_operating_cost",
+ "fieldtype": "Currency",
+ "label": "Operating Cost(Company Currency)",
+ "options": "Company:company:default_currency",
+ "print_hide": 1,
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "image",
- "fieldtype": "Attach",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Image",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "fieldname": "image",
+ "fieldtype": "Attach",
+ "label": "Image"
+ },
+ {
+ "default": "1",
+ "fieldname": "batch_size",
+ "fieldtype": "Int",
+ "label": "Batch Size"
}
- ],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
- "idx": 1,
- "image_view": 0,
- "in_create": 0,
- "is_submittable": 0,
- "issingle": 0,
- "istable": 1,
- "max_attachments": 0,
- "modified": "2018-03-26 09:55:28.107451",
- "modified_by": "Administrator",
- "module": "Manufacturing",
- "name": "BOM Operation",
- "owner": "Administrator",
- "permissions": [],
- "quick_entry": 0,
- "read_only": 0,
- "read_only_onload": 0,
- "show_name_in_global_search": 0,
- "track_changes": 0,
- "track_seen": 0
-}
\ No newline at end of file
+ ],
+ "idx": 1,
+ "istable": 1,
+ "modified": "2019-07-16 22:35:55.374037",
+ "modified_by": "govindsmenokee@gmail.com",
+ "module": "Manufacturing",
+ "name": "BOM Operation",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC"
+}
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py
index 06f238aa5c..3a77e2f209 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py
@@ -505,7 +505,7 @@ def get_material_request_items(row, sales_order,
total_qty = row['qty']
required_qty = 0
- if ignore_existing_ordered_qty or bin_dict.get("projected_qty") < 0:
+ if ignore_existing_ordered_qty or bin_dict.get("projected_qty", 0) < 0:
required_qty = total_qty
elif total_qty > bin_dict.get("projected_qty"):
required_qty = total_qty - bin_dict.get("projected_qty")
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py
index 0e8f69145b..2b70325d9f 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/work_order.py
@@ -4,6 +4,7 @@
from __future__ import unicode_literals
import frappe
import json
+import math
from frappe import _
from frappe.utils import flt, get_datetime, getdate, date_diff, cint, nowdate
from frappe.model.document import Document
@@ -323,7 +324,7 @@ class WorkOrder(Document):
select
operation, description, workstation, idx,
base_hour_rate as hour_rate, time_in_mins,
- "Pending" as status, parent as bom
+ "Pending" as status, parent as bom, batch_size
from
`tabBOM Operation`
where
@@ -348,7 +349,7 @@ class WorkOrder(Document):
bom_qty = frappe.db.get_value("BOM", self.bom_no, "quantity")
for d in self.get("operations"):
- d.time_in_mins = flt(d.time_in_mins) / flt(bom_qty) * flt(self.qty)
+ d.time_in_mins = flt(d.time_in_mins) / flt(bom_qty) * math.ceil(flt(self.qty) / flt(d.batch_size))
self.calculate_operating_cost()
diff --git a/erpnext/manufacturing/doctype/work_order_operation/work_order_operation.json b/erpnext/manufacturing/doctype/work_order_operation/work_order_operation.json
index 9c1c95383b..75d42cd061 100644
--- a/erpnext/manufacturing/doctype/work_order_operation/work_order_operation.json
+++ b/erpnext/manufacturing/doctype/work_order_operation/work_order_operation.json
@@ -1,690 +1,200 @@
{
- "allow_copy": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "beta": 0,
- "creation": "2014-10-16 14:35:41.950175",
- "custom": 0,
- "docstatus": 0,
- "doctype": "DocType",
- "document_type": "",
- "editable_grid": 1,
- "engine": "InnoDB",
+ "creation": "2014-10-16 14:35:41.950175",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "details",
+ "operation",
+ "bom",
+ "description",
+ "col_break1",
+ "completed_qty",
+ "status",
+ "workstation",
+ "estimated_time_and_cost",
+ "planned_start_time",
+ "planned_end_time",
+ "column_break_10",
+ "time_in_mins",
+ "hour_rate",
+ "batch_size",
+ "planned_operating_cost",
+ "section_break_9",
+ "actual_start_time",
+ "actual_end_time",
+ "column_break_11",
+ "actual_operation_time",
+ "actual_operating_cost"
+ ],
"fields": [
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "details",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
+ "fieldname": "details",
+ "fieldtype": "Section Break"
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "operation",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Operation",
- "length": 0,
- "no_copy": 0,
- "oldfieldname": "operation_no",
- "oldfieldtype": "Data",
- "options": "Operation",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
+ "fieldname": "operation",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Operation",
+ "oldfieldname": "operation_no",
+ "oldfieldtype": "Data",
+ "options": "Operation",
+ "read_only": 1,
+ "reqd": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "bom",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "BOM",
- "length": 0,
- "no_copy": 1,
- "options": "BOM",
- "permlevel": 0,
- "precision": "",
- "print_hide": 1,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
+ "fieldname": "bom",
+ "fieldtype": "Link",
+ "label": "BOM",
+ "no_copy": 1,
+ "options": "BOM",
+ "print_hide": 1,
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "description",
- "fieldtype": "Text Editor",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Operation Description",
- "length": 0,
- "no_copy": 0,
- "oldfieldname": "opn_description",
- "oldfieldtype": "Text",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
+ "fieldname": "description",
+ "fieldtype": "Text Editor",
+ "label": "Operation Description",
+ "oldfieldname": "opn_description",
+ "oldfieldtype": "Text",
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "col_break1",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
+ "fieldname": "col_break1",
+ "fieldtype": "Column Break"
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "description": "Operation completed for how many finished goods?",
- "fieldname": "completed_qty",
- "fieldtype": "Float",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Completed Qty",
- "length": 0,
- "no_copy": 1,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
+ "description": "Operation completed for how many finished goods?",
+ "fieldname": "completed_qty",
+ "fieldtype": "Float",
+ "label": "Completed Qty",
+ "no_copy": 1,
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "default": "Pending",
- "fieldname": "status",
- "fieldtype": "Select",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Status",
- "length": 0,
- "no_copy": 1,
- "options": "Pending\nWork in Progress\nCompleted",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
+ "default": "Pending",
+ "fieldname": "status",
+ "fieldtype": "Select",
+ "in_list_view": 1,
+ "label": "Status",
+ "no_copy": 1,
+ "options": "Pending\nWork in Progress\nCompleted",
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "workstation",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Workstation",
- "length": 0,
- "no_copy": 0,
- "oldfieldname": "workstation",
- "oldfieldtype": "Link",
- "options": "Workstation",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
+ "fieldname": "workstation",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Workstation",
+ "oldfieldname": "workstation",
+ "oldfieldtype": "Link",
+ "options": "Workstation"
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "estimated_time_and_cost",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Estimated Time and Cost",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
+ "fieldname": "estimated_time_and_cost",
+ "fieldtype": "Section Break",
+ "label": "Estimated Time and Cost"
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "planned_start_time",
- "fieldtype": "Datetime",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Planned Start Time",
- "length": 0,
- "no_copy": 1,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
+ "fieldname": "planned_start_time",
+ "fieldtype": "Datetime",
+ "label": "Planned Start Time",
+ "no_copy": 1,
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "planned_end_time",
- "fieldtype": "Datetime",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Planned End Time",
- "length": 0,
- "no_copy": 1,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
+ "fieldname": "planned_end_time",
+ "fieldtype": "Datetime",
+ "label": "Planned End Time",
+ "no_copy": 1,
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "column_break_10",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
+ "fieldname": "column_break_10",
+ "fieldtype": "Column Break"
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "description": "in Minutes",
- "fieldname": "time_in_mins",
- "fieldtype": "Float",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Operation Time",
- "length": 0,
- "no_copy": 0,
- "oldfieldname": "time_in_mins",
- "oldfieldtype": "Currency",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
+ "description": "in Minutes",
+ "fieldname": "time_in_mins",
+ "fieldtype": "Float",
+ "in_list_view": 1,
+ "label": "Operation Time",
+ "oldfieldname": "time_in_mins",
+ "oldfieldtype": "Currency",
+ "reqd": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "hour_rate",
- "fieldtype": "Float",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Hour Rate",
- "length": 0,
- "no_copy": 0,
- "oldfieldname": "hour_rate",
- "oldfieldtype": "Currency",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
+ "fieldname": "hour_rate",
+ "fieldtype": "Float",
+ "label": "Hour Rate",
+ "oldfieldname": "hour_rate",
+ "oldfieldtype": "Currency",
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "planned_operating_cost",
- "fieldtype": "Currency",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Planned Operating Cost",
- "length": 0,
- "no_copy": 0,
- "options": "Company:company:default_currency",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
+ "fieldname": "planned_operating_cost",
+ "fieldtype": "Currency",
+ "label": "Planned Operating Cost",
+ "options": "Company:company:default_currency",
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "section_break_9",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Actual Time and Cost",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
+ "fieldname": "section_break_9",
+ "fieldtype": "Section Break",
+ "label": "Actual Time and Cost"
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "actual_start_time",
- "fieldtype": "Datetime",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Actual Start Time",
- "length": 0,
- "no_copy": 1,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
+ "fieldname": "actual_start_time",
+ "fieldtype": "Datetime",
+ "label": "Actual Start Time",
+ "no_copy": 1,
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "description": "Updated via 'Time Log'",
- "fieldname": "actual_end_time",
- "fieldtype": "Datetime",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Actual End Time",
- "length": 0,
- "no_copy": 1,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
+ "description": "Updated via 'Time Log'",
+ "fieldname": "actual_end_time",
+ "fieldtype": "Datetime",
+ "label": "Actual End Time",
+ "no_copy": 1,
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "column_break_11",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
+ "fieldname": "column_break_11",
+ "fieldtype": "Column Break"
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "description": "in Minutes\nUpdated via 'Time Log'",
- "fieldname": "actual_operation_time",
- "fieldtype": "Float",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Actual Operation Time",
- "length": 0,
- "no_copy": 1,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
+ "description": "in Minutes\nUpdated via 'Time Log'",
+ "fieldname": "actual_operation_time",
+ "fieldtype": "Float",
+ "label": "Actual Operation Time",
+ "no_copy": 1,
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "description": "(Hour Rate / 60) * Actual Operation Time",
- "fieldname": "actual_operating_cost",
- "fieldtype": "Currency",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Actual Operating Cost",
- "length": 0,
- "no_copy": 1,
- "options": "Company:company:default_currency",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
+ "description": "(Hour Rate / 60) * Actual Operation Time",
+ "fieldname": "actual_operating_cost",
+ "fieldtype": "Currency",
+ "label": "Actual Operating Cost",
+ "no_copy": 1,
+ "options": "Company:company:default_currency",
+ "read_only": 1
+ },
+ {
+ "fieldname": "batch_size",
+ "fieldtype": "Int",
+ "label": "Batch Size",
+ "read_only": 1
}
- ],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
- "idx": 0,
- "image_view": 0,
- "in_create": 0,
- "is_submittable": 0,
- "issingle": 0,
- "istable": 1,
- "max_attachments": 0,
- "modified": "2018-02-13 02:58:11.328693",
- "modified_by": "Administrator",
- "module": "Manufacturing",
+ ],
+ "istable": 1,
+ "modified": "2019-07-16 23:01:07.720337",
+ "modified_by": "govindsmenokee@gmail.com",
+ "module": "Manufacturing",
"name": "Work Order Operation",
- "name_case": "",
- "owner": "Administrator",
- "permissions": [],
- "quick_entry": 0,
- "read_only": 0,
- "read_only_onload": 0,
- "show_name_in_global_search": 0,
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1,
- "track_seen": 0
-}
\ No newline at end of file
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 0ba08187a8..398c6020a0 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -618,8 +618,10 @@ erpnext.patches.v11_1.set_missing_opportunity_from
erpnext.patches.v12_0.set_quotation_status
erpnext.patches.v12_0.set_priority_for_support
erpnext.patches.v12_0.delete_priority_property_setter
+erpnext.patches.v12_0.set_default_batch_size
execute:frappe.delete_doc("DocType", "Project Task")
erpnext.patches.v11_1.update_default_supplier_in_item_defaults
erpnext.patches.v12_0.update_due_date_in_gle
erpnext.patches.v12_0.add_default_buying_selling_terms_in_company
erpnext.patches.v12_0.update_ewaybill_field_position
+erpnext.patches.v11_1.set_status_for_material_request_type_manufacture
diff --git a/erpnext/patches/v11_1/set_status_for_material_request_type_manufacture.py b/erpnext/patches/v11_1/set_status_for_material_request_type_manufacture.py
new file mode 100644
index 0000000000..d41cff523d
--- /dev/null
+++ b/erpnext/patches/v11_1/set_status_for_material_request_type_manufacture.py
@@ -0,0 +1,9 @@
+from __future__ import unicode_literals
+import frappe
+
+def execute():
+ frappe.db.sql("""
+ update `tabMaterial Request`
+ set status='Manufactured'
+ where docstatus=1 and material_request_type='Manufacture' and per_ordered=100 and status != 'Stopped'
+ """)
\ No newline at end of file
diff --git a/erpnext/patches/v12_0/set_default_batch_size.py b/erpnext/patches/v12_0/set_default_batch_size.py
new file mode 100644
index 0000000000..6fb69456dd
--- /dev/null
+++ b/erpnext/patches/v12_0/set_default_batch_size.py
@@ -0,0 +1,19 @@
+import frappe
+
+
+def execute():
+ frappe.reload_doc("manufacturing", "doctype", "bom_operation")
+ frappe.reload_doc("manufacturing", "doctype", "work_order_operation")
+
+ frappe.db.sql("""
+ UPDATE
+ `tabBOM Operation` bo
+ SET
+ bo.batch_size = 1
+ """)
+ frappe.db.sql("""
+ UPDATE
+ `tabWork Order Operation` wop
+ SET
+ wop.batch_size = 1
+ """)
diff --git a/erpnext/projects/doctype/project/project.js b/erpnext/projects/doctype/project/project.js
index 5613f088e1..beba2bbe74 100644
--- a/erpnext/projects/doctype/project/project.js
+++ b/erpnext/projects/doctype/project/project.js
@@ -1,6 +1,23 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
frappe.ui.form.on("Project", {
+ setup(frm) {
+ frm.make_methods = {
+ 'Timesheet': () => {
+ let doctype = 'Timesheet';
+ frappe.model.with_doctype(doctype, () => {
+ let new_doc = frappe.model.get_new_doc(doctype);
+
+ // add a new row and set the project
+ let time_log = frappe.model.get_new_doc('Timesheet Detail');
+ time_log.project = frm.doc.name;
+ new_doc.time_logs = [time_log];
+
+ frappe.ui.form.make_quick_entry(doctype, null, null, new_doc);
+ });
+ },
+ }
+ },
onload: function (frm) {
var so = frappe.meta.get_docfield("Project", "sales_order");
so.get_route_options_for_new_doc = function (field) {
diff --git a/erpnext/selling/doctype/campaign/campaign.json b/erpnext/selling/doctype/campaign/campaign.json
index d12069959c..986ac1306c 100644
--- a/erpnext/selling/doctype/campaign/campaign.json
+++ b/erpnext/selling/doctype/campaign/campaign.json
@@ -6,18 +6,13 @@
"description": "Keep Track of Sales Campaigns. Keep track of Leads, Quotations, Sales Order etc from Campaigns to gauge Return on Investment. ",
"doctype": "DocType",
"document_type": "Setup",
+ "engine": "InnoDB",
"field_order": [
"campaign",
"campaign_name",
"naming_series",
- "from_date",
- "column_break1",
- "status",
- "to_date",
- "budget_section",
- "currency",
- "column_break2",
- "budget",
+ "campaign_schedules_section",
+ "campaign_schedules",
"description_section",
"description"
],
@@ -52,57 +47,25 @@
"oldfieldtype": "Text",
"width": "300px"
},
- {
- "fieldname": "status",
- "fieldtype": "Select",
- "in_list_view": 1,
- "label": "Status",
- "options": "\nPlanned\nIn Progress\nCompleted\nCancelled",
- "reqd": 1,
- "default": "Planned"
- },
- {
- "fieldname": "from_date",
- "fieldtype": "Date",
- "label": "From Date"
- },
- {
- "fieldname": "to_date",
- "fieldtype": "Date",
- "label": "To Date"
- },
- {
- "fieldname": "column_break1",
- "fieldtype": "Column Break"
- },
- {
- "fieldname": "budget",
- "fieldtype": "Currency",
- "label": "Budget"
- },
{
"fieldname": "description_section",
"fieldtype": "Section Break"
},
{
- "fieldname": "currency",
- "fieldtype": "Link",
- "label": "Currency",
- "options": "Currency"
+ "fieldname": "campaign_schedules",
+ "fieldtype": "Table",
+ "label": "Campaign Schedules",
+ "options": "Campaign Email Schedule"
},
{
- "fieldname": "column_break2",
- "fieldtype": "Column Break"
- },
- {
- "fieldname": "budget_section",
+ "fieldname": "campaign_schedules_section",
"fieldtype": "Section Break",
- "label": "BUDGET"
+ "label": "Campaign Schedules"
}
],
"icon": "fa fa-bullhorn",
"idx": 1,
- "modified": "2019-04-29 22:09:39.251884",
+ "modified": "2019-07-22 12:03:39.832342",
"modified_by": "Administrator",
"module": "Selling",
"name": "Campaign",
@@ -140,5 +103,7 @@
"write": 1
}
],
- "quick_entry": 1
-}
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC"
+}
\ No newline at end of file
diff --git a/erpnext/selling/doctype/campaign/campaign_dashboard.py b/erpnext/selling/doctype/campaign/campaign_dashboard.py
new file mode 100644
index 0000000000..a9d8eca38c
--- /dev/null
+++ b/erpnext/selling/doctype/campaign/campaign_dashboard.py
@@ -0,0 +1,13 @@
+from __future__ import unicode_literals
+from frappe import _
+
+def get_data():
+ return {
+ 'fieldname': 'campaign_name',
+ 'transactions': [
+ {
+ 'label': _('Email Campaigns'),
+ 'items': ['Email Campaign']
+ }
+ ],
+ }
diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py
index 0c5188a4fe..09dc9a9932 100755
--- a/erpnext/selling/doctype/sales_order/sales_order.py
+++ b/erpnext/selling/doctype/sales_order/sales_order.py
@@ -509,10 +509,12 @@ def make_material_request(source_name, target_doc=None):
doc.material_request_type = "Purchase"
def update_item(source, target, source_parent):
+ # qty is for packed items, because packed items don't have stock_qty field
+ qty = source.get("stock_qty") or source.get("qty")
target.project = source_parent.project
- target.qty = source.stock_qty - requested_item_qty.get(source.name, 0)
+ target.qty = qty - requested_item_qty.get(source.name, 0)
target.conversion_factor = 1
- target.stock_qty = source.stock_qty - requested_item_qty.get(source.name, 0)
+ target.stock_qty = qty - requested_item_qty.get(source.name, 0)
doc = get_mapped_doc("Sales Order", source_name, {
"Sales Order": {
diff --git a/erpnext/setup/doctype/item_group/item_group.py b/erpnext/setup/doctype/item_group/item_group.py
index 8fbeac8138..cab21162c7 100644
--- a/erpnext/setup/doctype/item_group/item_group.py
+++ b/erpnext/setup/doctype/item_group/item_group.py
@@ -69,8 +69,7 @@ class ItemGroup(NestedSet, WebsiteGenerator):
"items": get_product_list_for_group(product_group = self.name, start=start,
limit=context.page_length + 1, search=frappe.form_dict.get("search")),
"parents": get_parent_item_groups(self.parent_item_group),
- "title": self.name,
- "products_as_list": cint(frappe.db.get_single_value('Products Settings', 'products_as_list'))
+ "title": self.name
})
if self.slideshow:
diff --git a/erpnext/stock/dashboard/item_dashboard.js b/erpnext/stock/dashboard/item_dashboard.js
index 157dbfe174..1bfa2cf56c 100644
--- a/erpnext/stock/dashboard/item_dashboard.js
+++ b/erpnext/stock/dashboard/item_dashboard.js
@@ -16,18 +16,47 @@ erpnext.stock.ItemDashboard = Class.extend({
this.content = $(frappe.render_template('item_dashboard')).appendTo(this.parent);
this.result = this.content.find('.result');
- // move
this.content.on('click', '.btn-move', function() {
- erpnext.stock.move_item(unescape($(this).attr('data-item')), $(this).attr('data-warehouse'),
- null, $(this).attr('data-actual_qty'), null, function() { me.refresh(); });
+ handle_move_add($(this), "Move")
});
this.content.on('click', '.btn-add', function() {
- erpnext.stock.move_item(unescape($(this).attr('data-item')), null, $(this).attr('data-warehouse'),
- $(this).attr('data-actual_qty'), $(this).attr('data-rate'),
- function() { me.refresh(); });
+ handle_move_add($(this), "Add")
});
+ function handle_move_add(element, action) {
+ let item = unescape(element.attr('data-item'));
+ let warehouse = unescape(element.attr('data-warehouse'));
+ let actual_qty = unescape(element.attr('data-actual_qty'));
+ let disable_quick_entry = Number(unescape(element.attr('data-disable_quick_entry')));
+ let entry_type = action === "Move" ? "Material Transfer": null;
+
+ if (disable_quick_entry) {
+ open_stock_entry(item, warehouse, entry_type);
+ } else {
+ if (action === "Add") {
+ let rate = unescape($(this).attr('data-rate'));
+ erpnext.stock.move_item(item, null, warehouse, actual_qty, rate, function() { me.refresh(); });
+ }
+ else {
+ erpnext.stock.move_item(item, warehouse, null, actual_qty, null, function() { me.refresh(); });
+ }
+ }
+ }
+
+ function open_stock_entry(item, warehouse, entry_type) {
+ frappe.model.with_doctype('Stock Entry', function() {
+ var doc = frappe.model.get_new_doc('Stock Entry');
+ if (entry_type) doc.stock_entry_type = entry_type;
+
+ var row = frappe.model.add_child(doc, 'items');
+ row.item_code = item;
+ row.s_warehouse = warehouse;
+
+ frappe.set_route('Form', doc.doctype, doc.name);
+ })
+ }
+
// more
this.content.find('.btn-more').on('click', function() {
me.start += 20;
@@ -196,4 +225,4 @@ erpnext.stock.move_item = function(item, source, target, actual_qty, rate, callb
frappe.set_route('Form', doc.doctype, doc.name);
})
});
-}
\ No newline at end of file
+}
diff --git a/erpnext/stock/dashboard/item_dashboard.py b/erpnext/stock/dashboard/item_dashboard.py
index 487c765659..cafb5c3a0a 100644
--- a/erpnext/stock/dashboard/item_dashboard.py
+++ b/erpnext/stock/dashboard/item_dashboard.py
@@ -44,7 +44,9 @@ def get_data(item_code=None, warehouse=None, item_group=None,
for item in items:
item.update({
- 'item_name': frappe.get_cached_value("Item", item.item_code, 'item_name')
+ 'item_name': frappe.get_cached_value("Item", item.item_code, 'item_name'),
+ 'disable_quick_entry': frappe.get_cached_value("Item", item.item_code, 'has_batch_no')
+ or frappe.get_cached_value("Item", item.item_code, 'has_serial_no'),
})
return items
diff --git a/erpnext/stock/dashboard/item_dashboard_list.html b/erpnext/stock/dashboard/item_dashboard_list.html
index 5a3fa2ed48..e1914ed76a 100644
--- a/erpnext/stock/dashboard/item_dashboard_list.html
+++ b/erpnext/stock/dashboard/item_dashboard_list.html
@@ -43,11 +43,13 @@
{% if d.actual_qty %}