mirror of
https://github.com/frappe/frappe_docker.git
synced 2026-06-17 13:55:08 +00:00
fix: resolve all ERPNext v16 naming convention issues
- All ERPNext doctypes append company abbr to names (Warehouse, Tax
Template, etc.) — switch existence checks to use filter-based lookup
instead of hardcoded "{name} - Furnitex" strings
- Add safe_insert() helper to absorb DuplicateEntryError gracefully so
re-runs never crash mid-way
- Service items: remove item_defaults entirely (non-stock items have no
warehouse, avoids cross-company warehouse validation error)
- Raw material items: only add item_defaults when a valid Furnitex
warehouse is resolved
- Supplier URD tax default: look up template by title+company filter
rather than bare name string
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
ff152a3020
commit
356fec8c38
1 changed files with 103 additions and 66 deletions
|
|
@ -28,6 +28,23 @@ def get_abbr():
|
|||
def exists(doctype, name):
|
||||
return frappe.db.exists(doctype, name)
|
||||
|
||||
def exists_filter(doctype, filters):
|
||||
"""Existence check by filters (for docs where name includes company abbr)."""
|
||||
return frappe.db.get_value(doctype, filters, "name")
|
||||
|
||||
def safe_insert(doc):
|
||||
"""Insert and return True, or skip silently on duplicate and return False."""
|
||||
try:
|
||||
doc.flags.ignore_permissions = True
|
||||
doc.insert()
|
||||
return True
|
||||
except frappe.DuplicateEntryError:
|
||||
return False
|
||||
except Exception as e:
|
||||
if "Duplicate entry" in str(e):
|
||||
return False
|
||||
raise
|
||||
|
||||
def ok(msg):
|
||||
print(f" [OK] {msg}")
|
||||
|
||||
|
|
@ -59,10 +76,11 @@ def create_uoms():
|
|||
"uom_name": uom_name,
|
||||
"must_be_whole_number": whole,
|
||||
})
|
||||
d.flags.ignore_permissions = True
|
||||
d.flags.ignore_mandatory = True
|
||||
d.insert()
|
||||
if safe_insert(d):
|
||||
ok(f"UOM: {uom_name}")
|
||||
else:
|
||||
skip(f"UOM: {uom_name} (duplicate)")
|
||||
else:
|
||||
skip(f"UOM: {uom_name}")
|
||||
frappe.db.commit()
|
||||
|
|
@ -91,9 +109,10 @@ def create_item_groups():
|
|||
"parent_item_group": parent,
|
||||
"is_group": 0,
|
||||
})
|
||||
d.flags.ignore_permissions = True
|
||||
d.insert()
|
||||
if safe_insert(d):
|
||||
ok(f"Item Group: {name}")
|
||||
else:
|
||||
skip(f"Item Group: {name} (duplicate)")
|
||||
else:
|
||||
skip(f"Item Group: {name}")
|
||||
frappe.db.commit()
|
||||
|
|
@ -117,9 +136,10 @@ def create_supplier_groups():
|
|||
"supplier_group_name": name,
|
||||
"parent_supplier_group": parent,
|
||||
})
|
||||
d.flags.ignore_permissions = True
|
||||
d.insert()
|
||||
if safe_insert(d):
|
||||
ok(f"Supplier Group: {name}")
|
||||
else:
|
||||
skip(f"Supplier Group: {name} (duplicate)")
|
||||
else:
|
||||
skip(f"Supplier Group: {name}")
|
||||
frappe.db.commit()
|
||||
|
|
@ -157,9 +177,10 @@ def create_warehouses():
|
|||
"company": COMPANY,
|
||||
"is_group": is_group,
|
||||
})
|
||||
d.flags.ignore_permissions = True
|
||||
d.insert()
|
||||
if safe_insert(d):
|
||||
ok(f"Warehouse: {w_full}")
|
||||
else:
|
||||
skip(f"Warehouse: {w_full} (duplicate)")
|
||||
else:
|
||||
skip(f"Warehouse: {w_full}")
|
||||
frappe.db.commit()
|
||||
|
|
@ -197,68 +218,81 @@ def _find_account(account_name_fragment, root_type=None, account_type=None):
|
|||
|
||||
def create_tax_templates():
|
||||
print("\n[5/9] Creating Tax Templates...")
|
||||
# ERPNext stores tax templates as "{title} - {abbr}", so we check by title+company
|
||||
|
||||
# ── No GST (URD Purchase) ──
|
||||
urd = "No GST - URD Purchase"
|
||||
if not exists("Purchase Taxes and Charges Template", urd):
|
||||
urd_title = "No GST - URD Purchase"
|
||||
if not exists_filter("Purchase Taxes and Charges Template",
|
||||
{"title": urd_title, "company": COMPANY}):
|
||||
d = frappe.get_doc({
|
||||
"doctype": "Purchase Taxes and Charges Template",
|
||||
"title": urd,
|
||||
"title": urd_title,
|
||||
"company": COMPANY,
|
||||
"is_default": 0,
|
||||
"taxes": [], # zero rows = zero tax, clean COGS booking
|
||||
"taxes": [],
|
||||
})
|
||||
d.flags.ignore_permissions = True
|
||||
d.insert()
|
||||
ok(f"Purchase Tax Template: {urd}")
|
||||
if safe_insert(d):
|
||||
ok(f"Purchase Tax Template: {urd_title}")
|
||||
else:
|
||||
skip(f"Purchase Tax Template: {urd}")
|
||||
skip(f"Purchase Tax Template: {urd_title} (duplicate)")
|
||||
else:
|
||||
skip(f"Purchase Tax Template: {urd_title}")
|
||||
|
||||
# ── GST 18% Purchase ──
|
||||
gst18_p = "GST 18% - Purchase"
|
||||
if not exists("Purchase Taxes and Charges Template", gst18_p):
|
||||
gst18_p_title = "GST 18% - Purchase"
|
||||
if not exists_filter("Purchase Taxes and Charges Template",
|
||||
{"title": gst18_p_title, "company": COMPANY}):
|
||||
cgst = _find_account("CGST")
|
||||
sgst = _find_account("SGST")
|
||||
taxes = []
|
||||
if cgst:
|
||||
taxes.append({"charge_type": "On Net Total", "account_head": cgst, "rate": 9, "description": "CGST @ 9%"})
|
||||
taxes.append({"charge_type": "On Net Total", "account_head": cgst,
|
||||
"rate": 9, "description": "CGST @ 9%"})
|
||||
if sgst:
|
||||
taxes.append({"charge_type": "On Net Total", "account_head": sgst, "rate": 9, "description": "SGST @ 9%"})
|
||||
taxes.append({"charge_type": "On Net Total", "account_head": sgst,
|
||||
"rate": 9, "description": "SGST @ 9%"})
|
||||
d = frappe.get_doc({
|
||||
"doctype": "Purchase Taxes and Charges Template",
|
||||
"title": gst18_p,
|
||||
"title": gst18_p_title,
|
||||
"company": COMPANY,
|
||||
"is_default": 0,
|
||||
"taxes": taxes,
|
||||
})
|
||||
d.flags.ignore_permissions = True
|
||||
d.insert()
|
||||
ok(f"Purchase Tax Template: {gst18_p}" + (" (no accounts found, empty)" if not taxes else ""))
|
||||
if safe_insert(d):
|
||||
ok(f"Purchase Tax Template: {gst18_p_title}" +
|
||||
(" (no GST accounts in CoA, left empty)" if not taxes else ""))
|
||||
else:
|
||||
skip(f"Purchase Tax Template: {gst18_p}")
|
||||
skip(f"Purchase Tax Template: {gst18_p_title} (duplicate)")
|
||||
else:
|
||||
skip(f"Purchase Tax Template: {gst18_p_title}")
|
||||
|
||||
# ── GST 18% Sales ──
|
||||
gst18_s = "GST 18% - Sales"
|
||||
if not exists("Sales Taxes and Charges Template", gst18_s):
|
||||
gst18_s_title = "GST 18% - Sales"
|
||||
if not exists_filter("Sales Taxes and Charges Template",
|
||||
{"title": gst18_s_title, "company": COMPANY}):
|
||||
cgst = _find_account("CGST")
|
||||
sgst = _find_account("SGST")
|
||||
taxes = []
|
||||
if cgst:
|
||||
taxes.append({"charge_type": "On Net Total", "account_head": cgst, "rate": 9, "description": "CGST @ 9%"})
|
||||
taxes.append({"charge_type": "On Net Total", "account_head": cgst,
|
||||
"rate": 9, "description": "CGST @ 9%"})
|
||||
if sgst:
|
||||
taxes.append({"charge_type": "On Net Total", "account_head": sgst, "rate": 9, "description": "SGST @ 9%"})
|
||||
taxes.append({"charge_type": "On Net Total", "account_head": sgst,
|
||||
"rate": 9, "description": "SGST @ 9%"})
|
||||
d = frappe.get_doc({
|
||||
"doctype": "Sales Taxes and Charges Template",
|
||||
"title": gst18_s,
|
||||
"title": gst18_s_title,
|
||||
"company": COMPANY,
|
||||
"is_default": 0,
|
||||
"taxes": taxes,
|
||||
})
|
||||
d.flags.ignore_permissions = True
|
||||
d.insert()
|
||||
ok(f"Sales Tax Template: {gst18_s}" + (" (no accounts found, empty)" if not taxes else ""))
|
||||
if safe_insert(d):
|
||||
ok(f"Sales Tax Template: {gst18_s_title}" +
|
||||
(" (no GST accounts in CoA, left empty)" if not taxes else ""))
|
||||
else:
|
||||
skip(f"Sales Tax Template: {gst18_s}")
|
||||
skip(f"Sales Tax Template: {gst18_s_title} (duplicate)")
|
||||
else:
|
||||
skip(f"Sales Tax Template: {gst18_s_title}")
|
||||
|
||||
frappe.db.commit()
|
||||
|
||||
|
|
@ -289,7 +323,8 @@ def create_service_items():
|
|||
|
||||
for code, name, uom, desc in items:
|
||||
if not exists("Item", code):
|
||||
d_dict = {
|
||||
# Non-stock service items: no warehouse needed in item_defaults
|
||||
d = frappe.get_doc({
|
||||
"doctype": "Item",
|
||||
"item_code": code,
|
||||
"item_name": name,
|
||||
|
|
@ -301,16 +336,12 @@ def create_service_items():
|
|||
"is_purchase_item": 0,
|
||||
"is_sales_item": 1,
|
||||
"standard_rate": 0,
|
||||
}
|
||||
if income_acct:
|
||||
d_dict["item_defaults"] = [{
|
||||
"company": COMPANY,
|
||||
"income_account": income_acct,
|
||||
}]
|
||||
d = frappe.get_doc(d_dict)
|
||||
d.flags.ignore_permissions = True
|
||||
d.insert()
|
||||
# No item_defaults row — avoids warehouse company-mismatch validation
|
||||
})
|
||||
if safe_insert(d):
|
||||
ok(f"Service Item: {code} [{uom}]")
|
||||
else:
|
||||
skip(f"Service Item: {code} (duplicate)")
|
||||
else:
|
||||
skip(f"Service Item: {code}")
|
||||
|
||||
|
|
@ -370,6 +401,8 @@ def create_raw_material_items():
|
|||
"is_sales_item": 0,
|
||||
"valuation_method": "FIFO",
|
||||
}
|
||||
# Only add item_defaults if we have a valid Furnitex warehouse
|
||||
if default_wh or cogs_acct:
|
||||
defaults = {"company": COMPANY}
|
||||
if default_wh:
|
||||
defaults["default_warehouse"] = default_wh
|
||||
|
|
@ -378,9 +411,10 @@ def create_raw_material_items():
|
|||
d_dict["item_defaults"] = [defaults]
|
||||
|
||||
d = frappe.get_doc(d_dict)
|
||||
d.flags.ignore_permissions = True
|
||||
d.insert()
|
||||
if safe_insert(d):
|
||||
ok(f"Raw Material: {code} [{uom}]")
|
||||
else:
|
||||
skip(f"Raw Material: {code} (duplicate)")
|
||||
else:
|
||||
skip(f"Raw Material: {code}")
|
||||
|
||||
|
|
@ -414,17 +448,20 @@ def create_suppliers():
|
|||
"gst_category": gst_cat,
|
||||
"default_currency": "INR",
|
||||
})
|
||||
d.flags.ignore_permissions = True
|
||||
d.insert()
|
||||
if safe_insert(d):
|
||||
ok(f"Supplier: {name} [{gst_cat}]")
|
||||
else:
|
||||
skip(f"Supplier: {name} (duplicate)")
|
||||
else:
|
||||
skip(f"Supplier: {name}")
|
||||
|
||||
frappe.db.commit()
|
||||
|
||||
# Set URD tax default on all Unregistered suppliers
|
||||
# Set URD tax default — look up by title+company (name includes abbr)
|
||||
urd_template = "No GST - URD Purchase"
|
||||
if exists("Purchase Taxes and Charges Template", urd_template):
|
||||
urd_full = exists_filter("Purchase Taxes and Charges Template",
|
||||
{"title": urd_template, "company": COMPANY})
|
||||
if urd_full:
|
||||
urd_suppliers = frappe.db.sql(
|
||||
"""SELECT name FROM `tabSupplier`
|
||||
WHERE supplier_group = 'Local Market Vendor (Unregistered)'""",
|
||||
|
|
@ -433,10 +470,10 @@ def create_suppliers():
|
|||
for s in urd_suppliers:
|
||||
frappe.db.set_value(
|
||||
"Supplier", s.name,
|
||||
"default_purchase_taxes_and_charges_template", urd_template
|
||||
"default_purchase_taxes_and_charges_template", urd_full
|
||||
)
|
||||
frappe.db.commit()
|
||||
ok(f"Set '{urd_template}' as default tax on {len(urd_suppliers)} URD supplier(s)")
|
||||
ok(f"Set '{urd_full}' as default tax on {len(urd_suppliers)} URD supplier(s)")
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
|
|
|
|||
Loading…
Reference in a new issue