Merge pull request 'update customer quick entry form and field descriptions' (#12) from main into production
Reviewed-on: #12
This commit was merged in pull request #12.
This commit is contained in:
@@ -3,14 +3,39 @@ frappe.provide("ns_app.customer");
|
|||||||
// Preserve original quick entry
|
// Preserve original quick entry
|
||||||
const _make_quick_entry = frappe.ui.form.make_quick_entry;
|
const _make_quick_entry = frappe.ui.form.make_quick_entry;
|
||||||
|
|
||||||
|
console.log("NS APP CUSTOMER JS LOADED");
|
||||||
|
|
||||||
// Override
|
// Override
|
||||||
frappe.ui.form.make_quick_entry = function (doctype, after_insert) {
|
frappe.ui.form.make_quick_entry = function (doctype, after_insert) {
|
||||||
if (doctype === "Customer" && frappe.route_options) {
|
|
||||||
|
if (doctype === "Customer") {
|
||||||
|
|
||||||
console.log("NS App: Intercepted Customer Quick Entry");
|
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({
|
ns_app.customer.open_quick_entry({
|
||||||
callback: after_insert,
|
callback: after_insert,
|
||||||
route_options: frappe.route_options || {}
|
customer_name: customer_name
|
||||||
});
|
});
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -18,6 +43,7 @@ frappe.ui.form.make_quick_entry = function (doctype, after_insert) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
ns_app.customer.open_quick_entry = function (opts = {}) {
|
ns_app.customer.open_quick_entry = function (opts = {}) {
|
||||||
|
|
||||||
console.log("NS App: Custom Customer Quick Entry OPENED");
|
console.log("NS App: Custom Customer Quick Entry OPENED");
|
||||||
|
|
||||||
const d = new frappe.ui.Dialog({
|
const d = new frappe.ui.Dialog({
|
||||||
@@ -25,96 +51,129 @@ ns_app.customer.open_quick_entry = function (opts = {}) {
|
|||||||
size: "large",
|
size: "large",
|
||||||
|
|
||||||
fields: [
|
fields: [
|
||||||
|
|
||||||
// ───────── CUSTOMER ─────────
|
// ───────── CUSTOMER ─────────
|
||||||
{ fieldtype: "Section Break", label: "Customer" },
|
{
|
||||||
|
fieldtype: "Section Break",
|
||||||
|
label: "Customer Information"
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
fieldname: "customer_name",
|
fieldname: "customer_name",
|
||||||
label: "Customer Name",
|
label: "Customer Name",
|
||||||
fieldtype: "Data",
|
fieldtype: "Data",
|
||||||
reqd: 1,
|
reqd: 1,
|
||||||
default: opts.route_options?.customer_name || ""
|
default: opts.customer_name || "",
|
||||||
|
description: "Enter the customer or company name"
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
fieldname: "customer_type",
|
fieldname: "customer_type",
|
||||||
label: "Customer Type",
|
label: "Customer Type",
|
||||||
fieldtype: "Select",
|
fieldtype: "Select",
|
||||||
options: "Company\nIndividual",
|
options: "Company\nIndividual",
|
||||||
default: "Company",
|
default: "Company",
|
||||||
reqd: 1
|
reqd: 1,
|
||||||
|
description: "Select whether this customer is a company or individual"
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
fieldname: "customer_group",
|
fieldname: "customer_group",
|
||||||
label: "Customer Group",
|
label: "Customer Group",
|
||||||
fieldtype: "Link",
|
fieldtype: "Link",
|
||||||
options: "Customer Group",
|
options: "Customer Group",
|
||||||
default: "Commercial",
|
default: "Commercial",
|
||||||
reqd: 1
|
reqd: 1,
|
||||||
|
description: "Select the customer group"
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
fieldname: "custom_send_via",
|
fieldname: "custom_send_via",
|
||||||
label: "Send Via",
|
label: "Preferred Delivery Method",
|
||||||
fieldtype: "Select",
|
fieldtype: "Select",
|
||||||
options: "mail\nemail\nfax"
|
options: "mail\nemail\nfax",
|
||||||
|
description: "Choose how documents should be sent to the customer"
|
||||||
},
|
},
|
||||||
|
|
||||||
// ───────── CONTACT ─────────
|
// ───────── CONTACT ─────────
|
||||||
{ fieldtype: "Section Break", label: "Primary Contact" },
|
{
|
||||||
|
fieldtype: "Section Break",
|
||||||
|
label: "Primary Contact"
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
fieldname: "email_id",
|
fieldname: "email_id",
|
||||||
label: "Email",
|
label: "Email Address",
|
||||||
fieldtype: "Data",
|
fieldtype: "Data",
|
||||||
options: "Email"
|
options: "Email",
|
||||||
|
description: "Enter the customer's email address"
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
fieldname: "mobile_no",
|
fieldname: "mobile_no",
|
||||||
label: "Mobile",
|
label: "Mobile Phone Number",
|
||||||
fieldtype: "Data",
|
fieldtype: "Data",
|
||||||
reqd: 1
|
reqd: 1,
|
||||||
|
description: "Enter the customer's mobile phone number"
|
||||||
},
|
},
|
||||||
|
|
||||||
// ───────── ADDRESS ─────────
|
// ───────── ADDRESS ─────────
|
||||||
{ fieldtype: "Section Break", label: "Address" },
|
{
|
||||||
|
fieldtype: "Section Break",
|
||||||
|
label: "Address Information"
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
fieldname: "address_line1",
|
fieldname: "address_line1",
|
||||||
label: "Address Line 1",
|
label: "Address Line 1",
|
||||||
fieldtype: "Data",
|
fieldtype: "Data",
|
||||||
reqd: 1
|
reqd: 1,
|
||||||
|
description: "Enter the street address"
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
fieldname: "address_line2",
|
fieldname: "address_line2",
|
||||||
label: "Address Line 2",
|
label: "Address Line 2",
|
||||||
fieldtype: "Data"
|
fieldtype: "Data",
|
||||||
|
description: "Enter apartment, suite, or secondary address information"
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
fieldname: "pincode",
|
fieldname: "pincode",
|
||||||
label: "ZIP Code",
|
label: "ZIP Code",
|
||||||
fieldtype: "Data",
|
fieldtype: "Data",
|
||||||
reqd: 1
|
reqd: 1,
|
||||||
|
description: "Enter the ZIP or postal code"
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
fieldname: "city",
|
fieldname: "city",
|
||||||
label: "City",
|
label: "City",
|
||||||
fieldtype: "Data"
|
fieldtype: "Data",
|
||||||
|
description: "Enter the city"
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
fieldname: "state",
|
fieldname: "state",
|
||||||
label: "State",
|
label: "State",
|
||||||
fieldtype: "Data"
|
fieldtype: "Data",
|
||||||
|
description: "Enter the state"
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
fieldname: "country",
|
fieldname: "country",
|
||||||
label: "Country",
|
label: "Country",
|
||||||
fieldtype: "Link",
|
fieldtype: "Link",
|
||||||
options: "Country",
|
options: "Country",
|
||||||
default: "United States"
|
default: "United States",
|
||||||
|
description: "Select the country"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
primary_action_label: "Create Customer",
|
primary_action_label: "Create Customer",
|
||||||
|
|
||||||
primary_action(values) {
|
primary_action(values) {
|
||||||
|
|
||||||
console.log("NS App: Create Customer clicked", values);
|
console.log("NS App: Create Customer clicked", values);
|
||||||
|
|
||||||
d.disable_primary_action();
|
d.disable_primary_action();
|
||||||
@@ -122,19 +181,23 @@ ns_app.customer.open_quick_entry = function (opts = {}) {
|
|||||||
frappe.call({
|
frappe.call({
|
||||||
method: "ns_app.api.customer.create_customer_full",
|
method: "ns_app.api.customer.create_customer_full",
|
||||||
args: values,
|
args: values,
|
||||||
|
|
||||||
callback(r) {
|
callback(r) {
|
||||||
|
|
||||||
console.log("NS App: Customer created", r.message);
|
console.log("NS App: Customer created", r.message);
|
||||||
|
|
||||||
d.hide();
|
d.hide();
|
||||||
|
|
||||||
frappe.show_alert({
|
frappe.show_alert({
|
||||||
message: "Customer created via NS App",
|
message: "Customer created via NS App",
|
||||||
indicator: "green"
|
indicator: "green"
|
||||||
});
|
});
|
||||||
|
|
||||||
if (opts.callback && r.message) {
|
if (opts.callback) {
|
||||||
opts.callback(r.message.name || r.message);
|
opts.callback(r.message);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
always() {
|
always() {
|
||||||
d.enable_primary_action();
|
d.enable_primary_action();
|
||||||
}
|
}
|
||||||
@@ -142,9 +205,47 @@ ns_app.customer.open_quick_entry = function (opts = {}) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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
|
// ZIP auto-fill
|
||||||
d.fields_dict.pincode.df.onchange = () => {
|
d.fields_dict.pincode.df.onchange = () => {
|
||||||
|
|
||||||
const zip = d.get_value("pincode");
|
const zip = d.get_value("pincode");
|
||||||
|
|
||||||
if (!zip || zip.length < 5) return;
|
if (!zip || zip.length < 5) return;
|
||||||
|
|
||||||
console.log("NS App: ZIP lookup", zip);
|
console.log("NS App: ZIP lookup", zip);
|
||||||
@@ -152,9 +253,11 @@ ns_app.customer.open_quick_entry = function (opts = {}) {
|
|||||||
fetch(`https://api.zippopotam.us/us/${zip}`)
|
fetch(`https://api.zippopotam.us/us/${zip}`)
|
||||||
.then(r => r.ok ? r.json() : null)
|
.then(r => r.ok ? r.json() : null)
|
||||||
.then(data => {
|
.then(data => {
|
||||||
|
|
||||||
if (!data || !data.places?.length) return;
|
if (!data || !data.places?.length) return;
|
||||||
|
|
||||||
const p = data.places[0];
|
const p = data.places[0];
|
||||||
|
|
||||||
d.set_value("city", p["place name"]);
|
d.set_value("city", p["place name"]);
|
||||||
d.set_value("state", p["state"]);
|
d.set_value("state", p["state"]);
|
||||||
d.set_value("country", data.country);
|
d.set_value("country", data.country);
|
||||||
@@ -164,29 +267,11 @@ ns_app.customer.open_quick_entry = function (opts = {}) {
|
|||||||
.catch(() => {});
|
.catch(() => {});
|
||||||
};
|
};
|
||||||
|
|
||||||
d.show();
|
|
||||||
|
|
||||||
let autoPayField = d.get_field("custom_auto_pay_enabled");
|
|
||||||
|
|
||||||
// Initial state
|
|
||||||
d.set_df_property(
|
|
||||||
"custom_auto_pay_id",
|
|
||||||
"hidden",
|
|
||||||
!d.get_value("custom_auto_pay_enabled")
|
|
||||||
);
|
|
||||||
|
|
||||||
// When checkbox changes
|
|
||||||
autoPayField.$input.on("change", () => {
|
|
||||||
let isChecked = d.get_value("custom_auto_pay_enabled");
|
|
||||||
|
|
||||||
d.set_df_property("custom_auto_pay_id", "hidden", !isChecked);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Prevent Enter from submitting unless primary button is focused
|
// Prevent Enter from submitting unless primary button is focused
|
||||||
d.$wrapper.on("keydown", "input, select, textarea", function (e) {
|
d.$wrapper.on("keydown", "input, select, textarea", function (e) {
|
||||||
|
|
||||||
if (e.key === "Enter") {
|
if (e.key === "Enter") {
|
||||||
|
|
||||||
const active = document.activeElement;
|
const active = document.activeElement;
|
||||||
|
|
||||||
// Allow Enter ONLY if primary action button is focused
|
// Allow Enter ONLY if primary action button is focused
|
||||||
@@ -205,10 +290,10 @@ ns_app.customer.open_quick_entry = function (opts = {}) {
|
|||||||
.filter(":visible:not([disabled])");
|
.filter(":visible:not([disabled])");
|
||||||
|
|
||||||
const index = fields.index(this);
|
const index = fields.index(this);
|
||||||
|
|
||||||
if (index > -1 && index + 1 < fields.length) {
|
if (index > -1 && index + 1 < fields.length) {
|
||||||
fields.eq(index + 1).focus();
|
fields.eq(index + 1).focus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
};
|
};
|
||||||
Reference in New Issue
Block a user