fix(pricing rule): free item duplication (#33746)

* fix(pricing rule): free item duplication

* chore: linting issues
diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py
index 1ce780e..57feaa0 100644
--- a/erpnext/accounts/doctype/pricing_rule/utils.py
+++ b/erpnext/accounts/doctype/pricing_rule/utils.py
@@ -687,11 +687,21 @@
 
 def apply_pricing_rule_for_free_items(doc, pricing_rule_args):
 	if pricing_rule_args:
-		items = tuple((d.item_code, d.pricing_rules) for d in doc.items if d.is_free_item)
+		args = {(d["item_code"], d["pricing_rules"]): d for d in pricing_rule_args}
 
-		for args in pricing_rule_args:
-			if not items or (args.get("item_code"), args.get("pricing_rules")) not in items:
-				doc.append("items", args)
+		for item in doc.items:
+			if not item.is_free_item:
+				continue
+
+			free_item_data = args.get((item.item_code, item.pricing_rules))
+			if free_item_data:
+				free_item_data.pop("item_name")
+				free_item_data.pop("description")
+				item.update(free_item_data)
+				args.pop((item.item_code, item.pricing_rules))
+
+		for free_item in args.values():
+			doc.append("items", free_item)
 
 
 def get_pricing_rule_items(pr_doc, other_items=False) -> list:
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index 5c1c6d1..3a778fa 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -1473,6 +1473,7 @@
 					"parenttype": d.parenttype,
 					"parent": d.parent,
 					"pricing_rules": d.pricing_rules,
+					"is_free_item": d.is_free_item,
 					"warehouse": d.warehouse,
 					"serial_no": d.serial_no,
 					"batch_no": d.batch_no,