frappe.provide("ns_app.customer"); console.log("NS APP CUSTOMER JS LOADED"); $(document).ready(() => { setTimeout(() => { const TargetClass = frappe.ui.form.CustomerQuickEntryForm; if (!TargetClass) { console.error( "NS App: CustomerQuickEntryForm not found" ); return; } // Prevent duplicate patching if (TargetClass.__ns_patched) { console.log( "NS App: already patched" ); return; } console.log( "NS App: patching CustomerQuickEntryForm" ); frappe.ui.form.CustomerQuickEntryForm = class extends TargetClass { render_dialog() { console.log( "NS App: render_dialog intercepted" ); let customer_name = ""; // Route option first if (frappe.route_options?.name) { customer_name = frappe.route_options.name; } // Focused field fallback if (!customer_name) { const active = document.activeElement; if ( active && active.value ) { customer_name = active.value; } } // cur_frm fallback if ( !customer_name && typeof cur_frm !== "undefined" && cur_frm ) { customer_name = cur_frm.doc.customer || cur_frm.doc.party_name || ""; } console.log( "NS App: Captured customer name:", customer_name ); // DO NOT call super.render_dialog() // This restores the fully custom dialog ns_app.customer.open_quick_entry({ customer_name: customer_name, callback: this.after_insert }); } }; TargetClass.__ns_patched = true; console.log( "NS App: CustomerQuickEntryForm patched successfully" ); }, 1000); }); 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 setTimeout(() => { d.fields.forEach(field => { const control = d.get_field( field.fieldname ); if ( !control || !control.$input ) { return; } control.$input.attr( "aria-label", field.label || field.fieldname ); control.$input.attr( "title", field.label || field.fieldname ); if (field.label) { control.$input.attr( "placeholder", field.label ); } }); console.log( "NS App: accessibility applied" ); }, 300); // ZIP autofill 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(() => {}); }; // Enter navigation d.$wrapper.on( "keydown", "input, select, textarea", function (e) { if ( e.key === "Enter" ) { const active = document.activeElement; // Allow submit only // on primary button if ( active && active.classList.contains( "btn-primary" ) ) { return; } e.preventDefault(); 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(); } } } ); };