// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt

// eslint-disable-next-line
frappe.provide("erpnext.accounts.dimensions");
erpnext.buying.setup_buying_controller();

frappe.ui.form.on("Material Request", {
	setup: function (frm) {
		frm.custom_make_buttons = {
			"Stock Entry": "Issue Material",
			"Pick List": "Pick List",
			"Purchase Order": "Purchase Order",
			"Request for Quotation": "Request for Quotation",
			"Supplier Quotation": "Supplier Quotation",
			"Work Order": "Work Order",
			"Purchase Receipt": "Purchase Receipt",
		};

		// formatter for material request item
		frm.set_indicator_formatter("item_code", function (doc) {
			return doc.stock_qty <= doc.ordered_qty ? "green" : "orange";
		});

		frm.set_query("item_code", "items", function () {
			return {
				query: "erpnext.controllers.queries.item_query",
			};
		});

		frm.set_query("from_warehouse", "items", function (doc) {
			return {
				filters: {
					company: doc.company,
					is_group: 0,
				},
			};
		});

		frm.set_query("bom_no", "items", function (doc, cdt, cdn) {
			var row = locals[cdt][cdn];
			return {
				filters: {
					item: row.item_code,
				},
			};
		});

		frm.set_query("buying_price_list", () => {
			return {
				filters: {
					buying: 1,
					enabled: 1,
				},
			};
		});
	},

	schedule_date(frm) {
		if (frm.doc.schedule_date) {
			frm.doc.items.forEach((d) => {
				frappe.model.set_value(d.doctype, d.name, "schedule_date", frm.doc.schedule_date);
			});
		}
	},

	onload: function (frm) {
		// add item, if previous view was item
		erpnext.utils.add_item(frm);

		// set schedule_date
		set_schedule_date(frm);

		frm.set_query("warehouse", "items", function (doc) {
			return {
				filters: {
					company: doc.company,
					is_group: 0,
				},
			};
		});

		frm.set_query("set_warehouse", function (doc) {
			return {
				filters: {
					company: doc.company,
					is_group: 0,
				},
			};
		});

		frm.set_query("set_from_warehouse", function (doc) {
			return {
				filters: {
					company: doc.company,
					is_group: 0,
				},
			};
		});

		erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype);
		if (!frm.doc.buying_price_list) {
			const buying_price_list = frappe.defaults.get_default("buying_price_list");
			if (frappe.has_permission("Price List", "read", buying_price_list)) {
				frm.set_value("buying_price_list", buying_price_list);
			}
		}
	},

	company: function (frm) {
		erpnext.accounts.dimensions.update_dimension(frm, frm.doctype);
		erpnext.utils.set_letter_head(frm);
	},

	onload_post_render: function (frm) {
		frm.get_field("items").grid.set_multiple_add("item_code", "qty");
	},

	refresh: function (frm) {
		frm.events.make_custom_buttons(frm);
		frm.toggle_reqd("customer", frm.doc.material_request_type == "Customer Provided");
		erpnext.buying.prevent_past_schedule_dates(frm);
		frm.trigger("set_warehouse_label");
	},

	transaction_date(frm) {
		erpnext.buying.prevent_past_schedule_dates(frm);
		frm.set_value("schedule_date", "");
	},

	set_from_warehouse: function (frm) {
		if (frm.doc.material_request_type == "Material Transfer" && frm.doc.set_from_warehouse) {
			frm.doc.items.forEach((d) => {
				frappe.model.set_value(d.doctype, d.name, "from_warehouse", frm.doc.set_from_warehouse);
			});
		}
	},

	make_custom_buttons: function (frm) {
		if (frm.doc.docstatus == 0) {
			frm.add_custom_button(
				__("Bill of Materials"),
				() => frm.events.get_items_from_bom(frm),
				__("Get Items From")
			);
		}

		if (frm.doc.docstatus == 1 && frm.doc.status != "Stopped") {
			let precision = frappe.defaults.get_default("float_precision");

			if (flt(frm.doc.per_received, precision) < 100) {
				frm.add_custom_button(__("Stop"), () => frm.events.update_status(frm, "Stopped"));
			}

			if (flt(frm.doc.per_ordered, precision) < 100) {
				let add_create_pick_list_button = () => {
					if (frm.doc.items.some((item) => item.stock_qty - item.picked_qty > 0)) {
						frm.add_custom_button(
							__("Pick List"),
							() => frm.events.create_pick_list(frm),
							__("Create")
						);
					}
				};

				if (frm.doc.material_request_type === "Material Transfer") {
					add_create_pick_list_button();
					frm.add_custom_button(
						__("Material Transfer"),
						() => frm.events.make_stock_entry(frm),
						__("Create")
					);

					frm.add_custom_button(
						__("Material Transfer (In Transit)"),
						() => frm.events.make_in_transit_stock_entry(frm),
						__("Create")
					);
				}

				if (frm.doc.material_request_type === "Material Issue") {
					frm.add_custom_button(
						__("Issue Material"),
						() => frm.events.make_stock_entry(frm),
						__("Create")
					);
				}

				if (frm.doc.material_request_type === "Customer Provided") {
					frm.add_custom_button(
						__("Material Receipt"),
						() => frm.events.make_stock_entry(frm),
						__("Create")
					);
				}

				if (frm.doc.material_request_type === "Purchase") {
					frm.add_custom_button(
						__("Purchase Order"),
						() => frm.events.make_purchase_order(frm),
						__("Create")
					);

					frm.add_custom_button(
						__("Request for Quotation"),
						() => frm.events.make_request_for_quotation(frm),
						__("Create")
					);

					frm.add_custom_button(
						__("Supplier Quotation"),
						() => frm.events.make_supplier_quotation(frm),
						__("Create")
					);
				}

				if (frm.doc.material_request_type === "Manufacture") {
					frm.add_custom_button(
						__("Work Order"),
						() => frm.events.raise_work_orders(frm),
						__("Create")
					);
				}

				if (frm.doc.material_request_type === "Subcontracting") {
					frm.add_custom_button(
						__("Subcontracted Purchase Order"),
						() => frm.events.make_purchase_order(frm),
						__("Create")
					);
				}

				frm.page.set_inner_btn_group_as_primary(__("Create"));
			}
		}

		if (frm.doc.docstatus === 0) {
			frm.add_custom_button(
				__("Sales Order"),
				() => frm.events.get_items_from_sales_order(frm),
				__("Get Items From")
			);
		}

		if (frm.doc.docstatus == 1 && frm.doc.status == "Stopped") {
			frm.add_custom_button(__("Re-open"), () => frm.events.update_status(frm, "Submitted"));
		}
	},

	update_status: function (frm, stop_status) {
		frappe.call({
			method: "erpnext.stock.doctype.material_request.material_request.update_status",
			args: { name: frm.doc.name, status: stop_status },
			callback(r) {
				if (!r.exc) {
					frm.reload_doc();
				}
			},
		});
	},

	get_items_from_sales_order: function (frm) {
		erpnext.utils.map_current_doc({
			method: "erpnext.selling.doctype.sales_order.sales_order.make_material_request",
			source_doctype: "Sales Order",
			target: frm,
			setters: {
				customer: frm.doc.customer || undefined,
				delivery_date: undefined,
			},
			get_query_filters: {
				docstatus: 1,
				status: ["not in", ["Closed", "On Hold"]],
				per_delivered: ["<", 99.99],
				company: frm.doc.company,
			},
		});
	},

	get_item_data: function (frm, item, overwrite_warehouse = false) {
		if (item && !item.item_code) {
			return;
		}

		frappe.call({
			method: "erpnext.stock.get_item_details.get_item_details",
			args: {
				ctx: {
					item_code: item.item_code,
					from_warehouse: item.from_warehouse,
					warehouse: item.warehouse,
					doctype: frm.doc.doctype,
					buying_price_list: frm.doc.buying_price_list,
					currency: frappe.defaults.get_default("Currency"),
					name: frm.doc.name,
					qty: item.qty || 1,
					stock_qty: item.stock_qty,
					company: frm.doc.company,
					conversion_rate: 1,
					material_request_type: frm.doc.material_request_type,
					plc_conversion_rate: 1,
					rate: item.rate,
					uom: item.uom,
					conversion_factor: item.conversion_factor,
					project: item.project,
				},
				overwrite_warehouse: overwrite_warehouse,
			},
			callback: function (r) {
				const d = item;
				let allow_to_change_fields = [
					"actual_qty",
					"projected_qty",
					"min_order_qty",
					"item_name",
					"stock_uom",
					"uom",
					"conversion_factor",
					"stock_qty",
				];

				if (overwrite_warehouse) {
					allow_to_change_fields.push("description");
				}

				if (!r.exc) {
					$.each(r.message, function (key, value) {
						if (!d[key] || allow_to_change_fields.includes(key)) {
							d[key] = value;
						}
					});

					if (d.price_list_rate != r.message.price_list_rate) {
						d.rate = 0.0;
						d.price_list_rate = r.message.price_list_rate;
						frappe.model.set_value(d.doctype, d.name, "rate", d.price_list_rate);
					}

					refresh_field("items");
				}
			},
		});
	},

	get_items_from_bom: function (frm) {
		var d = new frappe.ui.Dialog({
			title: __("Get Items from BOM"),
			fields: [
				{
					fieldname: "bom",
					fieldtype: "Link",
					label: __("BOM"),
					options: "BOM",
					reqd: 1,
					get_query: function () {
						return { filters: { docstatus: 1, is_active: 1 } };
					},
				},
				{
					fieldname: "warehouse",
					fieldtype: "Link",
					label: __("For Warehouse"),
					options: "Warehouse",
					reqd: 1,
					get_query: function () {
						return { filters: { company: frm.doc.company } };
					},
				},
				{ fieldname: "qty", fieldtype: "Float", label: __("Quantity"), reqd: 1, default: 1 },
				{
					fieldname: "fetch_exploded",
					fieldtype: "Check",
					label: __("Fetch exploded BOM (including sub-assemblies)"),
					default: 1,
				},
			],
			primary_action_label: __("Get Items"),
			primary_action(values) {
				if (!values) return;
				values["company"] = frm.doc.company;
				if (!frm.doc.company) frappe.throw(__("Company field is required"));
				frappe.call({
					method: "erpnext.manufacturing.doctype.bom.bom.get_bom_items",
					args: values,
					callback: function (r) {
						if (!r.message) {
							frappe.throw(__("BOM does not contain any stock item"));
						} else {
							erpnext.utils.remove_empty_first_row(frm, "items");
							$.each(r.message, function (i, item) {
								var d = frappe.model.add_child(cur_frm.doc, "Material Request Item", "items");
								d.item_code = item.item_code;
								d.item_name = item.item_name;
								d.description = item.description;
								d.warehouse = values.warehouse;
								d.uom = item.stock_uom;
								d.stock_uom = item.stock_uom;
								d.conversion_factor = 1;
								d.qty = item.qty;
								d.project = item.project;
							});
						}
						d.hide();
						refresh_field("items");
					},
				});
			},
		});

		d.show();
	},

	make_purchase_order: function (frm) {
		frappe.call({
			method: "erpnext.stock.doctype.material_request.material_request.get_item_default_suppliers",
			args: {
				source_name: frm.doc.name,
				filtered_children: (frm.get_selected() || {}).items || [],
			},
			freeze: true,
			callback: function (r) {
				const items = r.message || [];
				const suppliers = new Set(items.map((item) => item.supplier || ""));

				if (suppliers.size > 1) {
					frm.events.select_suppliers_for_items(frm, items);
					return;
				}

				frappe.model.open_mapped_doc({
					method: "erpnext.stock.doctype.material_request.material_request.make_purchase_order",
					frm: frm,
					args: { supplier: items.length ? items[0].supplier : null },
					run_link_triggers: true,
				});
			},
		});
	},

	select_suppliers_for_items: function (frm, items) {
		const rows = items.map((item) => Object.assign({}, item, { qty: item.pending_qty, __checked: 1 }));

		const supplier_query = () => {
			return { filters: { disabled: 0, prevent_pos: 0 } };
		};

		const dialog = new frappe.ui.Dialog({
			title: __("Select Supplier for Items"),
			size: "large",
			fields: [
				{
					fieldname: "supplier",
					fieldtype: "Link",
					options: "Supplier",
					label: __("Set Supplier for All Items"),
					get_query: supplier_query,
					onchange: function () {
						const supplier = dialog.get_value("supplier");
						if (!supplier) return;

						rows.forEach((row) => (row.supplier = supplier));
						dialog.fields_dict.items.grid.refresh();
					},
				},
				{ fieldtype: "Column Break" },
				{ fieldtype: "Section Break" },
				{
					fieldname: "items",
					fieldtype: "Table",
					label: __("Items"),
					cannot_add_rows: true,
					cannot_delete_rows: true,
					in_place_edit: true,
					data: rows,
					get_data: () => rows,
					description: __("A separate Purchase Order is created for each Supplier."),
					fields: [
						{
							fieldtype: "Data",
							fieldname: "material_request_item",
							hidden: 1,
						},
						{
							fieldtype: "Link",
							fieldname: "item_code",
							options: "Item",
							label: __("Item Code"),
							read_only: 1,
							in_list_view: 1,
							columns: 3,
						},
						{
							fieldtype: "Data",
							fieldname: "item_name",
							label: __("Item Name"),
							read_only: 1,
							in_list_view: 1,
							columns: 2,
						},
						{
							fieldtype: "Float",
							fieldname: "pending_qty",
							hidden: 1,
						},
						{
							fieldtype: "Float",
							fieldname: "qty",
							label: __("Quantity"),
							reqd: 1,
							in_list_view: 1,
							columns: 1,
						},
						{
							fieldtype: "Link",
							fieldname: "uom",
							options: "UOM",
							label: __("UOM"),
							read_only: 1,
							in_list_view: 1,
							columns: 1,
						},
						{
							fieldtype: "Link",
							fieldname: "supplier",
							options: "Supplier",
							label: __("Supplier"),
							get_query: supplier_query,
							reqd: 1,
							in_list_view: 1,
							columns: 3,
						},
					],
				},
			],
			primary_action_label: __("Create"),
			primary_action: function (values) {
				const item_suppliers = (values.items || []).filter((row) => row.__checked);
				if (!item_suppliers.length) {
					frappe.throw(__("Select at least one Item"));
				}

				const item_link = (row) =>
					frappe.utils.get_form_link(
						"Item",
						row.item_code,
						true,
						frappe.utils.escape_html(row.item_code)
					);

				const missing_supplier = item_suppliers.find((row) => !row.supplier);
				if (missing_supplier) {
					frappe.throw(__("Select a Supplier for Item {0}", [item_link(missing_supplier)]));
				}

				const invalid_qty = item_suppliers.find(
					(row) => flt(row.qty) <= 0 || flt(row.qty) > flt(row.pending_qty)
				);
				if (invalid_qty) {
					const pending_qty = `${format_number(invalid_qty.pending_qty)} ${frappe.utils.escape_html(
						invalid_qty.uom
					)}`;
					frappe.throw(
						__("Quantity for Item {0} must be greater than zero and cannot exceed {1}", [
							item_link(invalid_qty),
							`<b>${pending_qty}</b>`,
						])
					);
				}

				frappe.call({
					method: "erpnext.stock.doctype.material_request.material_request.make_purchase_orders_by_supplier",
					args: { source_name: frm.doc.name, item_suppliers: item_suppliers },
					freeze: true,
					callback: function (r) {
						if (r.exc) return;

						dialog.hide();

						const purchase_orders = r.message || [];
						if (purchase_orders.length === 1) {
							frappe.set_route("Form", "Purchase Order", purchase_orders[0]);
						}
					},
				});
			},
		});

		dialog.show();
	},

	make_request_for_quotation: function (frm) {
		frappe.model.open_mapped_doc({
			method: "erpnext.stock.doctype.material_request.material_request.make_request_for_quotation",
			frm: frm,
			run_link_triggers: true,
		});
	},

	make_supplier_quotation: function (frm) {
		frappe.model.open_mapped_doc({
			method: "erpnext.stock.doctype.material_request.material_request.make_supplier_quotation",
			frm: frm,
		});
	},

	make_stock_entry: function (frm) {
		frappe.model.open_mapped_doc({
			method: "erpnext.stock.doctype.material_request.material_request.make_stock_entry",
			frm: frm,
		});
	},

	make_in_transit_stock_entry(frm) {
		frappe.prompt(
			[
				{
					label: __("In Transit Warehouse"),
					fieldname: "in_transit_warehouse",
					fieldtype: "Link",
					options: "Warehouse",
					reqd: 1,
					get_query: () => {
						return {
							filters: {
								company: frm.doc.company,
								is_group: 0,
								warehouse_type: "Transit",
							},
						};
					},
				},
			],
			(values) => {
				frappe.call({
					method: "erpnext.stock.doctype.material_request.material_request.make_in_transit_stock_entry",
					args: {
						source_name: frm.doc.name,
						in_transit_warehouse: values.in_transit_warehouse,
					},
					callback: function (r) {
						if (r.message) {
							let doc = frappe.model.sync(r.message);
							frappe.set_route("Form", doc[0].doctype, doc[0].name);
						}
					},
				});
			},
			__("In Transit Transfer"),
			__("Create Stock Entry")
		);
	},

	create_pick_list: (frm) => {
		frappe.model.open_mapped_doc({
			method: "erpnext.stock.doctype.material_request.material_request.create_pick_list",
			frm: frm,
		});
	},

	raise_work_orders: function (frm) {
		frappe.call({
			method: "erpnext.stock.doctype.material_request.material_request.raise_work_orders",
			args: {
				material_request: frm.doc.name,
				company: frm.doc.company,
			},
			freeze: true,
			callback: function (r) {
				if (r.message.length) {
					frm.reload_doc();
				}
			},
		});
	},
	material_request_type: function (frm) {
		frm.toggle_reqd("customer", frm.doc.material_request_type == "Customer Provided");

		if (frm.doc.material_request_type !== "Material Transfer" && frm.doc.set_from_warehouse) {
			frm.set_value("set_from_warehouse", "");
		}

		frm.trigger("set_warehouse_label");
	},

	set_warehouse_label(frm) {
		let warehouse_label =
			frm.doc.material_request_type === "Material Transfer" ? "Target Warehouse" : "Warehouse";
		if (frm.doc.material_request_type === "Material Issue") {
			warehouse_label = "From Warehouse";
		}

		frm.fields_dict["items"].grid.update_docfield_property("warehouse", "label", __(warehouse_label));

		warehouse_label = "Set " + warehouse_label;
		frm.set_df_property("set_warehouse", "label", __(warehouse_label));

		refresh_field("items");
	},
});

