diff --git a/crm/api/doc.py b/crm/api/doc.py index 64d14f6f..2e1e0d5c 100644 --- a/crm/api/doc.py +++ b/crm/api/doc.py @@ -593,17 +593,18 @@ def get_sidebar_fields(doctype, name): for section in layout: section["name"] = section.get("name") or section.get("label") - for field in section.get("fields") if section.get("fields") else []: - field_obj = next((f for f in fields if f.fieldname == field), None) - if field_obj: - if field_obj.permlevel > 0: - field_has_write_access = field_obj.permlevel in has_write_access_to_permlevels - field_has_read_access = field_obj.permlevel in has_read_access_to_permlevels - if not field_has_write_access and field_has_read_access: - field_obj.read_only = 1 - if not field_has_read_access and not field_has_write_access: - field_obj.hidden = 1 - section["fields"][section.get("fields").index(field)] = get_field_obj(field_obj) + for column in section.get("columns") if section.get("columns") else []: + for field in column.get("fields") if column.get("fields") else []: + field_obj = next((f for f in fields if f.fieldname == field), None) + if field_obj: + if field_obj.permlevel > 0: + field_has_write_access = field_obj.permlevel in has_write_access_to_permlevels + field_has_read_access = field_obj.permlevel in has_read_access_to_permlevels + if not field_has_write_access and field_has_read_access: + field_obj.read_only = 1 + if not field_has_read_access and not field_has_write_access: + field_obj.hidden = 1 + column["fields"][column.get("fields").index(field)] = get_field_obj(field_obj) fields_meta = {} for field in fields: diff --git a/frontend/src/components/Controls/GridRowFieldsModal.vue b/frontend/src/components/Controls/GridRowFieldsModal.vue index 4c18383f..894baf72 100644 --- a/frontend/src/components/Controls/GridRowFieldsModal.vue +++ b/frontend/src/components/Controls/GridRowFieldsModal.vue @@ -101,10 +101,12 @@ function saveChanges() { _tabs.forEach((tab) => { if (!tab.sections) return tab.sections.forEach((section) => { - if (!section.fields) return - section.fields = section.fields.map( - (field) => field.fieldname || field.name, - ) + section.columns.forEach((column) => { + if (!column.fields) return + column.fields = column.fields.map( + (field) => field.fieldname || field.name, + ) + }) }) }) loading.value = true diff --git a/frontend/src/components/Modals/ContactModal.vue b/frontend/src/components/Modals/ContactModal.vue index 465610f9..88c9953f 100644 --- a/frontend/src/components/Modals/ContactModal.vue +++ b/frontend/src/components/Modals/ContactModal.vue @@ -125,10 +125,12 @@ const tabs = createResource({ transform: (_tabs) => { return _tabs.forEach((tab) => { tab.sections.forEach((section) => { - section.fields.forEach((field) => { - if (field.type === 'Table') { - _contact.value[field.name] = [] - } + section.columns.forEach((column) => { + column.fields.forEach((field) => { + if (field.type === 'Table') { + _contact.value[field.name] = [] + } + }) }) }) }) diff --git a/frontend/src/components/Modals/DataFieldsModal.vue b/frontend/src/components/Modals/DataFieldsModal.vue index 87b4ce62..04c3c6f7 100644 --- a/frontend/src/components/Modals/DataFieldsModal.vue +++ b/frontend/src/components/Modals/DataFieldsModal.vue @@ -101,10 +101,12 @@ function saveChanges() { _tabs.forEach((tab) => { if (!tab.sections) return tab.sections.forEach((section) => { - if (!section.fields) return - section.fields = section.fields.map( - (field) => field.fieldname || field.name, - ) + section.columns.forEach((column) => { + if (!column.fields) return + column.fields = column.fields.map( + (field) => field.fieldname || field.name, + ) + }) }) }) loading.value = true diff --git a/frontend/src/components/Modals/DealModal.vue b/frontend/src/components/Modals/DealModal.vue index 49243672..f2b97ed8 100644 --- a/frontend/src/components/Modals/DealModal.vue +++ b/frontend/src/components/Modals/DealModal.vue @@ -109,18 +109,20 @@ const tabs = createResource({ transform: (_tabs) => { return _tabs.forEach((tab) => { tab.sections.forEach((section) => { - section.fields.forEach((field) => { - if (field.name == 'status') { - field.type = 'Select' - field.options = dealStatuses.value - field.prefix = getDealStatus(deal.status).iconColorClass - } else if (field.name == 'deal_owner') { - field.type = 'User' - } + section.columns.forEach((column) => { + column.fields.forEach((field) => { + if (field.name == 'status') { + field.type = 'Select' + field.options = dealStatuses.value + field.prefix = getDealStatus(deal.status).iconColorClass + } else if (field.name == 'deal_owner') { + field.type = 'User' + } - if (field.type === 'Table') { - deal[field.name] = [] - } + if (field.type === 'Table') { + deal[field.name] = [] + } + }) }) }) }) diff --git a/frontend/src/components/Modals/LeadModal.vue b/frontend/src/components/Modals/LeadModal.vue index 848d6cb0..08f0ccad 100644 --- a/frontend/src/components/Modals/LeadModal.vue +++ b/frontend/src/components/Modals/LeadModal.vue @@ -71,18 +71,20 @@ const tabs = createResource({ transform: (_tabs) => { return _tabs.forEach((tab) => { tab.sections.forEach((section) => { - section.fields.forEach((field) => { - if (field.name == 'status') { - field.type = 'Select' - field.options = leadStatuses.value - field.prefix = getLeadStatus(lead.status).iconColorClass - } else if (field.name == 'lead_owner') { - field.type = 'User' - } + section.columns.forEach((column) => { + column.fields.forEach((field) => { + if (field.name == 'status') { + field.type = 'Select' + field.options = leadStatuses.value + field.prefix = getLeadStatus(lead.status).iconColorClass + } else if (field.name == 'lead_owner') { + field.type = 'User' + } - if (field.type === 'Table') { - lead[field.name] = [] - } + if (field.type === 'Table') { + lead[field.name] = [] + } + }) }) }) }) diff --git a/frontend/src/components/Modals/OrganizationModal.vue b/frontend/src/components/Modals/OrganizationModal.vue index 022dadd6..8818f0e4 100644 --- a/frontend/src/components/Modals/OrganizationModal.vue +++ b/frontend/src/components/Modals/OrganizationModal.vue @@ -124,10 +124,12 @@ const tabs = createResource({ transform: (_tabs) => { return _tabs.forEach((tab) => { tab.sections.forEach((section) => { - section.fields.forEach((field) => { - if (field.type === 'Table') { - _organization.value[field.name] = [] - } + section.columns.forEach((column) => { + column.fields.forEach((field) => { + if (field.type === 'Table') { + _organization.value[field.name] = [] + } + }) }) }) }) diff --git a/frontend/src/components/Modals/QuickEntryModal.vue b/frontend/src/components/Modals/QuickEntryModal.vue index 4f32a7e5..c9e83790 100644 --- a/frontend/src/components/Modals/QuickEntryModal.vue +++ b/frontend/src/components/Modals/QuickEntryModal.vue @@ -99,10 +99,12 @@ function saveChanges() { _tabs.forEach((tab) => { if (!tab.sections) return tab.sections.forEach((section) => { - if (!section.fields) return - section.fields = section.fields.map( - (field) => field.fieldname || field.name, - ) + section.columns.forEach((column) => { + if (!column.fields) return + column.fields = column.fields.map( + (field) => field.fieldname || field.name, + ) + }) }) }) loading.value = true diff --git a/frontend/src/components/Modals/SidePanelModal.vue b/frontend/src/components/Modals/SidePanelModal.vue index 2302cb00..a209f6ba 100644 --- a/frontend/src/components/Modals/SidePanelModal.vue +++ b/frontend/src/components/Modals/SidePanelModal.vue @@ -51,7 +51,7 @@ :opened="section.opened" > @@ -129,10 +129,12 @@ function saveChanges() { let _tabs = JSON.parse(JSON.stringify(tabs.data)) _tabs.forEach((tab) => { tab.sections.forEach((section) => { - if (!section.fields) return - section.fields = section.fields - .map((field) => field.fieldname || field.name) - .filter(Boolean) + section.columns.forEach((column) => { + if (!column.fields) return + column.fields = column.fields + .map((field) => field.fieldname || field.name) + .filter(Boolean) + }) }) }) loading.value = true diff --git a/frontend/src/pages/Contact.vue b/frontend/src/pages/Contact.vue index db0fa94a..68ada588 100644 --- a/frontend/src/pages/Contact.vue +++ b/frontend/src/pages/Contact.vue @@ -139,8 +139,8 @@ { - return { - ...section, - fields: computed(() => - section.fields.map((field) => { - if (field.name === 'email_id') { - return { - ...field, - type: 'dropdown', - options: - contact.data?.email_ids?.map((email) => { - return { - name: email.name, - value: email.email_id, - selected: email.email_id === contact.data.email_id, - placeholder: 'john@doe.com', - onClick: () => { - _contact.value.email_id = email.email_id - setAsPrimary('email', email.email_id) - }, - onSave: (option, isNew) => { - if (isNew) { - createNew('email', option.value) - if (contact.data.email_ids.length === 1) { - _contact.value.email_id = option.value - } - } else { - editOption( - 'Contact Email', - option.name, - 'email_id', - option.value, - ) + section.columns = section.columns.map((column) => { + column.fields = column.fields.map((field) => { + if (field.name === 'email_id') { + return { + ...field, + read_only: false, + type: 'dropdown', + options: + contact.data?.email_ids?.map((email) => { + return { + name: email.name, + value: email.email_id, + selected: email.email_id === contact.data.email_id, + placeholder: 'john@doe.com', + onClick: () => { + _contact.value.email_id = email.email_id + setAsPrimary('email', email.email_id) + }, + onSave: (option, isNew) => { + if (isNew) { + createNew('email', option.value) + if (contact.data.email_ids.length === 1) { + _contact.value.email_id = option.value } - }, - onDelete: async (option, isNew) => { - contact.data.email_ids = contact.data.email_ids.filter( - (email) => email.name !== option.name, + } else { + editOption( + 'Contact Email', + option.name, + 'email_id', + option.value, ) - !isNew && - (await deleteOption('Contact Email', option.name)) - if (_contact.value.email_id === option.value) { - if (contact.data.email_ids.length === 0) { - _contact.value.email_id = '' - } else { - _contact.value.email_id = contact.data.email_ids.find( - (email) => email.is_primary, - )?.email_id - } - } - }, - } - }) || [], - create: () => { - contact.data?.email_ids?.push({ - name: 'new-1', - value: '', - selected: false, - isNew: true, - }) - }, - } - } else if (field.name === 'mobile_no') { - return { - ...field, - type: 'dropdown', - options: - contact.data?.phone_nos?.map((phone) => { - return { - name: phone.name, - value: phone.phone, - selected: phone.phone === contact.data.actual_mobile_no, - onClick: () => { - _contact.value.actual_mobile_no = phone.phone - _contact.value.mobile_no = phone.phone - setAsPrimary('mobile_no', phone.phone) - }, - onSave: (option, isNew) => { - if (isNew) { - createNew('phone', option.value) - if (contact.data.phone_nos.length === 1) { - _contact.value.actual_mobile_no = option.value - } + } + }, + onDelete: async (option, isNew) => { + contact.data.email_ids = contact.data.email_ids.filter( + (email) => email.name !== option.name, + ) + !isNew && (await deleteOption('Contact Email', option.name)) + if (_contact.value.email_id === option.value) { + if (contact.data.email_ids.length === 0) { + _contact.value.email_id = '' } else { - editOption( - 'Contact Phone', - option.name, - 'phone', - option.value, - ) + _contact.value.email_id = contact.data.email_ids.find( + (email) => email.is_primary, + )?.email_id } - }, - onDelete: async (option, isNew) => { - contact.data.phone_nos = contact.data.phone_nos.filter( - (phone) => phone.name !== option.name, - ) - !isNew && - (await deleteOption('Contact Phone', option.name)) - if (_contact.value.actual_mobile_no === option.value) { - if (contact.data.phone_nos.length === 0) { - _contact.value.actual_mobile_no = '' - } else { - _contact.value.actual_mobile_no = - contact.data.phone_nos.find( - (phone) => phone.is_primary_mobile_no, - )?.phone - } - } - }, - } - }) || [], - create: () => { - contact.data?.phone_nos?.push({ - name: 'new-1', - value: '', - selected: false, - isNew: true, - }) - }, - } - } else if (field.name === 'address') { - return { - ...field, - create: (value, close) => { - _contact.value.address = value - _address.value = {} - showAddressModal.value = true - close() - }, - edit: async (addr) => { - _address.value = await call('frappe.client.get', { - doctype: 'Address', - name: addr, - }) - showAddressModal.value = true - }, - } - } else { - return field + } + }, + } + }) || [], + create: () => { + contact.data?.email_ids?.push({ + name: 'new-1', + value: '', + selected: false, + isNew: true, + }) + }, } - }), - ), - } + } else if (field.name === 'mobile_no') { + return { + ...field, + read_only: false, + type: 'dropdown', + options: + contact.data?.phone_nos?.map((phone) => { + return { + name: phone.name, + value: phone.phone, + selected: phone.phone === contact.data.actual_mobile_no, + onClick: () => { + _contact.value.actual_mobile_no = phone.phone + _contact.value.mobile_no = phone.phone + setAsPrimary('mobile_no', phone.phone) + }, + onSave: (option, isNew) => { + if (isNew) { + createNew('phone', option.value) + if (contact.data.phone_nos.length === 1) { + _contact.value.actual_mobile_no = option.value + } + } else { + editOption( + 'Contact Phone', + option.name, + 'phone', + option.value, + ) + } + }, + onDelete: async (option, isNew) => { + contact.data.phone_nos = contact.data.phone_nos.filter( + (phone) => phone.name !== option.name, + ) + !isNew && (await deleteOption('Contact Phone', option.name)) + if (_contact.value.actual_mobile_no === option.value) { + if (contact.data.phone_nos.length === 0) { + _contact.value.actual_mobile_no = '' + } else { + _contact.value.actual_mobile_no = + contact.data.phone_nos.find( + (phone) => phone.is_primary_mobile_no, + )?.phone + } + } + }, + } + }) || [], + create: () => { + contact.data?.phone_nos?.push({ + name: 'new-1', + value: '', + selected: false, + isNew: true, + }) + }, + } + } else if (field.name === 'address') { + return { + ...field, + create: (value, close) => { + _contact.value.address = value + _address.value = {} + showAddressModal.value = true + close() + }, + edit: async (addr) => { + _address.value = await call('frappe.client.get', { + doctype: 'Address', + name: addr, + }) + showAddressModal.value = true + }, + } + } else { + return field + } + }) + return column + }) + return section }) } diff --git a/frontend/src/pages/Deal.vue b/frontend/src/pages/Deal.vue index 2ee383c6..6154ecc8 100644 --- a/frontend/src/pages/Deal.vue +++ b/frontend/src/pages/Deal.vue @@ -168,8 +168,8 @@ { if (section.name == 'contacts_section') return - section.fields.forEach((field) => { + section.columns[0].fields.forEach((field) => { if (field.name == 'organization') { field.create = (value, close) => { _organization.value.organization_name = value diff --git a/frontend/src/pages/Lead.vue b/frontend/src/pages/Lead.vue index 5e768d42..ba586364 100644 --- a/frontend/src/pages/Lead.vue +++ b/frontend/src/pages/Lead.vue @@ -183,7 +183,7 @@ :opened="section.opened" >
{ - return { - ...section, - fields: computed(() => - section.fields.map((field) => { - if (field.name === 'email_id') { - return { - ...field, - type: 'dropdown', - options: - contact.data?.email_ids?.map((email) => { - return { - name: email.name, - value: email.email_id, - selected: email.email_id === contact.data.email_id, - placeholder: 'john@doe.com', - onClick: () => { - _contact.value.email_id = email.email_id - setAsPrimary('email', email.email_id) - }, - onSave: (option, isNew) => { - if (isNew) { - createNew('email', option.value) - if (contact.data.email_ids.length === 1) { - _contact.value.email_id = option.value - } - } else { - editOption( - 'Contact Email', - option.name, - 'email_id', - option.value, - ) + section.columns = section.columns.map((column) => { + column.fields = column.fields.map((field) => { + if (field.name === 'email_id') { + return { + ...field, + type: 'dropdown', + options: + contact.data?.email_ids?.map((email) => { + return { + name: email.name, + value: email.email_id, + selected: email.email_id === contact.data.email_id, + placeholder: 'john@doe.com', + onClick: () => { + _contact.value.email_id = email.email_id + setAsPrimary('email', email.email_id) + }, + onSave: (option, isNew) => { + if (isNew) { + createNew('email', option.value) + if (contact.data.email_ids.length === 1) { + _contact.value.email_id = option.value } - }, - onDelete: async (option, isNew) => { - contact.data.email_ids = contact.data.email_ids.filter( - (email) => email.name !== option.name, + } else { + editOption( + 'Contact Email', + option.name, + 'email_id', + option.value, ) - !isNew && - (await deleteOption('Contact Email', option.name)) - if (_contact.value.email_id === option.value) { - if (contact.data.email_ids.length === 0) { - _contact.value.email_id = '' - } else { - _contact.value.email_id = contact.data.email_ids.find( - (email) => email.is_primary, - )?.email_id - } - } - }, - } - }) || [], - create: () => { - contact.data?.email_ids?.push({ - name: 'new-1', - value: '', - selected: false, - isNew: true, - }) - }, - } - } else if (field.name === 'mobile_no') { - return { - ...field, - type: 'dropdown', - options: - contact.data?.phone_nos?.map((phone) => { - return { - name: phone.name, - value: phone.phone, - selected: phone.phone === contact.data.actual_mobile_no, - onClick: () => { - _contact.value.actual_mobile_no = phone.phone - _contact.value.mobile_no = phone.phone - setAsPrimary('mobile_no', phone.phone) - }, - onSave: (option, isNew) => { - if (isNew) { - createNew('phone', option.value) - if (contact.data.phone_nos.length === 1) { - _contact.value.actual_mobile_no = option.value - } + } + }, + onDelete: async (option, isNew) => { + contact.data.email_ids = contact.data.email_ids.filter( + (email) => email.name !== option.name, + ) + !isNew && (await deleteOption('Contact Email', option.name)) + if (_contact.value.email_id === option.value) { + if (contact.data.email_ids.length === 0) { + _contact.value.email_id = '' } else { - editOption( - 'Contact Phone', - option.name, - 'phone', - option.value, - ) + _contact.value.email_id = contact.data.email_ids.find( + (email) => email.is_primary, + )?.email_id } - }, - onDelete: async (option, isNew) => { - contact.data.phone_nos = contact.data.phone_nos.filter( - (phone) => phone.name !== option.name, - ) - !isNew && - (await deleteOption('Contact Phone', option.name)) - if (_contact.value.actual_mobile_no === option.value) { - if (contact.data.phone_nos.length === 0) { - _contact.value.actual_mobile_no = '' - } else { - _contact.value.actual_mobile_no = - contact.data.phone_nos.find( - (phone) => phone.is_primary_mobile_no, - )?.phone - } - } - }, - } - }) || [], - create: () => { - contact.data?.phone_nos?.push({ - name: 'new-1', - value: '', - selected: false, - isNew: true, - }) - }, - } - } else if (field.name === 'address') { - return { - ...field, - create: (value, close) => { - _contact.value.address = value - _address.value = {} - showAddressModal.value = true - close() - }, - edit: async (addr) => { - _address.value = await call('frappe.client.get', { - doctype: 'Address', - name: addr, - }) - showAddressModal.value = true - }, - } - } else { - return field + } + }, + } + }) || [], + create: () => { + contact.data?.email_ids?.push({ + name: 'new-1', + value: '', + selected: false, + isNew: true, + }) + }, } - }), - ), - } + } else if (field.name === 'mobile_no') { + return { + ...field, + type: 'dropdown', + options: + contact.data?.phone_nos?.map((phone) => { + return { + name: phone.name, + value: phone.phone, + selected: phone.phone === contact.data.actual_mobile_no, + onClick: () => { + _contact.value.actual_mobile_no = phone.phone + _contact.value.mobile_no = phone.phone + setAsPrimary('mobile_no', phone.phone) + }, + onSave: (option, isNew) => { + if (isNew) { + createNew('phone', option.value) + if (contact.data.phone_nos.length === 1) { + _contact.value.actual_mobile_no = option.value + } + } else { + editOption( + 'Contact Phone', + option.name, + 'phone', + option.value, + ) + } + }, + onDelete: async (option, isNew) => { + contact.data.phone_nos = contact.data.phone_nos.filter( + (phone) => phone.name !== option.name, + ) + !isNew && (await deleteOption('Contact Phone', option.name)) + if (_contact.value.actual_mobile_no === option.value) { + if (contact.data.phone_nos.length === 0) { + _contact.value.actual_mobile_no = '' + } else { + _contact.value.actual_mobile_no = + contact.data.phone_nos.find( + (phone) => phone.is_primary_mobile_no, + )?.phone + } + } + }, + } + }) || [], + create: () => { + contact.data?.phone_nos?.push({ + name: 'new-1', + value: '', + selected: false, + isNew: true, + }) + }, + } + } else if (field.name === 'address') { + return { + ...field, + create: (value, close) => { + _contact.value.address = value + _address.value = {} + showAddressModal.value = true + close() + }, + edit: async (addr) => { + _address.value = await call('frappe.client.get', { + doctype: 'Address', + name: addr, + }) + showAddressModal.value = true + }, + } + } else { + return field + } + }) + return column + }) + return section }) } diff --git a/frontend/src/pages/MobileDeal.vue b/frontend/src/pages/MobileDeal.vue index 8d5493db..3af5ff1c 100644 --- a/frontend/src/pages/MobileDeal.vue +++ b/frontend/src/pages/MobileDeal.vue @@ -99,8 +99,8 @@ { if (section.name == 'contacts_section') return - section.fields.forEach((field) => { + section.columns[0].fields.forEach((field) => { if (field.name == 'organization') { field.create = (value, close) => { _organization.value.organization_name = value diff --git a/frontend/src/pages/MobileLead.vue b/frontend/src/pages/MobileLead.vue index 954a2d1b..91e4b30b 100644 --- a/frontend/src/pages/MobileLead.vue +++ b/frontend/src/pages/MobileLead.vue @@ -76,7 +76,7 @@ >
{ - return { - ...section, - fields: computed(() => - section.fields.map((field) => { - if (field.name === 'address') { - return { - ...field, - create: (value, close) => { - _organization.value.address = value - _address.value = {} - showAddressModal.value = true - close() - }, - edit: async (addr) => { - _address.value = await call('frappe.client.get', { - doctype: 'Address', - name: addr, - }) - showAddressModal.value = true - }, - } - } else { - return field + section.columns = section.columns.map((column) => { + column.fields = column.fields.map((field) => { + if (field.name === 'address') { + return { + ...field, + create: (value, close) => { + _organization.value.address = value + _address.value = {} + showAddressModal.value = true + close() + }, + edit: async (addr) => { + _address.value = await call('frappe.client.get', { + doctype: 'Address', + name: addr, + }) + showAddressModal.value = true + }, } - }), - ), - } + } else { + return field + } + }) + return column + }) + return section }) } diff --git a/frontend/src/pages/Organization.vue b/frontend/src/pages/Organization.vue index 2a1c7917..777be19b 100644 --- a/frontend/src/pages/Organization.vue +++ b/frontend/src/pages/Organization.vue @@ -124,9 +124,9 @@ { - return { - ...section, - fields: computed(() => - section.fields.map((field) => { - if (field.name === 'address') { - return { - ...field, - create: (value, close) => { - _organization.value.address = value - _address.value = {} - showAddressModal.value = true - close() - }, - edit: async (addr) => { - _address.value = await call('frappe.client.get', { - doctype: 'Address', - name: addr, - }) - showAddressModal.value = true - }, - } - } else { - return field + section.columns = section.columns.map((column) => { + column.fields = column.fields.map((field) => { + if (field.name === 'address') { + return { + ...field, + create: (value, close) => { + _organization.value.address = value + _address.value = {} + showAddressModal.value = true + close() + }, + edit: async (addr) => { + _address.value = await call('frappe.client.get', { + doctype: 'Address', + name: addr, + }) + showAddressModal.value = true + }, } - }), - ), - } + } else { + return field + } + }) + return column + }) + return section }) }