feat: pricing rule enhancements
diff --git a/erpnext/public/js/controllers/buying.js b/erpnext/public/js/controllers/buying.js
index a2a431d..3ec27fc 100644
--- a/erpnext/public/js/controllers/buying.js
+++ b/erpnext/public/js/controllers/buying.js
@@ -130,13 +130,23 @@
 		if (doc.doctype == "Purchase Order" && item.blanket_order_rate) {
 			item_rate = item.blanket_order_rate;
 		}
-		item.discount_amount = flt(item_rate) * flt(item.discount_percentage) / 100;
+
+		if (item.discount_percentage) {
+			item.discount_amount = flt(item_rate) * flt(item.discount_percentage) / 100;
+		}
+
 		item.rate = flt((item.price_list_rate) - (item.discount_amount), precision('rate', item));
 
 		this.calculate_taxes_and_totals();
 	},
 
 	discount_percentage: function(doc, cdt, cdn) {
+		var item = frappe.get_doc(cdt, cdn);
+		item.discount_amount = 0.0;
+		this.price_list_rate(doc, cdt, cdn);
+	},
+
+	discount_amount: function(doc, cdt, cdn) {
 		this.price_list_rate(doc, cdt, cdn);
 	},
 
diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js
index d09c15a..b3c8538 100644
--- a/erpnext/public/js/controllers/taxes_and_totals.js
+++ b/erpnext/public/js/controllers/taxes_and_totals.js
@@ -20,6 +20,9 @@
 
 		if(item.discount_percentage){
 			item.discount_amount = flt(item.rate_with_margin) * flt(item.discount_percentage) / 100;
+		}
+
+		if (item.discount_amount) {
 			item.rate = flt((item.rate_with_margin) - (item.discount_amount), precision('rate', item));
 		}
 	},
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index 1430383..f647913 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -455,7 +455,8 @@
 							pos_profile: me.frm.doc.doctype == 'Sales Invoice' ? me.frm.doc.pos_profile : '',
 							cost_center: item.cost_center,
 							tax_category: me.frm.doc.tax_category,
-							item_tax_template: item.item_tax_template
+							item_tax_template: item.item_tax_template,
+							child_docname: item.name
 						}
 					},
 
@@ -480,7 +481,8 @@
 										});
 									}
 								},
-								() => me.conversion_factor(doc, cdt, cdn, true)
+								() => me.conversion_factor(doc, cdt, cdn, true),
+								() => me.update_free_items(item)
 							]);
 						}
 					}