frappe.ui.form.on("Material Request Item", {
	qty: function (frm, doctype, name) {
		const item = locals[doctype][name];
		if (flt(item.qty) < flt(item.min_order_qty)) {
			frappe.msgprint(__("Warning: Material Requested Qty is less than Minimum Order Qty"));
		}
		frm.events.get_item_data(frm, item, false);
	},

	from_warehouse: function (frm, doctype, name) {
		const item = locals[doctype][name];
		frm.events.get_item_data(frm, item, false);
	},

	warehouse: function (frm, doctype, name) {
		const item = locals[doctype][name];
		frm.events.get_item_data(frm, item, false);
	},

	rate(frm, doctype, name) {
		const item = locals[doctype][name];
		item.amount = flt(item.qty) * flt(item.rate);
		frappe.model.set_value(doctype, name, "amount", item.amount);
		refresh_field("amount", item.name, item.parentfield);
	},

	item_code: function (frm, doctype, name) {
		const item = locals[doctype][name];
		item.rate = 0;
		item.uom = "";
		set_schedule_date(frm);
		frm.events.get_item_data(frm, item, true);
	},

	schedule_date: function (frm, cdt, cdn) {
		var row = locals[cdt][cdn];
		if (row.schedule_date) {
			if (!frm.doc.schedule_date) {
				erpnext.utils.copy_value_in_all_rows(frm.doc, cdt, cdn, "items", "schedule_date");
			} else {
				set_schedule_date(frm);
			}
		}
	},

	conversion_factor: function (frm, doctype, name) {
		const item = locals[doctype][name];
		frm.events.get_item_data(frm, item, false);
	},
});

