diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js index b6c48c81b96..3dbf4d40eba 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 b87725ff546..72d53bfa016 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 219d9899d28..bc2ddffbcf3 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/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 46913dc3af2..874230052a6 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) @@ -396,16 +398,15 @@ class SalesInvoice(SellingController): self.account_for_change_amount = pos.get('account_for_change_amount') for fieldname in ('territory', 'naming_series', 'currency', 'letter_head', 'tc_name', - 'company', 'select_print_heading', 'cash_bank_account', 'write_off_account', + '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') - for field in ['taxes_and_charges', 'company_address']: - if pos.get(field): - self.set(field, pos.get(fieldname)) + 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')) @@ -1259,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 eca59750d5b..1923f78cf89 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 cd3d8dc1258..98c25b7514a 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 c40310b1932..418a23c2d7e 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 a617de3ba76..e1ed642e732 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -690,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 diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 45f7b30ae8a..c398a7342a6 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 028736c7c41..7e1b165a469 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 d3ee4475450..48295bee26a 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 e0f4be9233a..f5d8da74c56 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/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 47f56cbfb1e..ca59a396b87 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 2c46db0935e..0b4d38ce5c2 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/status_updater.py b/erpnext/controllers/status_updater.py index 2da0f0381b4..d8c50b2622f 100644 --- a/erpnext/controllers/status_updater.py +++ b/erpnext/controllers/status_updater.py @@ -90,7 +90,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/crm/doctype/lead/lead.js b/erpnext/crm/doctype/lead/lead.js index cfa72901104..122e2b4eee8 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 12b646dad77..01eee5b61f2 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/hr/doctype/designation/test_designation.py b/erpnext/hr/doctype/designation/test_designation.py index 3b00bd327ac..3b300941a65 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/employee/employee.py b/erpnext/hr/doctype/employee/employee.py index af87f85743b..cf418b0e8fe 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 3f5a2ab3337..8dd0acf4551 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 a78c9b2ddda..e9de393bc4e 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 3ca862be851..6d275c82d9d 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 7e3014b38c8..ef8004eedb7 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 c3aeb2b368c..88865964500 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 58a362d0864..28d9c43f8e3 100644 --- a/erpnext/hr/doctype/loan_application/loan_application.py +++ b/erpnext/hr/doctype/loan_application/loan_application.py @@ -30,7 +30,7 @@ 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) - diff --git a/erpnext/hr/doctype/staffing_plan/staffing_plan.js b/erpnext/hr/doctype/staffing_plan/staffing_plan.js index 4fbc6b3089a..04af2323c72 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 83e53135ef4..e6afbcc220f 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 22dba99af00..4a0ce1800a5 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 f1d16096c06..77164c4e675 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/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index 6f63dcf1ec6..a7162933bf0 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 08c4f4fce68..3ca851d783b 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/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index 0e8f69145b0..2b70325d9f1 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 9c1c95383bb..75d42cd061e 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 0ba08187a81..398c6020a05 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 00000000000..d41cff523d5 --- /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 00000000000..6fb69456dd1 --- /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 5613f088e16..beba2bbe743 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/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index 03e903c06f9..15687293447 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -197,6 +197,9 @@ class Item(WebsiteGenerator): 'route')) + '/' + self.scrub((self.item_name if self.item_name else self.item_code) + '-' + random_string(5)) def validate_website_image(self): + if frappe.flags.in_import: + return + """Validate if the website image is a public file""" auto_set_website_image = False if not self.website_image and self.image: @@ -216,8 +219,7 @@ class Item(WebsiteGenerator): if not file_doc: if not auto_set_website_image: - frappe.msgprint(_("Website Image {0} attached to Item {1} cannot be found") - .format(self.website_image, self.name)) + frappe.msgprint(_("Website Image {0} attached to Item {1} cannot be found").format(self.website_image, self.name)) self.website_image = None @@ -228,6 +230,9 @@ class Item(WebsiteGenerator): self.website_image = None def make_thumbnail(self): + if frappe.flags.in_import: + return + """Make a thumbnail of `website_image`""" import requests.exceptions diff --git a/erpnext/stock/doctype/material_request/material_request_list.js b/erpnext/stock/doctype/material_request/material_request_list.js index 1cf971ff449..614ecb8a8f9 100644 --- a/erpnext/stock/doctype/material_request/material_request_list.js +++ b/erpnext/stock/doctype/material_request/material_request_list.js @@ -20,6 +20,8 @@ frappe.listview_settings['Material Request'] = { return [__("Issued"), "green", "per_ordered,=,100"]; } else if (doc.material_request_type == "Customer Provided") { return [__("Received"), "green", "per_ordered,=,100"]; + } else if (doc.material_request_type == "Manufacture") { + return [__("Manufactured"), "green", "per_ordered,=,100"]; } } } diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js index a2d3e75f239..7da648aad99 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js @@ -264,8 +264,12 @@ frappe.ui.form.on('Purchase Receipt Item', { var d = locals[cdt][cdn]; frappe.db.get_value('Item', {name: d.item_code}, 'sample_quantity', (r) => { frappe.model.set_value(cdt, cdn, "sample_quantity", r.sample_quantity); + validate_sample_quantity(frm, cdt, cdn); }); }, + qty: function(frm, cdt, cdn) { + validate_sample_quantity(frm, cdt, cdn); + }, sample_quantity: function(frm, cdt, cdn) { validate_sample_quantity(frm, cdt, cdn); }, @@ -283,7 +287,7 @@ cur_frm.cscript['Make Stock Entry'] = function() { var validate_sample_quantity = function(frm, cdt, cdn) { var d = locals[cdt][cdn]; - if (d.sample_quantity) { + if (d.sample_quantity && d.qty) { frappe.call({ method: 'erpnext.stock.doctype.stock_entry.stock_entry.validate_sample_quantity', args: {