frappe.provide("ns_app.customer"); // Preserve original quick entry const _make_quick_entry = frappe.ui.form.make_quick_entry; console.log("NS APP CUSTOMER JS LOADED"); // Override frappe.ui.form.make_quick_entry = function (doctype, after_insert) { if (doctype === "Customer") { console.log("NS App: Intercepted Customer Quick Entry"); let customer_name = ""; // Pull value from current form route options if (frappe.route_options?.name) { customer_name = frappe.route_options.name; } // Fallback: get typed value from active link field if (!customer_name) { const active = document.activeElement; if (active && active.value) { customer_name = active.value; } } console.log("NS App: Captured customer name:", customer_name); ns_app.customer.open_quick_entry({ callback: after_insert, customer_name: customer_name }); return; } return _make_quick_entry.apply(this, arguments); }; ns_app.customer.open_quick_entry = function (opts = {}) { console.log("NS App: Custom Customer Quick Entry OPENED"); const d = new frappe.ui.Dialog({ title: "New Customer", size: "large", fields: [ // ───────── CUSTOMER ───────── { fieldtype: "Section Break", label: "Customer Information" }, { fieldname: "customer_name", label: "Customer Name", fieldtype: "Data", reqd: 1, default: opts.customer_name || "", description: "Enter the customer or company name" }, { fieldname: "customer_type", label: "Customer Type", fieldtype: "Select", options: "Company\nIndividual", default: "Company", reqd: 1, description: "Select whether this customer is a company or individual" }, { fieldname: "customer_group", label: "Customer Group", fieldtype: "Link", options: "Customer Group", default: "Commercial", reqd: 1, description: "Select the customer group" }, { fieldname: "custom_send_via", label: "Preferred Delivery Method", fieldtype: "Select", options: "mail\nemail\nfax", description: "Choose how documents should be sent to the customer" }, // ───────── CONTACT ───────── { fieldtype: "Section Break", label: "Primary Contact" }, { fieldname: "email_id", label: "Email Address", fieldtype: "Data", options: "Email", description: "Enter the customer's email address" }, { fieldname: "mobile_no", label: "Mobile Phone Number", fieldtype: "Data", reqd: 1, description: "Enter the customer's mobile phone number" }, // ───────── ADDRESS ───────── { fieldtype: "Section Break", label: "Address Information" }, { fieldname: "address_line1", label: "Address Line 1", fieldtype: "Data", reqd: 1, description: "Enter the street address" }, { fieldname: "address_line2", label: "Address Line 2", fieldtype: "Data", description: "Enter apartment, suite, or secondary address information" }, { fieldname: "pincode", label: "ZIP Code", fieldtype: "Data", reqd: 1, description: "Enter the ZIP or postal code" }, { fieldname: "city", label: "City", fieldtype: "Data", description: "Enter the city" }, { fieldname: "state", label: "State", fieldtype: "Data", description: "Enter the state" }, { fieldname: "country", label: "Country", fieldtype: "Link", options: "Country", default: "United States", description: "Select the country" } ], primary_action_label: "Create Customer", primary_action(values) { console.log("NS App: Create Customer clicked", values); d.disable_primary_action(); frappe.call({ method: "ns_app.api.customer.create_customer_full", args: values, callback(r) { console.log("NS App: Customer created", r.message); d.hide(); frappe.show_alert({ message: "Customer created via NS App", indicator: "green" }); if (opts.callback) { opts.callback(r.message); } }, always() { d.enable_primary_action(); } }); } }); d.show(); // Accessibility labels for screen readers setTimeout(() => { d.fields.forEach(field => { const control = d.get_field(field.fieldname); if (!control || !control.$input) return; // ARIA label control.$input.attr( "aria-label", field.label || field.fieldname ); // Screen reader title control.$input.attr( "title", field.label || field.fieldname ); // Placeholder text if (field.label) { control.$input.attr( "placeholder", field.label ); } }); console.log("NS App: Accessibility labels applied"); }, 300); // ZIP auto-fill d.fields_dict.pincode.df.onchange = () => { const zip = d.get_value("pincode"); if (!zip || zip.length < 5) return; console.log("NS App: ZIP lookup", zip); fetch(`https://api.zippopotam.us/us/${zip}`) .then(r => r.ok ? r.json() : null) .then(data => { if (!data || !data.places?.length) return; const p = data.places[0]; d.set_value("city", p["place name"]); d.set_value("state", p["state"]); d.set_value("country", data.country); console.log("NS App: ZIP autofill success"); }) .catch(() => {}); }; // Prevent Enter from submitting unless primary button is focused d.$wrapper.on("keydown", "input, select, textarea", function (e) { if (e.key === "Enter") { const active = document.activeElement; // Allow Enter ONLY if primary action button is focused if ( active && active.classList.contains("btn-primary") ) { return; } e.preventDefault(); // Move to next field const fields = d.$wrapper .find("input, select, textarea") .filter(":visible:not([disabled])"); const index = fields.index(this); if (index > -1 && index + 1 < fields.length) { fields.eq(index + 1).focus(); } } }); };