// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt frappe.provide("erpnext.buying"); // cur_frm.add_fetch('project', 'cost_center', 'cost_center'); erpnext.buying = { setup_buying_controller: function () { erpnext.buying.BuyingController = class BuyingController extends erpnext.TransactionController { setup() { super.setup(); this.toggle_enable_for_stock_uom("allow_to_edit_stock_uom_qty_for_purchase"); this.frm.email_field = "contact_email"; } onload(doc, cdt, cdn) { this.setup_queries(doc, cdt, cdn); super.onload(); this.frm.set_query("shipping_rule", function () { return { filters: { shipping_rule_type: "Buying", }, }; }); this.frm.set_query("project", function (doc) { return { filters: { company: doc.company, }, }; }); if ( this.frm.doc.__islocal && frappe.meta.has_field(this.frm.doc.doctype, "disable_rounded_total") ) { var df = frappe.meta.get_docfield(this.frm.doc.doctype, "disable_rounded_total"); var disable = cint(df.default) || cint(frappe.sys_defaults.disable_rounded_total); this.frm.set_value("disable_rounded_total", disable); } // no idea where me is coming from if (this.frm.get_field("shipping_address")) { this.frm.set_query("shipping_address", () => { if (this.frm.doc.customer) { return { query: "frappe.contacts.doctype.address.address.address_query", filters: { link_doctype: "Customer", link_name: this.frm.doc.customer }, }; } else return erpnext.queries.company_address_query(this.frm.doc); }); } if (this.frm.get_field("dispatch_address")) { this.frm.set_query("dispatch_address", () => { return erpnext.queries.address_query(this.frm.doc); }); } } setup_queries(doc, cdt, cdn) { var me = this; if (this.frm.fields_dict.buying_price_list) { this.frm.set_query("buying_price_list", function () { return { filters: { buying: 1 }, }; }); } if (this.frm.fields_dict.tc_name) { this.frm.set_query("tc_name", function () { return { filters: { buying: 1 }, }; }); } me.frm.set_query("supplier", erpnext.queries.supplier); me.frm.set_query("contact_person", erpnext.queries.contact_query); me.frm.set_query("supplier_address", erpnext.queries.address_query); me.frm.set_query("billing_address", erpnext.queries.company_address_query); erpnext.accounts.dimensions.setup_dimension_filters(me.frm, me.frm.doctype); this.frm.set_query("item_code", "items", function () { if (me.frm.doc.is_subcontracted) { var filters = { supplier: me.frm.doc.supplier }; if (me.frm.doc.is_old_subcontracting_flow) { filters["is_sub_contracted_item"] = 1; } else { filters["is_stock_item"] = 0; } return { query: "erpnext.controllers.queries.item_query", filters: filters, }; } else { return { query: "erpnext.controllers.queries.item_query", filters: { supplier: me.frm.doc.supplier, is_purchase_item: 1, has_variants: 0 }, }; } }); this.frm.set_query("manufacturer", "items", function (doc, cdt, cdn) { const row = locals[cdt][cdn]; return { query: "erpnext.controllers.queries.item_manufacturer_query", filters: { item_code: row.item_code }, }; }); if (this.frm.fields_dict["items"].grid.get_field("item_code")) { this.frm.set_query("item_tax_template", "items", function (doc, cdt, cdn) { return me.set_query_for_item_tax_template(doc, cdt, cdn); }); } } refresh(doc) { frappe.dynamic_link = { doc: this.frm.doc, fieldname: "supplier", doctype: "Supplier" }; this.frm.toggle_display( "supplier_name", this.frm.doc.supplier_name && this.frm.doc.supplier_name !== this.frm.doc.supplier ); if ( this.frm.doc.docstatus == 0 && (this.frm.doctype === "Purchase Order" || this.frm.doctype === "Material Request") ) { this.set_from_product_bundle(); } this.toggle_subcontracting_fields(); super.refresh(); } toggle_subcontracting_fields() { if (["Purchase Receipt", "Purchase Invoice"].includes(this.frm.doc.doctype)) { this.frm.fields_dict.supplied_items.grid.update_docfield_property( "consumed_qty", "read_only", this.frm.doc.__onload && this.frm.doc.__onload.backflush_based_on === "BOM" ); this.frm.set_df_property("supplied_items", "cannot_add_rows", 1); this.frm.set_df_property("supplied_items", "cannot_delete_rows", 1); } } supplier() { var me = this; erpnext.utils.get_party_details(this.frm, null, null, function () { me.apply_price_list(); }); } company() { super.company(); if (!frappe.meta.has_field(this.frm.doc.doctype, "billing_address")) return; frappe.call({ method: "erpnext.setup.doctype.company.company.get_billing_shipping_address", args: { name: this.frm.doc.company, billing_address: this.frm.doc.billing_address, shipping_address: this.frm.doc.shipping_address, }, callback: (r) => { if (!this.frm.doc.billing_address) this.frm.set_value("billing_address", r.message.primary_address || ""); if ( !frappe.meta.has_field(this.frm.doc.doctype, "shipping_address") || this.frm.doc.shipping_address ) return; this.frm.set_value("shipping_address", r.message.shipping_address || ""); }, }); erpnext.utils.set_letter_head(this.frm); } supplier_address() { erpnext.utils.get_address_display(this.frm); erpnext.utils.set_taxes_from_address( this.frm, "supplier_address", "supplier_address", "supplier_address" ); } buying_price_list() { this.apply_price_list(); } discount_percentage(doc, cdt, cdn) { var item = frappe.get_doc(cdt, cdn); item.discount_amount = 0.0; this.price_list_rate(doc, cdt, cdn); } discount_amount(doc, cdt, cdn) { var item = frappe.get_doc(cdt, cdn); item.discount_percentage = 0.0; this.price_list_rate(doc, cdt, cdn); } qty(doc, cdt, cdn) { if ( doc.doctype == "Purchase Receipt" || (doc.doctype == "Purchase Invoice" && doc.update_stock) ) { this.calculate_received_qty(doc, cdt, cdn); } super.qty(doc, cdt, cdn); } rejected_qty(doc, cdt, cdn) { this.calculate_received_qty(doc, cdt, cdn); } calculate_received_qty(doc, cdt, cdn) { var item = frappe.get_doc(cdt, cdn); frappe.model.round_floats_in(item, ["qty", "rejected_qty"]); if ( !doc.is_return && this.validate_negative_quantity(cdt, cdn, item, ["qty", "rejected_qty"]) ) { return; } let received_qty = flt(item.qty + item.rejected_qty, precision("received_qty", item)); let received_stock_qty = flt(item.conversion_factor, precision("conversion_factor", item)) * flt(received_qty); frappe.model.set_value(cdt, cdn, "received_qty", received_qty); frappe.model.set_value(cdt, cdn, "received_stock_qty", received_stock_qty); } batch_no(doc, cdt, cdn) { super.batch_no(doc, cdt, cdn); } validate_negative_quantity(cdt, cdn, item, fieldnames) { if (!item || !fieldnames) { return; } var is_negative_qty = false; for (var i = 0; i < fieldnames.length; i++) { if (item[fieldnames[i]] < 0) { frappe.msgprint( __("Row #{0}: {1} can not be negative for item {2}", [ item.idx, __(frappe.meta.get_label(cdt, fieldnames[i], cdn)), item.item_code, ]) ); is_negative_qty = true; break; } } return is_negative_qty; } warehouse(doc, cdt, cdn) { var item = frappe.get_doc(cdt, cdn); if (item.item_code && item.warehouse) { return this.frm.call({ method: "erpnext.stock.get_item_details.get_bin_details", child: item, args: { item_code: item.item_code, warehouse: item.warehouse, company: doc.company, include_child_warehouses: true, }, }); } } project(doc, cdt, cdn) { var item = frappe.get_doc(cdt, cdn); if (item.project) { $.each(this.frm.doc["items"] || [], function (i, other_item) { if (!other_item.project) { other_item.project = item.project; refresh_field("project", other_item.name, other_item.parentfield); } }); } } rejected_warehouse(doc, cdt) { // trigger autofill_warehouse only if parent rejected_warehouse field is triggered if (["Purchase Invoice", "Purchase Receipt"].includes(cdt)) { this.autofill_warehouse(doc.items, "rejected_warehouse", doc.rejected_warehouse); } } category(doc, cdt, cdn) { // should be the category field of tax table if (cdt != doc.doctype) { this.calculate_taxes_and_totals(); } } add_deduct_tax(doc, cdt, cdn) { this.calculate_taxes_and_totals(); } set_from_product_bundle() { var me = this; this.frm.add_custom_button( __("Product Bundle"), function () { erpnext.buying.get_items_from_product_bundle(me.frm); }, __("Get Items From") ); } shipping_address() { var me = this; erpnext.utils.get_address_display( this.frm, "shipping_address", "shipping_address_display", true ); } dispatch_address() { var me = this; erpnext.utils.get_address_display( this.frm, "dispatch_address", "dispatch_address_display", true ); } billing_address() { erpnext.utils.get_address_display( this.frm, "billing_address", "billing_address_display", true ); } tc_name() { this.get_terms(); } update_auto_repeat_reference(doc) { if (doc.auto_repeat) { frappe.call({ method: "frappe.automation.doctype.auto_repeat.auto_repeat.update_reference", args: { docname: doc.auto_repeat, reference: doc.name, }, callback: function (r) { if (r.message == "success") { frappe.show_alert({ message: __("Auto repeat document updated"), indicator: "green", }); } else { frappe.show_alert({ message: __("An error occurred during the update process"), indicator: "red", }); } }, }); } } manufacturer(doc, cdt, cdn) { const row = locals[cdt][cdn]; if (row.manufacturer) { frappe.call({ method: "erpnext.stock.doctype.item_manufacturer.item_manufacturer.get_item_manufacturer_part_no", args: { item_code: row.item_code, manufacturer: row.manufacturer, }, callback: function (r) { if (r.message) { frappe.model.set_value(cdt, cdn, "manufacturer_part_no", r.message); } }, }); } } manufacturer_part_no(doc, cdt, cdn) { const row = locals[cdt][cdn]; if (row.manufacturer_part_no) { frappe.model.get_value( "Item Manufacturer", { item_code: row.item_code, manufacturer: row.manufacturer, manufacturer_part_no: row.manufacturer_part_no, }, "name", function (data) { if (!data) { let msg = { message: __("Manufacturer Part Number {0} is invalid", [ row.manufacturer_part_no, ]), title: __("Invalid Part Number"), }; frappe.throw(msg); } } ); } } add_serial_batch_bundle(doc, cdt, cdn) { let item = locals[cdt][cdn]; let me = this; let fields = ["has_batch_no", "has_serial_no"]; frappe.db.get_value("Item", item.item_code, fields).then((r) => { if (r.message && (r.message.has_batch_no || r.message.has_serial_no)) { fields.forEach((field) => { item[field] = r.message[field]; }); item.type_of_transaction = item.qty > 0 ? "Inward" : "Outward"; item.is_rejected = false; new erpnext.SerialBatchPackageSelector(me.frm, item, (r) => { if (r) { let qty = Math.abs(r.total_qty); if (doc.is_return) { qty = qty * -1; } let update_values = { serial_and_batch_bundle: r.name, use_serial_batch_fields: 0, qty: qty / flt( item.conversion_factor || 1, precision("conversion_factor", item) ), }; if (r.warehouse) { update_values["warehouse"] = r.warehouse; } frappe.model.set_value(item.doctype, item.name, update_values); } }); } }); } add_serial_batch_for_rejected_qty(doc, cdt, cdn) { let item = locals[cdt][cdn]; let me = this; let fields = ["has_batch_no", "has_serial_no"]; frappe.db.get_value("Item", item.item_code, fields).then((r) => { if (r.message && (r.message.has_batch_no || r.message.has_serial_no)) { fields.forEach((field) => { item[field] = r.message[field]; }); item.type_of_transaction = !doc.is_return > 0 ? "Inward" : "Outward"; item.is_rejected = true; new erpnext.SerialBatchPackageSelector(me.frm, item, (r) => { if (r) { let qty = Math.abs(r.total_qty); if (doc.is_return) { qty = qty * -1; } let update_values = { rejected_serial_and_batch_bundle: r.name, use_serial_batch_fields: 0, rejected_qty: qty / flt( item.conversion_factor || 1, precision("conversion_factor", item) ), }; if (r.warehouse) { update_values["rejected_warehouse"] = r.warehouse; } frappe.model.set_value(item.doctype, item.name, update_values); } }); } }); } }; }, }; erpnext.buying.link_to_mrs = function (frm) { frappe.call({ method: "erpnext.buying.utils.get_linked_material_requests", args: { items: frm.doc.items.map((item) => item.item_code), }, callback: function (r) { if (!r.message || r.message.length == 0) { frappe.throw({ message: __("No pending Material Requests found to link for the given items."), title: __("Note"), }); } var item_length = frm.doc.items.length; for (let item of frm.doc.items) { var qty = item.qty; (r.message[0] || []).forEach(function (d) { if ( d.qty > 0 && qty > 0 && item.item_code == d.item_code && !item.material_request_item ) { item.material_request = d.mr_name; item.material_request_item = d.mr_item; var my_qty = Math.min(qty, d.qty); qty = qty - my_qty; d.qty = d.qty - my_qty; item.stock_qty = my_qty * item.conversion_factor; item.qty = my_qty; frappe.msgprint( "Assigning " + d.mr_name + " to " + d.item_code + " (row " + item.idx + ")" ); if (qty > 0) { frappe.msgprint("Splitting " + qty + " units of " + d.item_code); var newrow = frappe.model.add_child(frm.doc, item.doctype, "items"); item_length++; for (var key in item) { newrow[key] = item[key]; } newrow.idx = item_length; newrow["stock_qty"] = newrow.conversion_factor * qty; newrow["qty"] = qty; newrow["material_request"] = ""; newrow["material_request_item"] = ""; } } }); } refresh_field("items"); }, }); }; erpnext.buying.get_default_bom = function (frm) { $.each(frm.doc["items"] || [], function (i, d) { if (d.item_code && d.bom === "") { return frappe.call({ type: "GET", method: "erpnext.stock.get_item_details.get_default_bom", args: { item_code: d.item_code, }, callback: function (r) { if (r) { frappe.model.set_value(d.doctype, d.name, "bom", r.message); } }, }); } }); }; erpnext.buying.get_items_from_product_bundle = function (frm) { var dialog = new frappe.ui.Dialog({ title: __("Get Items from Product Bundle"), fields: [ { fieldtype: "Link", label: __("Product Bundle"), fieldname: "product_bundle", options: "Product Bundle", reqd: 1, }, { fieldtype: "Currency", label: __("Quantity"), fieldname: "quantity", reqd: 1, default: 1, }, ], primary_action_label: "Get Items", primary_action(args) { if (!args) return; dialog.hide(); return frappe.call({ type: "GET", method: "erpnext.stock.doctype.packed_item.packed_item.get_items_from_product_bundle", args: { row: { item_code: args.product_bundle, quantity: args.quantity, parenttype: frm.doc.doctype, parent: frm.doc.name, supplier: frm.doc.supplier, currency: frm.doc.currency, conversion_rate: frm.doc.conversion_rate, price_list: frm.doc.buying_price_list, price_list_currency: frm.doc.price_list_currency, plc_conversion_rate: frm.doc.plc_conversion_rate, company: frm.doc.company, is_subcontracted: frm.doc.is_subcontracted, transaction_date: frm.doc.transaction_date || frm.doc.posting_date, ignore_pricing_rule: frm.doc.ignore_pricing_rule, doctype: frm.doc.doctype, }, }, freeze: true, callback: function (r) { const first_row_is_empty = function (child_table) { if ($.isArray(child_table) && child_table.length > 0) { return !child_table[0].item_code; } return false; }; const remove_empty_first_row = function (frm) { if (first_row_is_empty(frm.doc.items)) { frm.doc.items = frm.doc.items.splice(1); } }; if (!r.exc && r.message) { remove_empty_first_row(frm); for (var i = 0; i < r.message.length; i++) { var d = frm.add_child("items"); var item = r.message[i]; for (var key in item) { if (!is_null(item[key]) && key !== "doctype") { d[key] = item[key]; } } if (frappe.meta.get_docfield(d.doctype, "price_list_rate", d.name)) { frm.script_manager.trigger("price_list_rate", d.doctype, d.name); } } frm.refresh_field("items"); } }, }); }, }); dialog.show(); };