erpnext.buying.MaterialRequestController = class MaterialRequestController extends (
	erpnext.buying.BuyingController
) {
	tc_name() {
		this.get_terms();
	}

	item_code() {
		// to override item code trigger from transaction.js
	}

	validate_company_and_party() {
		return true;
	}

	calculate_taxes_and_totals() {
		return;
	}

	validate() {
		set_schedule_date(this.frm);
	}

	onload() {
		this.frm.set_query("item_code", "items", function (doc, cdt, cdn) {
			let filters = { is_stock_item: 1 };

			if (doc.material_request_type == "Customer Provided") {
				filters.customer = doc.customer;
			} else if (
				doc.material_request_type == "Purchase" ||
				doc.material_request_type == "Subcontracting"
			) {
				filters = { is_purchase_item: 1 };
			} else if (doc.material_request_type == "Manufacture") {
				filters.include_item_in_manufacturing = 1;
			}

			return {
				query: "erpnext.controllers.queries.item_query",
				filters: filters,
			};
		});
	}

	items_add(doc, cdt, cdn) {
		var row = frappe.get_doc(cdt, cdn);
		if (doc.schedule_date) {
			row.schedule_date = doc.schedule_date;
			refresh_field("schedule_date", cdn, "items");
		} else {
			this.frm.script_manager.copy_from_first_row("items", row, ["schedule_date"]);
		}
	}

	items_on_form_rendered() {
		set_schedule_date(this.frm);
	}

	qty(doc, cdt, cdn) {
		var row = frappe.get_doc(cdt, cdn);
		row.amount = flt(row.qty) * flt(row.rate);
		frappe.model.set_value(cdt, cdn, "amount", row.amount);
		refresh_field("amount", row.name, row.parentfield);
	}
};

// for backward compatibility: combine new and previous states
extend_cscript(cur_frm.cscript, new erpnext.buying.MaterialRequestController({ frm: cur_frm }));

function set_schedule_date(frm) {
	if (frm.doc.schedule_date) {
		erpnext.utils.copy_value_in_all_rows(
			frm.doc,
			frm.doc.doctype,
			frm.doc.name,
			"items",
			"schedule_date"
		);
	}
}
