|
@@ -0,0 +1,113 @@
|
|
|
+import frappe
|
|
|
+import re
|
|
|
+from frappe import _
|
|
|
+
|
|
|
+def generate_customer_abbr(doc, method):
|
|
|
+ """Generate customer abbreviation based on customer name"""
|
|
|
+ if not doc.customer_name or doc.customer_abbr:
|
|
|
+ return
|
|
|
+
|
|
|
+ # Get base abbreviation from customer name
|
|
|
+ base_abbr = _get_base_abbreviation(doc.customer_name)
|
|
|
+
|
|
|
+ # Generate unique abbreviation
|
|
|
+ unique_abbr = _generate_unique_abbreviation(base_abbr, doc.name)
|
|
|
+
|
|
|
+ doc.customer_abbr = unique_abbr
|
|
|
+
|
|
|
+def _get_base_abbreviation(customer_name):
|
|
|
+ """Extract base abbreviation from customer name"""
|
|
|
+ if not customer_name:
|
|
|
+ return "C"
|
|
|
+
|
|
|
+ # Remove special characters and split by spaces
|
|
|
+ clean_name = re.sub(r'[^a-zA-Z0-9\s]', '', customer_name)
|
|
|
+ words = clean_name.split()
|
|
|
+
|
|
|
+ if not words:
|
|
|
+ return "C"
|
|
|
+
|
|
|
+ # Get first word and extract first 2 characters
|
|
|
+ first_word = words[0].upper()
|
|
|
+ if len(first_word) >= 2:
|
|
|
+ return first_word[:2]
|
|
|
+ else:
|
|
|
+ return first_word + "C"
|
|
|
+
|
|
|
+def _generate_unique_abbreviation(base_abbr, current_doc_name):
|
|
|
+ """Generate unique 3-character abbreviation"""
|
|
|
+ if len(base_abbr) >= 3:
|
|
|
+ base_abbr = base_abbr[:2]
|
|
|
+
|
|
|
+ # Try base abbreviation first
|
|
|
+ if _is_abbreviation_unique(base_abbr, current_doc_name):
|
|
|
+ return base_abbr.ljust(3, "0")
|
|
|
+
|
|
|
+ # Try with numbers 1-9
|
|
|
+ for num in range(1, 10):
|
|
|
+ test_abbr = base_abbr + str(num)
|
|
|
+ if _is_abbreviation_unique(test_abbr, current_doc_name):
|
|
|
+ return test_abbr
|
|
|
+
|
|
|
+ # Try with letters A-W (excluding X, Y, Z for future use)
|
|
|
+ for letter in "ABCDEFGHIJKLMNOPQRSTUVW":
|
|
|
+ test_abbr = base_abbr + letter
|
|
|
+ if _is_abbreviation_unique(test_abbr, current_doc_name):
|
|
|
+ return test_abbr
|
|
|
+
|
|
|
+ # If still not unique, try with numbers 0-9 for first position
|
|
|
+ for num in range(0, 10):
|
|
|
+ test_abbr = str(num) + base_abbr[:2]
|
|
|
+ if _is_abbreviation_unique(test_abbr, current_doc_name):
|
|
|
+ return test_abbr
|
|
|
+
|
|
|
+ # Last resort: use numbers
|
|
|
+ counter = 1
|
|
|
+ while counter <= 999:
|
|
|
+ test_abbr = str(counter).zfill(3)
|
|
|
+ if _is_abbreviation_unique(test_abbr, current_doc_name):
|
|
|
+ return test_abbr
|
|
|
+ counter += 1
|
|
|
+
|
|
|
+ # If all else fails, use a timestamp-based approach
|
|
|
+ import time
|
|
|
+ timestamp = int(time.time()) % 1000
|
|
|
+ return str(timestamp).zfill(3)
|
|
|
+
|
|
|
+def _is_abbreviation_unique(abbreviation, current_doc_name):
|
|
|
+ """Check if abbreviation is unique in the system"""
|
|
|
+ existing = frappe.db.get_value(
|
|
|
+ "Customer",
|
|
|
+ {"customer_abbr": abbreviation, "name": ["!=", current_doc_name]},
|
|
|
+ "name"
|
|
|
+ )
|
|
|
+ return not existing
|
|
|
+
|
|
|
+def validate_customer_abbr(doc, method):
|
|
|
+ """Validate customer abbreviation format and uniqueness"""
|
|
|
+ if not doc.customer_abbr:
|
|
|
+ return
|
|
|
+
|
|
|
+ # Check length
|
|
|
+ if len(doc.customer_abbr) != 3:
|
|
|
+ frappe.throw(_("Customer abbreviation must be exactly 3 characters long"))
|
|
|
+
|
|
|
+ # Check format (alphanumeric only)
|
|
|
+ if not re.match(r'^[A-Z0-9]{3}$', doc.customer_abbr):
|
|
|
+ frappe.throw(_("Customer abbreviation must contain only uppercase letters and numbers"))
|
|
|
+
|
|
|
+ # Check uniqueness
|
|
|
+ existing = frappe.db.get_value(
|
|
|
+ "Customer",
|
|
|
+ {"customer_abbr": doc.customer_abbr, "name": ["!=", doc.name]},
|
|
|
+ "name"
|
|
|
+ )
|
|
|
+ if existing:
|
|
|
+ frappe.throw(_("Customer abbreviation '{0}' already exists for customer '{1}'").format(
|
|
|
+ doc.customer_abbr, existing
|
|
|
+ ))
|
|
|
+
|
|
|
+def on_customer_name_change(doc, method):
|
|
|
+ """Handle customer name changes to update abbreviation if needed"""
|
|
|
+ if doc.has_value_changed("customer_name") and not doc.customer_abbr:
|
|
|
+ generate_customer_abbr(doc, method)
|