diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index f258546d61..c6a5dae629 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -219,6 +219,9 @@ class PurchaseOrder(BuyingController): frappe.get_doc('Authorization Control').validate_approving_authority(self.doctype, self.company, self.base_grand_total) + self.update_blanket_order() + + def on_cancel(self): super(PurchaseOrder, self).on_cancel() @@ -241,6 +244,9 @@ class PurchaseOrder(BuyingController): self.update_requested_qty() self.update_ordered_qty() + self.update_blanket_order(cancel=True) + + def on_update(self): pass diff --git a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json index fa372baa96..281251900e 100755 --- a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json +++ b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json @@ -1594,6 +1594,37 @@ "translatable": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "delivered_by_supplier", + "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": "To be delivered to customer", + "length": 0, + "no_copy": 0, + "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 + }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -1611,7 +1642,7 @@ "in_standard_filter": 0, "label": "Blanket Order", "length": 0, - "no_copy": 0, + "no_copy": 1, "options": "Blanket Order", "permlevel": 0, "precision": "", @@ -1632,8 +1663,8 @@ "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "delivered_by_supplier", - "fieldtype": "Check", + "fieldname": "blanket_order_rate", + "fieldtype": "Currency", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, @@ -1641,9 +1672,9 @@ "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, - "label": "To be delivered to customer", + "label": "Blanket Order Rate", "length": 0, - "no_copy": 0, + "no_copy": 1, "permlevel": 0, "precision": "", "print_hide": 1, @@ -2083,7 +2114,7 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2018-05-24 09:15:37.473332", + "modified": "2018-05-28 08:43:23.251530", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order Item", diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index a76a3b3bfb..6ed9507526 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -342,6 +342,18 @@ class StockController(AccountsController): if self.docstatus==1: raise frappe.ValidationError + def update_blanket_order(self, cancel=False): + for item in self.items: + if item.blanket_order: + ordered_qty, doc_name = frappe.db.get_value("Blanket Order Item", {"parent": item.blanket_order}, ["ordered_qty", "name"]) + if not cancel: + ordered_qty = ordered_qty + item.qty + else: + ordered_qty = ordered_qty - item.qty + ordered_qty = flt(ordered_qty, item.precision("qty")) + frappe.db.set_value("Blanket Order Item", doc_name, "ordered_qty", ordered_qty) + + def update_gl_entries_after(posting_date, posting_time, for_warehouses=None, for_items=None, warehouse_account=None): def _delete_gl_entries(voucher_type, voucher_no): diff --git a/erpnext/manufacturing/doctype/blanket_order/blanket_order.js b/erpnext/manufacturing/doctype/blanket_order/blanket_order.js index 5e6a2a67f1..0c02d1cd85 100644 --- a/erpnext/manufacturing/doctype/blanket_order/blanket_order.js +++ b/erpnext/manufacturing/doctype/blanket_order/blanket_order.js @@ -3,41 +3,28 @@ frappe.ui.form.on('Blanket Order', { refresh: function(frm) { - if (frm.doc.customer) { + if (frm.doc.customer && frm.doc.docstatus === 1) { frm.add_custom_button(__('View Orders'), function() { frappe.set_route('List', 'Sales Order', {blanket_order: frm.doc.name}); }); - if (frm.doc.docstatus === 1) { - frm.add_primary_button(__("Create Sales Order"), function(){ - + frm.add_custom_button(__("Create Sales Order"), function(){ + frappe.model.open_mapped_doc({ + method: "erpnext.manufacturing.doctype.blanket_order.blanket_order.make_sales_order", + frm: frm }); - } + }).addClass("btn-primary"); } - if (frm.doc.supplier) { + if (frm.doc.supplier && frm.doc.docstatus === 1) { frm.add_custom_button(__('View Orders'), function() { frappe.set_route('List', 'Purchase Order', {blanket_order: frm.doc.name}); }); - } - - // if (frm.doc.project) { - // frm.add_custom_button(__('Project'), function() { - // frappe.set_route("Form", "Project", frm.doc.project); - // },__("View")); - // frm.add_custom_button(__('Task'), function() { - // frappe.set_route('List', 'Task', {project: frm.doc.project}); - // },__("View")); - // } - - if ((!frm.doc.employee) && (frm.doc.docstatus === 1)) { - frm.add_custom_button(__('Employee'), function () { + frm.add_custom_button(__("Create Purchase Order"), function(){ frappe.model.open_mapped_doc({ - method: "erpnext.hr.doctype.employee_onboarding.employee_onboarding.make_employee", + method: "erpnext.manufacturing.doctype.blanket_order.blanket_order.make_purchase_order", frm: frm }); - }, __("Make")); - frm.page.set_inner_btn_group_as_primary(__("Make")); + }).addClass("btn-primary"); } - } }); diff --git a/erpnext/manufacturing/doctype/blanket_order/blanket_order.json b/erpnext/manufacturing/doctype/blanket_order/blanket_order.json index a3247ec052..8ad31cabf2 100644 --- a/erpnext/manufacturing/doctype/blanket_order/blanket_order.json +++ b/erpnext/manufacturing/doctype/blanket_order/blanket_order.json @@ -26,7 +26,7 @@ "ignore_xss_filter": 0, "in_filter": 0, "in_global_search": 0, - "in_list_view": 1, + "in_list_view": 0, "in_standard_filter": 0, "label": "Series", "length": 0, @@ -437,7 +437,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-05-24 07:56:37.911486", + "modified": "2018-05-28 05:56:05.922333", "modified_by": "Administrator", "module": "Manufacturing", "name": "Blanket Order", @@ -467,6 +467,7 @@ "quick_entry": 1, "read_only": 0, "read_only_onload": 0, + "search_fields": "order_type, to_date", "show_name_in_global_search": 0, "sort_field": "modified", "sort_order": "DESC", diff --git a/erpnext/manufacturing/doctype/blanket_order/blanket_order.py b/erpnext/manufacturing/doctype/blanket_order/blanket_order.py index c23c3dc715..06b6810982 100644 --- a/erpnext/manufacturing/doctype/blanket_order/blanket_order.py +++ b/erpnext/manufacturing/doctype/blanket_order/blanket_order.py @@ -5,6 +5,39 @@ from __future__ import unicode_literals import frappe from frappe.model.document import Document +from frappe.model.mapper import get_mapped_doc + class BlanketOrder(Document): pass + + +@frappe.whitelist() +def make_sales_order(source_name): + return get_mapped_doc("Blanket Order", source_name, { + "Blanket Order": { + "doctype": "Sales Order" + }, + "Blanket Order Item": { + "doctype": "Sales Order Item", + "field_map": { + "rate": "blanket_order_rate", + "parent": "blanket_order" + } + } + }) + +@frappe.whitelist() +def make_purchase_order(source_name): + return get_mapped_doc("Blanket Order", source_name, { + "Blanket Order": { + "doctype": "Purchase Order" + }, + "Blanket Order Item": { + "doctype": "Purchase Order Item", + "field_map": { + "rate": "blanket_order_rate", + "parent": "blanket_order" + } + } + }) diff --git a/erpnext/public/js/controllers/buying.js b/erpnext/public/js/controllers/buying.js index bbeb8e94f1..7eaba09ef9 100644 --- a/erpnext/public/js/controllers/buying.js +++ b/erpnext/public/js/controllers/buying.js @@ -108,8 +108,11 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({ var item = frappe.get_doc(cdt, cdn); frappe.model.round_floats_in(item, ["price_list_rate", "discount_percentage"]); - item.rate = flt(item.price_list_rate * (1 - item.discount_percentage / 100.0), - precision("rate", item)); + let item_rate = item.price_list_rate; + if (doc.doctype == "Purchase Order" && item.blanket_order_rate) { + item_rate = item.blanket_order_rate; + } + item.rate = flt(item_rate * (1 - item.discount_percentage / 100.0), precision("rate", item)); this.calculate_taxes_and_totals(); }, diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js index 71c098fbf8..0047186053 100644 --- a/erpnext/public/js/controllers/taxes_and_totals.js +++ b/erpnext/public/js/controllers/taxes_and_totals.js @@ -4,12 +4,15 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ setup: function() {}, apply_pricing_rule_on_item: function(item){ - + let effective_item_rate = item.price_list_rate; + if (item.parenttype === "Sales Order" && item.blanket_order_rate) { + effective_item_rate = item.blanket_order_rate; + } if(item.margin_type == "Percentage"){ - item.rate_with_margin = flt(item.price_list_rate) - + flt(item.price_list_rate) * ( flt(item.margin_rate_or_amount) / 100); + item.rate_with_margin = flt(effective_item_rate) + + flt(effective_item_rate) * ( flt(item.margin_rate_or_amount) / 100); } else { - item.rate_with_margin = flt(item.price_list_rate) + flt(item.margin_rate_or_amount); + item.rate_with_margin = flt(effective_item_rate) + flt(item.margin_rate_or_amount); item.base_rate_with_margin = flt(item.rate_with_margin) * flt(this.frm.doc.conversion_rate); } diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index ff0d134d81..3ed4e73e4a 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -1323,6 +1323,36 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ } }) } + }, + + blanket_order: function(doc, cdt, cdn) { + var me = this; + var item = locals[cdt][cdn]; + if (item.blanket_order && (item.parenttype=="Sales Order" || item.parenttype=="Purchase Order")) { + frappe.call({ + method: "erpnext.stock.get_item_details.get_blanket_order_details", + args: { + args:{ + item_code: item.item_code, + customer: doc.customer, + supplier: doc.supplier, + company: doc.company, + transaction_date: doc.transaction_date, + blanket_order: item.blanket_order + } + }, + callback: function(r) { + if (!r.message) { + frappe.throw(__("Invalid Blanket Order for the selected Customer and Item")) + } else { + frappe.run_serially([ + () => frappe.model.set_value(cdt, cdn, "blanket_order_rate", r.message.blanket_order_rate), + () => me.frm.script_manager.trigger("price_list_rate", cdt, cdn) + ]); + } + } + }) + } } }); diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 46303b550e..95c7c9286d 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -176,6 +176,8 @@ class SalesOrder(SellingController): self.update_project() self.update_prevdoc_status('submit') + self.update_blanket_order() + def on_cancel(self): # Cannot cancel closed SO if self.status == 'Closed': @@ -188,6 +190,8 @@ class SalesOrder(SellingController): frappe.db.set(self, 'status', 'Cancelled') + self.update_blanket_order(cancel=True) + def update_project(self): project_list = [] if self.project: @@ -382,6 +386,7 @@ class SalesOrder(SellingController): d.set("delivery_date", get_next_schedule_date(reference_delivery_date, auto_repeat_doc.frequency, cint(auto_repeat_doc.repeat_on_day))) + def get_list_context(context=None): from erpnext.controllers.website_list_for_contact import get_list_context list_context = get_list_context(context) diff --git a/erpnext/selling/doctype/sales_order_item/sales_order_item.json b/erpnext/selling/doctype/sales_order_item/sales_order_item.json index 9d8e298b54..904d8fa3cd 100644 --- a/erpnext/selling/doctype/sales_order_item/sales_order_item.json +++ b/erpnext/selling/doctype/sales_order_item/sales_order_item.json @@ -1681,38 +1681,6 @@ "translatable": 0, "unique": 0 }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "blanket_order", - "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": "Blanket Order", - "length": 0, - "no_copy": 0, - "options": "Blanket Order", - "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 - }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -1875,6 +1843,69 @@ "translatable": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "blanket_order", + "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": "Blanket Order", + "length": 0, + "no_copy": 1, + "options": "Blanket Order", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "blanket_order_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": "Blanket Order Rate", + "length": 0, + "no_copy": 1, + "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 + }, { "allow_bulk_edit": 0, "allow_on_submit": 1, @@ -2307,7 +2338,7 @@ "istable": 1, "max_attachments": 0, "menu_index": 0, - "modified": "2018-05-24 09:14:45.810084", + "modified": "2018-05-28 05:52:36.908884", "modified_by": "Administrator", "module": "Selling", "name": "Sales Order Item", diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index 3b1b4f3e18..2eed4c9afc 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -64,6 +64,8 @@ def get_item_details(args): else: out.update(get_valuation_rate(args.item_code, args.company, out.get("warehouse"))) + update_party_blanket_order(args, out) + get_price_list_rate(args, item_doc, out) if args.customer and cint(args.is_pos): @@ -185,7 +187,6 @@ def get_basic_details(args, item): company: "", order_type: "", is_pos: "", - ignore_pricing_rule: "", project: "", qty: "", stock_qty: "", @@ -735,3 +736,32 @@ def get_serial_no(args, serial_nos=None): serial_no = serial_nos return serial_no + + +def update_party_blanket_order(args, out): + blanket_order_details = get_blanket_order_details(args) + if blanket_order_details: + out.update(blanket_order_details) + +@frappe.whitelist() +def get_blanket_order_details(args): + if isinstance(args, string_types): + args = frappe._dict(json.loads(args)) + + blanket_order_details = None + condition1, condition2 = ' ', ' ' + if args.item_code: + if args.customer and args.doctype == "Sales Order": + condition1 = ' and bo.customer=%(customer)s ' + elif args.supplier and args.doctype == "Purchase Order": + condition1 = ' and bo.supplier=%(supplier)s ' + if args.blanket_order: + condition2 = ' and bo.name =%(blanket_order)s ' + blanket_order_details = frappe.db.sql(''' + select boi.rate as blanket_order_rate, bo.name as blanket_order + from `tabBlanket Order` bo, `tabBlanket Order Item` boi + where bo.to_date>=%(transaction_date)s and bo.company=%(company)s and boi.item_code=%(item_code)s + and bo.docstatus=1 and bo.name = boi.parent {0} {1} + '''.format(condition1, condition2), args, as_dict=True) + blanket_order_details = blanket_order_details[0] if blanket_order_details else '' + return blanket_order_details