From bdeb078f29140070ea093fbd98ac0c30a4bfa771 Mon Sep 17 00:00:00 2001 From: SUBHANKAR DHAR Date: Fri, 12 Jun 2026 16:14:16 +0530 Subject: [PATCH] fix: skip custom project fields that already exist natively in v16 ERPNext v16 ships project fields natively on Purchase Invoice, Purchase Order, Stock Entry, and Delivery Note. Adding duplicate custom fields caused UniqueFieldnameError. Now only two custom fields are added: - Purchase Invoice.is_urd_purchase (new URD toggle) - Journal Entry.project (only doctype missing it) Also update profitability report queries to use native 'project' field instead of 'furnitex_project' on PI, SE, and JE. Co-Authored-By: Claude Sonnet 4.6 --- setup_furnitex.py | 50 +++++++++++++++++++++++------------------------ 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/setup_furnitex.py b/setup_furnitex.py index b535d855..86547a88 100644 --- a/setup_furnitex.py +++ b/setup_furnitex.py @@ -473,46 +473,46 @@ def create_suppliers(): def create_custom_fields(): print("\n[9/9] Creating Custom Fields...") + # NOTE: Purchase Invoice, Purchase Order, Stock Entry, and Delivery Note + # already have a native 'project' field in ERPNext v16 — skip those. + # We only add: + # 1. is_urd_purchase (Check) on Purchase Invoice + # 2. project (Link) on Journal Entry (only one missing it) + # (dt, fieldname, label, fieldtype, options, insert_after, in_list_view) fields = [ - ("Purchase Invoice", "is_urd_purchase", "URD Purchase (No GST)", - "Check", None, "supplier", 1), - ("Purchase Invoice", "furnitex_project", "Project", - "Link", "Project", "is_urd_purchase", 1), - ("Purchase Order", "furnitex_project", "Project", - "Link", "Project", "supplier", 1), - ("Stock Entry", "furnitex_project", "Project", - "Link", "Project", "purpose", 1), - ("Delivery Note", "furnitex_project", "Project", - "Link", "Project", "customer", 0), - ("Journal Entry", "furnitex_project", "Project", - "Link", "Project", "voucher_type", 0), + ("Purchase Invoice", "is_urd_purchase", "URD Purchase (No GST)", + "Check", None, "supplier", 1), + ("Journal Entry", "project", "Project", + "Link", "Project", "voucher_type", 0), ] for dt, fn, label, ft, opts, after, in_list in fields: cf_name = f"{dt}-{fn}" if not exists("Custom Field", cf_name): d_dict = { - "doctype": "Custom Field", - "dt": dt, - "fieldname": fn, - "label": label, - "fieldtype": ft, - "insert_after": after, - "in_list_view": in_list, + "doctype": "Custom Field", + "dt": dt, + "fieldname": fn, + "label": label, + "fieldtype": ft, + "insert_after": after, + "in_list_view": in_list, "in_standard_filter": 1, - "search_index": 1, + "search_index": 1, } if opts: d_dict["options"] = opts d = frappe.get_doc(d_dict) - d.flags.ignore_permissions = True - d.insert() - ok(f"Custom Field: {dt}.{fn}") + if safe_insert(d): + ok(f"Custom Field: {dt}.{fn}") + else: + skip(f"Custom Field: {dt}.{fn} (duplicate)") else: skip(f"Custom Field: {dt}.{fn}") frappe.db.commit() + ok("Native 'project' field already present on PI, PO, Stock Entry, DN — no custom fields needed there") # ───────────────────────────────────────────────────────────── @@ -668,14 +668,14 @@ def get_data(filters): pname, as_dict=1)[0].v or 0) raw_mat = (frappe.db.sql( - "SELECT COALESCE(SUM(base_net_total),0) v FROM `tabPurchase Invoice` WHERE furnitex_project=%s AND docstatus=1", + "SELECT COALESCE(SUM(base_net_total),0) v FROM `tabPurchase Invoice` WHERE project=%s AND docstatus=1", pname, as_dict=1)[0].v or 0) consumed = (frappe.db.sql( """SELECT COALESCE(SUM(sed.amount),0) v FROM `tabStock Entry Detail` sed JOIN `tabStock Entry` se ON se.name=sed.parent - WHERE se.furnitex_project=%s AND se.stock_entry_type='Material Issue' AND se.docstatus=1""", + WHERE se.project=%s AND se.stock_entry_type='Material Issue' AND se.docstatus=1""", pname, as_dict=1)[0].v or 0) labour = (frappe.db.sql(