@@ -904,8 +906,9 @@
 	},
 
 	qty: function(doc, cdt, cdn) {
+		let item = frappe.get_doc(cdt, cdn);
 		this.conversion_factor(doc, cdt, cdn, true);
-		this.apply_pricing_rule(frappe.get_doc(cdt, cdn), true);
+		this.apply_pricing_rule(item, true);
 	},
 
 	service_stop_date: function(frm, cdt, cdn) {
@@ -1096,11 +1099,14 @@
 			var item_list = [];
 
 			$.each(this.frm.doc["items"] || [], function(i, d) {
-				if (d.item_code) {
+				if (d.item_code && !d.is_free_item) {
 					item_list.push({
 						"doctype": d.doctype,
 						"name": d.name,
-						"pricing_rule": d.pricing_rule
+						"item_code": d.item_code,
+						"pricing_rules": d.pricing_rules,
+						"parenttype": d.parenttype,
+						"parent": d.parent
 					})
 				}
 			});
@@ -1109,6 +1115,9 @@
 				args: { item_list: item_list },
 				callback: function(r) {
 					if (!r.exc && r.message) {
+						r.message.forEach(row_item => {
+							me.update_free_items(row_item);
+						});
 						me._set_values_for_item_list(r.message);
 						me.calculate_taxes_and_totals();
 						if(me.frm.doc.apply_discount_on) me.frm.trigger("apply_discount_on");
@@ -1127,6 +1136,7 @@
 			if(calculate_taxes_and_totals) me.calculate_taxes_and_totals();
 			return;
 		}
+
 		return this.frm.call({
 			method: "erpnext.accounts.doctype.pricing_rule.pricing_rule.apply_pricing_rule",
 			args: {	args: args },
@@ -1136,6 +1146,7 @@
 					if(item) me.set_gross_profit(item);
 					if(calculate_taxes_and_totals) me.calculate_taxes_and_totals();
 					if(me.frm.doc.apply_discount_on) me.frm.trigger("apply_discount_on")
+					me.update_free_items(item);
 				}
 			}
 		});
@@ -1176,16 +1187,19 @@
 				item_list.push({
 					"doctype": d.doctype,
 					"name": d.name,
+					"child_docname": d.name,
 					"item_code": d.item_code,
 					"item_group": d.item_group,
 					"brand": d.brand,
 					"qty": d.qty,
+					"stock_qty": d.stock_qty,
 					"uom": d.uom,
 					"parenttype": d.parenttype,
 					"parent": d.parent,
-					"pricing_rule": d.pricing_rule,
+					"pricing_rules": d.pricing_rules,
 					"warehouse": d.warehouse,
 					"serial_no": d.serial_no,
+					"price_list_rate": d.price_list_rate,
 					"discount_percentage": d.discount_percentage || 0.0,
 					"conversion_factor": d.conversion_factor || 1.0
 				});
@@ -1213,7 +1227,7 @@
 		var price_list_rate_changed = false;
 		for(var i=0, l=children.length; i<l; i++) {
 			var d = children[i];
-			var existing_pricing_rule = frappe.model.get_value(d.doctype, d.name, "pricing_rule");
+			var existing_pricing_rule = frappe.model.get_value(d.doctype, d.name, "pricing_rules");
 			for(var k in d) {
 				var v = d[k];
 				if (["doctype", "name"].indexOf(k)===-1) {
@@ -1225,7 +1239,7 @@
 			}
 
 			// if pricing rule set as blank from an existing value, apply price_list
-			if(!me.frm.doc.ignore_pricing_rule && existing_pricing_rule && !d.pricing_rule) {
+			if(!me.frm.doc.ignore_pricing_rule && existing_pricing_rule && !d.pricing_rules) {
 				me.apply_price_list(frappe.get_doc(d.doctype, d.name));
 			}
 		}
@@ -1274,6 +1288,58 @@
 		});
 	},
 
+	update_free_items: function(item) {
+		var me = this;
+
+		if (item.pricing_rules) {
+			frappe.call({
+				method: "erpnext.accounts.doctype.pricing_rule.pricing_rule.get_free_items",
+				args: {
+					pricing_rules: item.pricing_rules,
+					item_row: item
+				},
+				callback: function(r) {
+					let items = [];
+					let child = '';
+
+					me.frm.doc.items.map(d => {
+						items[d.item_code] = d;
+					});
+
+					if(r.message && r.message.length) {
+						r.message.forEach(d => {
+							// If free item is already exists
+
+							if(d.item_code in items &&
+								d.is_free_item && items[d.item_code].is_free_item) {
+								child = items[d.item_code];
+							} else {
+								child = frappe.model.add_child(me.frm.doc, item.doctype, "items");
+							}
+
+							$.each(d, function(k, v) {
+								child[k] = v;
+							});
+
+							me.frm.script_manager.trigger("price_list_rate", child.doctype, child.name);
+						});
+					}
+				}
+			});
+		} else if(item.remove_free_item) {
+			var items = [];
+
+			me.frm.doc.items.forEach(d => {
+				if(d.item_code != item.remove_free_item || !d.is_free_item) {
+					items.push(d);
+				}
+			});
+
+			me.frm.doc.items = items;
+			refresh_field('items');
+		}
+	},
+
 	validate_company_and_party: function() {
 		var me = this;
 		var valid = true;