diff --git a/README.md b/README.md index 21503df7..63e007ac 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ **Simplify Sales, Amplify Relationships** -![GitHub release (latest by date)](https://img.shields.io/github/v/release/frappe/crm) +[![GitHub release (latest by date)](https://img.shields.io/github/v/release/frappe/crm)](https://github.com/frappe/crm/releases)
@@ -181,6 +181,7 @@ You need Docker, docker-compose and git setup on your machine. Refer [Docker doc - [Discuss Forum](https://discuss.frappe.io/c/frappe-crm) - [Documentation](https://docs.frappe.io/crm) - [YouTube](https://www.youtube.com/channel/UCn3bV5kx77HsVwtnlCeEi_A) +- [X/Twitter](https://x.com/frappetech)

diff --git a/crm/api/doc.py b/crm/api/doc.py index f242c249..e61d484b 100644 --- a/crm/api/doc.py +++ b/crm/api/doc.py @@ -3,6 +3,7 @@ import json import frappe from frappe import _ from frappe.custom.doctype.property_setter.property_setter import make_property_setter +from frappe.desk.form.assign_to import set_status from frappe.model import no_value_fields from frappe.model.document import get_controller from frappe.utils import make_filter_tuple @@ -660,6 +661,24 @@ def get_fields_meta(doctype, restricted_fieldtypes=None, as_array=False, only_re return fields_meta +@frappe.whitelist() +def remove_assignments(doctype, name, assignees, ignore_permissions=False): + assignees = json.loads(assignees) + + if not assignees: + return + + for assign_to in assignees: + set_status( + doctype, + name, + todo=None, + assign_to=assign_to, + status="Cancelled", + ignore_permissions=ignore_permissions, + ) + +@frappe.whitelist() def get_assigned_users(doctype, name, default_assigned_to=None): assigned_users = frappe.get_all( "ToDo", diff --git a/crm/fcrm/doctype/crm_deal/api.py b/crm/fcrm/doctype/crm_deal/api.py index 5eaf2899..dd5d76df 100644 --- a/crm/fcrm/doctype/crm_deal/api.py +++ b/crm/fcrm/doctype/crm_deal/api.py @@ -13,7 +13,6 @@ def get_deal(name): deal["fields_meta"] = get_fields_meta("CRM Deal") deal["_form_script"] = get_form_script("CRM Deal") - deal["_assign"] = get_assigned_users("CRM Deal", deal.name) return deal diff --git a/crm/fcrm/doctype/crm_deal/crm_deal.json b/crm/fcrm/doctype/crm_deal/crm_deal.json index 67f441c3..bbef186d 100644 --- a/crm/fcrm/doctype/crm_deal/crm_deal.json +++ b/crm/fcrm/doctype/crm_deal/crm_deal.json @@ -11,11 +11,14 @@ "naming_series", "organization", "next_step", - "probability", "column_break_ijan", "status", - "close_date", "deal_owner", + "section_break_jgpm", + "probability", + "deal_value", + "column_break_kpxa", + "close_date", "contacts_tab", "contacts", "contact", @@ -374,12 +377,26 @@ "label": "Net Total", "options": "currency", "read_only": 1 + }, + { + "fieldname": "section_break_jgpm", + "fieldtype": "Section Break" + }, + { + "fieldname": "deal_value", + "fieldtype": "Currency", + "label": "Deal Value", + "options": "currency" + }, + { + "fieldname": "column_break_kpxa", + "fieldtype": "Column Break" } ], "grid_page_length": 50, "index_web_pages_for_search": 1, "links": [], - "modified": "2025-05-12 12:30:55.415282", + "modified": "2025-06-16 11:42:49.413483", "modified_by": "Administrator", "module": "FCRM", "name": "CRM Deal", diff --git a/crm/fcrm/doctype/crm_deal/crm_deal.py b/crm/fcrm/doctype/crm_deal/crm_deal.py index b650301e..b7e4ec83 100644 --- a/crm/fcrm/doctype/crm_deal/crm_deal.py +++ b/crm/fcrm/doctype/crm_deal/crm_deal.py @@ -24,6 +24,7 @@ class CRMDeal(Document): self.assign_agent(self.deal_owner) if self.has_value_changed("status"): add_status_change_log(self) + self.update_close_date() def after_insert(self): if self.deal_owner: @@ -133,6 +134,13 @@ class CRMDeal(Document): if sla: sla.apply(self) + def update_close_date(self): + """ + Update the close date based on the "Won" status. + """ + if self.status == "Won" and not self.close_date: + self.close_date = frappe.utils.nowdate() + @staticmethod def default_list_data(): columns = [ diff --git a/crm/fcrm/doctype/crm_deal_status/crm_deal_status.json b/crm/fcrm/doctype/crm_deal_status/crm_deal_status.json index b0374ca1..ae026c74 100644 --- a/crm/fcrm/doctype/crm_deal_status/crm_deal_status.json +++ b/crm/fcrm/doctype/crm_deal_status/crm_deal_status.json @@ -8,7 +8,8 @@ "field_order": [ "deal_status", "color", - "position" + "position", + "probability" ], "fields": [ { @@ -32,11 +33,17 @@ "fieldtype": "Int", "in_list_view": 1, "label": "Position" + }, + { + "fieldname": "probability", + "fieldtype": "Percent", + "label": "Probability" } ], + "grid_page_length": 50, "index_web_pages_for_search": 1, "links": [], - "modified": "2024-01-19 21:56:44.552134", + "modified": "2025-06-11 13:00:34.518808", "modified_by": "Administrator", "module": "FCRM", "name": "CRM Deal Status", @@ -68,7 +75,8 @@ "write": 1 } ], + "row_format": "Dynamic", "sort_field": "modified", "sort_order": "DESC", "states": [] -} \ No newline at end of file +} diff --git a/crm/fcrm/doctype/crm_lead/api.py b/crm/fcrm/doctype/crm_lead/api.py index 77e4fc58..e826e888 100644 --- a/crm/fcrm/doctype/crm_lead/api.py +++ b/crm/fcrm/doctype/crm_lead/api.py @@ -13,5 +13,4 @@ def get_lead(name): lead["fields_meta"] = get_fields_meta("CRM Lead") lead["_form_script"] = get_form_script("CRM Lead") - lead["_assign"] = get_assigned_users("CRM Lead", lead.name) return lead diff --git a/crm/fcrm/doctype/fcrm_settings/fcrm_settings.json b/crm/fcrm/doctype/fcrm_settings/fcrm_settings.json index 250c8c29..de27e245 100644 --- a/crm/fcrm/doctype/fcrm_settings/fcrm_settings.json +++ b/crm/fcrm/doctype/fcrm_settings/fcrm_settings.json @@ -7,6 +7,7 @@ "field_order": [ "defaults_tab", "restore_defaults", + "enable_forecasting", "branding_tab", "brand_name", "brand_logo", @@ -28,7 +29,7 @@ { "fieldname": "defaults_tab", "fieldtype": "Tab Break", - "label": "Defaults" + "label": "Settings" }, { "fieldname": "branding_tab", @@ -56,12 +57,19 @@ "fieldname": "favicon", "fieldtype": "Attach", "label": "Favicon" + }, + { + "default": "0", + "description": "It will make deal's \"Expected Closure Date\" mandatory to get accurate forecasting insights", + "fieldname": "enable_forecasting", + "fieldtype": "Check", + "label": "Enable Forecasting" } ], "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2025-02-20 12:38:38.088477", + "modified": "2025-06-11 19:12:16.762499", "modified_by": "Administrator", "module": "FCRM", "name": "FCRM Settings", @@ -95,7 +103,8 @@ "share": 1 } ], + "row_format": "Dynamic", "sort_field": "creation", "sort_order": "DESC", "states": [] -} \ No newline at end of file +} diff --git a/crm/fcrm/doctype/fcrm_settings/fcrm_settings.py b/crm/fcrm/doctype/fcrm_settings/fcrm_settings.py index 53c8c77c..f22114f1 100644 --- a/crm/fcrm/doctype/fcrm_settings/fcrm_settings.py +++ b/crm/fcrm/doctype/fcrm_settings/fcrm_settings.py @@ -3,6 +3,7 @@ import frappe from frappe import _ +from frappe.custom.doctype.property_setter.property_setter import delete_property_setter, make_property_setter from frappe.model.document import Document from crm.install import after_install @@ -15,6 +16,7 @@ class FCRMSettings(Document): def validate(self): self.do_not_allow_to_delete_if_standard() + self.setup_forecasting() def do_not_allow_to_delete_if_standard(self): if not self.has_value_changed("dropdown_items"): @@ -29,6 +31,23 @@ class FCRMSettings(Document): return frappe.throw(_("Cannot delete standard items {0}").format(", ".join(deleted_standard_items))) + def setup_forecasting(self): + if self.has_value_changed("enable_forecasting"): + if not self.enable_forecasting: + delete_property_setter( + "CRM Deal", + "reqd", + "close_date", + ) + else: + make_property_setter( + "CRM Deal", + "close_date", + "reqd", + 1 if self.enable_forecasting else 0, + "Check", + ) + def get_standard_dropdown_items(): return [item.get("name1") for item in frappe.get_hooks("standard_dropdown_items")] @@ -57,3 +76,36 @@ def sync_table(key, hook): crm_settings.set(key, items) crm_settings.save() + + +def create_forecasting_script(): + if not frappe.db.exists("CRM Form Script", "Forecasting Script"): + script = get_forecasting_script() + frappe.get_doc( + { + "doctype": "CRM Form Script", + "name": "Forecasting Script", + "dt": "CRM Deal", + "view": "Form", + "script": script, + "enabled": 1, + "is_standard": 1, + } + ).insert() + + +def get_forecasting_script(): + return """class CRMDeal { + async status() { + await this.doc.trigger('updateProbability') + } + async updateProbability() { + let status = await call("frappe.client.get_value", { + doctype: "CRM Deal Status", + fieldname: "probability", + filters: { name: this.doc.status }, + }) + + this.doc.probability = status.probability + } +}""" diff --git a/crm/install.py b/crm/install.py index 5f25e7d0..5ffa5a67 100644 --- a/crm/install.py +++ b/crm/install.py @@ -359,5 +359,8 @@ def add_standard_dropdown_items(): def add_default_scripts(): + from crm.fcrm.doctype.fcrm_settings.fcrm_settings import create_forecasting_script + for doctype in ["CRM Lead", "CRM Deal"]: create_product_details_script(doctype) + create_forecasting_script() diff --git a/crm/locale/main.pot b/crm/locale/main.pot index a18de683..4833e448 100644 --- a/crm/locale/main.pot +++ b/crm/locale/main.pot @@ -7,8 +7,8 @@ msgid "" msgstr "" "Project-Id-Version: Frappe CRM VERSION\n" "Report-Msgid-Bugs-To: shariq@frappe.io\n" -"POT-Creation-Date: 2025-05-25 09:35+0000\n" -"PO-Revision-Date: 2025-05-25 09:35+0000\n" +"POT-Creation-Date: 2025-06-15 09:36+0000\n" +"PO-Revision-Date: 2025-06-15 09:36+0000\n" "Last-Translator: shariq@frappe.io\n" "Language-Team: shariq@frappe.io\n" "MIME-Version: 1.0\n" @@ -25,6 +25,26 @@ msgstr "" msgid "01/04/2024 11:30 PM" msgstr "" +#: frontend/src/utils/index.js:170 +msgid "1 hour ago" +msgstr "" + +#: frontend/src/utils/index.js:166 +msgid "1 minute ago" +msgstr "" + +#: frontend/src/utils/index.js:184 +msgid "1 month ago" +msgstr "" + +#: frontend/src/utils/index.js:180 +msgid "1 week ago" +msgstr "" + +#: frontend/src/utils/index.js:188 +msgid "1 year ago" +msgstr "" + #. Option for the 'No. of Employees' (Select) field in DocType 'CRM Deal' #. Option for the 'No. of Employees' (Select) field in DocType 'CRM Lead' #. Option for the 'No. of Employees' (Select) field in DocType 'CRM @@ -176,8 +196,8 @@ msgstr "" msgid "Actions" msgstr "" -#: frontend/src/pages/Deal.vue:527 frontend/src/pages/Lead.vue:519 -#: frontend/src/pages/MobileDeal.vue:428 frontend/src/pages/MobileLead.vue:331 +#: frontend/src/pages/Deal.vue:540 frontend/src/pages/Lead.vue:529 +#: frontend/src/pages/MobileDeal.vue:443 frontend/src/pages/MobileLead.vue:344 msgid "Activity" msgstr "" @@ -285,9 +305,9 @@ msgstr "" #. Label of the amount (Currency) field in DocType 'CRM Products' #: crm/fcrm/doctype/crm_products/crm_products.json -#: frontend/src/pages/Contact.vue:613 frontend/src/pages/MobileContact.vue:587 -#: frontend/src/pages/MobileOrganization.vue:477 -#: frontend/src/pages/Organization.vue:516 +#: frontend/src/pages/Contact.vue:583 frontend/src/pages/MobileContact.vue:558 +#: frontend/src/pages/MobileOrganization.vue:460 +#: frontend/src/pages/Organization.vue:492 msgid "Amount" msgstr "" @@ -296,6 +316,10 @@ msgstr "" msgid "Amount after discount" msgstr "" +#: frontend/src/data/script.js:50 frontend/src/data/script.js:51 +msgid "An error occurred" +msgstr "" + #. Description of the 'Favicon' (Attach) field in DocType 'FCRM Settings' #: crm/fcrm/doctype/fcrm_settings/fcrm_settings.json msgid "An icon file with .ico extension. Should be 16 x 16 px. Generated using a favicon generator. [favicon-generator.org]" @@ -319,8 +343,8 @@ msgstr "" msgid "Annual Revenue" msgstr "" -#: frontend/src/components/Modals/DealModal.vue:210 -#: frontend/src/components/Modals/LeadModal.vue:150 +#: frontend/src/components/Modals/DealModal.vue:195 +#: frontend/src/components/Modals/LeadModal.vue:137 msgid "Annual Revenue should be a number" msgstr "" @@ -355,12 +379,12 @@ msgstr "" msgid "Are you sure you want to delete this attachment?" msgstr "" -#: frontend/src/pages/Contact.vue:321 frontend/src/pages/MobileContact.vue:292 +#: frontend/src/pages/Contact.vue:310 frontend/src/pages/MobileContact.vue:282 msgid "Are you sure you want to delete this contact?" msgstr "" -#: frontend/src/pages/MobileOrganization.vue:275 -#: frontend/src/pages/Organization.vue:317 +#: frontend/src/pages/MobileOrganization.vue:267 +#: frontend/src/pages/Organization.vue:302 msgid "Are you sure you want to delete this organization?" msgstr "" @@ -417,12 +441,12 @@ msgstr "" msgid "Attach" msgstr "" -#: frontend/src/pages/Deal.vue:112 frontend/src/pages/Lead.vue:162 +#: frontend/src/pages/Deal.vue:127 frontend/src/pages/Lead.vue:177 msgid "Attach a file" msgstr "" -#: frontend/src/pages/Deal.vue:562 frontend/src/pages/Lead.vue:554 -#: frontend/src/pages/MobileDeal.vue:464 frontend/src/pages/MobileLead.vue:367 +#: frontend/src/pages/Deal.vue:575 frontend/src/pages/Lead.vue:564 +#: frontend/src/pages/MobileDeal.vue:479 frontend/src/pages/MobileLead.vue:380 msgid "Attachments" msgstr "" @@ -685,8 +709,8 @@ msgstr "" msgid "Calling..." msgstr "" -#: frontend/src/pages/Deal.vue:547 frontend/src/pages/Lead.vue:539 -#: frontend/src/pages/MobileDeal.vue:448 frontend/src/pages/MobileLead.vue:351 +#: frontend/src/pages/Deal.vue:560 frontend/src/pages/Lead.vue:549 +#: frontend/src/pages/MobileDeal.vue:463 frontend/src/pages/MobileLead.vue:364 msgid "Calls" msgstr "" @@ -709,7 +733,7 @@ msgstr "" msgid "Canceled" msgstr "" -#: crm/fcrm/doctype/fcrm_settings/fcrm_settings.py:30 +#: crm/fcrm/doctype/fcrm_settings/fcrm_settings.py:32 msgid "Cannot delete standard items {0}" msgstr "" @@ -734,15 +758,15 @@ msgstr "" msgid "Change deal status" msgstr "" -#: frontend/src/pages/Contact.vue:41 frontend/src/pages/Lead.vue:86 +#: frontend/src/pages/Contact.vue:41 frontend/src/pages/Lead.vue:101 #: frontend/src/pages/MobileContact.vue:34 #: frontend/src/pages/MobileOrganization.vue:37 #: frontend/src/pages/Organization.vue:41 msgid "Change image" msgstr "" -#: frontend/src/pages/Lead.vue:244 frontend/src/pages/Lead.vue:270 -#: frontend/src/pages/MobileLead.vue:109 frontend/src/pages/MobileLead.vue:136 +#: frontend/src/pages/Lead.vue:260 frontend/src/pages/Lead.vue:286 +#: frontend/src/pages/MobileLead.vue:122 frontend/src/pages/MobileLead.vue:149 msgid "Choose Existing" msgstr "" @@ -785,11 +809,6 @@ msgstr "" msgid "Close" msgstr "" -#. Label of the close_date (Date) field in DocType 'CRM Deal' -#: crm/fcrm/doctype/crm_deal/crm_deal.json -msgid "Close Date" -msgstr "" - #: frontend/src/components/Layouts/AppSidebar.vue:107 msgid "Collapse" msgstr "" @@ -829,8 +848,8 @@ msgstr "" msgid "Comment" msgstr "" -#: frontend/src/pages/Deal.vue:537 frontend/src/pages/Lead.vue:529 -#: frontend/src/pages/MobileDeal.vue:438 frontend/src/pages/MobileLead.vue:341 +#: frontend/src/pages/Deal.vue:550 frontend/src/pages/Lead.vue:539 +#: frontend/src/pages/MobileDeal.vue:453 frontend/src/pages/MobileLead.vue:354 msgid "Comments" msgstr "" @@ -879,7 +898,7 @@ msgstr "" #: crm/fcrm/doctype/crm_contacts/crm_contacts.json #: crm/fcrm/doctype/crm_deal/crm_deal.json #: frontend/src/components/Layouts/AppSidebar.vue:541 -#: frontend/src/pages/Lead.vue:266 frontend/src/pages/MobileLead.vue:132 +#: frontend/src/pages/Lead.vue:282 frontend/src/pages/MobileLead.vue:145 msgid "Contact" msgstr "" @@ -887,7 +906,7 @@ msgstr "" msgid "Contact Already Exists" msgstr "" -#: frontend/src/components/Modals/AboutModal.vue:93 +#: frontend/src/components/Modals/AboutModal.vue:77 msgid "Contact Support" msgstr "" @@ -895,11 +914,11 @@ msgstr "" msgid "Contact Us" msgstr "" -#: frontend/src/pages/Deal.vue:641 frontend/src/pages/MobileDeal.vue:542 +#: frontend/src/pages/Deal.vue:654 frontend/src/pages/MobileDeal.vue:557 msgid "Contact added" msgstr "" -#: frontend/src/pages/Deal.vue:631 frontend/src/pages/MobileDeal.vue:532 +#: frontend/src/pages/Deal.vue:644 frontend/src/pages/MobileDeal.vue:547 msgid "Contact already added" msgstr "" @@ -911,17 +930,16 @@ msgstr "" msgid "Contact not found" msgstr "" -#: frontend/src/pages/Deal.vue:652 frontend/src/pages/MobileDeal.vue:553 +#: frontend/src/pages/Deal.vue:665 frontend/src/pages/MobileDeal.vue:568 msgid "Contact removed" msgstr "" -#: frontend/src/pages/Contact.vue:526 frontend/src/pages/Contact.vue:539 -#: frontend/src/pages/Contact.vue:552 frontend/src/pages/Contact.vue:562 -#: frontend/src/pages/Contact.vue:572 frontend/src/pages/MobileContact.vue:500 -#: frontend/src/pages/MobileContact.vue:513 -#: frontend/src/pages/MobileContact.vue:526 -#: frontend/src/pages/MobileContact.vue:536 -#: frontend/src/pages/MobileContact.vue:546 +#: frontend/src/pages/Contact.vue:508 frontend/src/pages/Contact.vue:521 +#: frontend/src/pages/Contact.vue:534 frontend/src/pages/Contact.vue:544 +#: frontend/src/pages/MobileContact.vue:483 +#: frontend/src/pages/MobileContact.vue:496 +#: frontend/src/pages/MobileContact.vue:509 +#: frontend/src/pages/MobileContact.vue:519 msgid "Contact updated" msgstr "" @@ -930,8 +948,8 @@ msgstr "" #. Label of a shortcut in the Frappe CRM Workspace #: crm/fcrm/doctype/crm_deal/crm_deal.json #: crm/fcrm/workspace/frappe_crm/frappe_crm.json -#: frontend/src/pages/Contact.vue:265 frontend/src/pages/MobileContact.vue:236 -#: frontend/src/pages/MobileOrganization.vue:357 +#: frontend/src/pages/Contact.vue:261 frontend/src/pages/MobileContact.vue:233 +#: frontend/src/pages/MobileOrganization.vue:340 msgid "Contacts" msgstr "" @@ -949,8 +967,8 @@ msgstr "" #: frontend/src/components/Layouts/AppSidebar.vue:369 #: frontend/src/components/ListBulkActions.vue:70 -#: frontend/src/pages/Lead.vue:204 frontend/src/pages/MobileLead.vue:49 -#: frontend/src/pages/MobileLead.vue:95 +#: frontend/src/pages/Lead.vue:220 frontend/src/pages/MobileLead.vue:61 +#: frontend/src/pages/MobileLead.vue:108 msgid "Convert" msgstr "" @@ -961,8 +979,8 @@ msgstr "" #: frontend/src/components/ListBulkActions.vue:62 #: frontend/src/components/ListBulkActions.vue:192 -#: frontend/src/pages/Lead.vue:38 frontend/src/pages/Lead.vue:215 -#: frontend/src/pages/MobileLead.vue:91 +#: frontend/src/pages/Lead.vue:52 frontend/src/pages/Lead.vue:231 +#: frontend/src/pages/MobileLead.vue:104 msgid "Convert to Deal" msgstr "" @@ -975,19 +993,19 @@ msgstr "" msgid "Converted successfully" msgstr "" -#: frontend/src/utils/index.js:218 +#: frontend/src/utils/index.js:342 msgid "Copied to clipboard" msgstr "" -#: frontend/src/components/Modals/AddressModal.vue:100 -#: frontend/src/components/Modals/CallLogModal.vue:85 +#: frontend/src/components/Modals/AddressModal.vue:92 +#: frontend/src/components/Modals/CallLogModal.vue:94 #: frontend/src/components/Modals/ContactModal.vue:37 -#: frontend/src/components/Modals/CreateDocumentModal.vue:87 +#: frontend/src/components/Modals/CreateDocumentModal.vue:88 #: frontend/src/components/Modals/DealModal.vue:63 #: frontend/src/components/Modals/EmailTemplateModal.vue:9 #: frontend/src/components/Modals/LeadModal.vue:34 #: frontend/src/components/Modals/NoteModal.vue:6 -#: frontend/src/components/Modals/OrganizationModal.vue:25 +#: frontend/src/components/Modals/OrganizationModal.vue:38 #: frontend/src/components/Modals/TaskModal.vue:6 #: frontend/src/components/Modals/ViewModal.vue:16 #: frontend/src/pages/CallLogs.vue:11 frontend/src/pages/Contacts.vue:13 @@ -1001,7 +1019,7 @@ msgstr "" msgid "Create" msgstr "" -#: frontend/src/components/Activities/Activities.vue:788 +#: frontend/src/components/Activities/Activities.vue:794 #: frontend/src/components/Activities/ActivityHeader.vue:137 #: frontend/src/components/Activities/ActivityHeader.vue:180 msgid "Create Call Log" @@ -1022,16 +1040,16 @@ msgstr "" #: frontend/src/components/Controls/Link.vue:48 #: frontend/src/components/Modals/EmailTemplateSelectorModal.vue:55 #: frontend/src/components/Modals/WhatsappTemplateSelectorModal.vue:45 -#: frontend/src/components/SidePanelLayout.vue:135 +#: frontend/src/components/SidePanelLayout.vue:138 msgid "Create New" msgstr "" -#: frontend/src/components/Activities/Activities.vue:379 +#: frontend/src/components/Activities/Activities.vue:383 #: frontend/src/components/Modals/NoteModal.vue:15 msgid "Create Note" msgstr "" -#: frontend/src/components/Activities/Activities.vue:394 +#: frontend/src/components/Activities/Activities.vue:398 #: frontend/src/components/Modals/TaskModal.vue:15 msgid "Create Task" msgstr "" @@ -1090,7 +1108,7 @@ msgstr "" msgid "Custom statuses" msgstr "" -#: frontend/src/pages/Deal.vue:432 +#: frontend/src/pages/Deal.vue:445 msgid "Customer created successfully" msgstr "" @@ -1104,8 +1122,8 @@ msgstr "" #: frontend/src/components/Activities/DataFields.vue:6 #: frontend/src/components/Layouts/AppSidebar.vue:569 -#: frontend/src/pages/Deal.vue:542 frontend/src/pages/Lead.vue:534 -#: frontend/src/pages/MobileDeal.vue:443 frontend/src/pages/MobileLead.vue:346 +#: frontend/src/pages/Deal.vue:555 frontend/src/pages/Lead.vue:544 +#: frontend/src/pages/MobileDeal.vue:458 frontend/src/pages/MobileLead.vue:359 msgid "Data" msgstr "" @@ -1140,21 +1158,26 @@ msgstr "" msgid "Deal Statuses" msgstr "" -#: frontend/src/pages/Contact.vue:634 frontend/src/pages/MobileContact.vue:608 -#: frontend/src/pages/MobileOrganization.vue:498 -#: frontend/src/pages/Organization.vue:537 +#. Label of the deal_value (Currency) field in DocType 'CRM Deal' +#: crm/fcrm/doctype/crm_deal/crm_deal.json +msgid "Deal Value" +msgstr "" + +#: frontend/src/pages/Contact.vue:604 frontend/src/pages/MobileContact.vue:579 +#: frontend/src/pages/MobileOrganization.vue:481 +#: frontend/src/pages/Organization.vue:513 msgid "Deal owner" msgstr "" -#: frontend/src/pages/Deal.vue:468 frontend/src/pages/MobileDeal.vue:363 +#: frontend/src/pages/Deal.vue:481 frontend/src/pages/MobileDeal.vue:378 msgid "Deal updated" msgstr "" #. Label of a shortcut in the Frappe CRM Workspace #: crm/fcrm/workspace/frappe_crm/frappe_crm.json -#: frontend/src/pages/Deal.vue:487 frontend/src/pages/MobileContact.vue:320 -#: frontend/src/pages/MobileDeal.vue:382 -#: frontend/src/pages/MobileOrganization.vue:351 +#: frontend/src/pages/Deal.vue:500 frontend/src/pages/MobileContact.vue:310 +#: frontend/src/pages/MobileDeal.vue:397 +#: frontend/src/pages/MobileOrganization.vue:334 msgid "Deals" msgstr "" @@ -1223,11 +1246,6 @@ msgstr "" msgid "Default statuses, custom fields and layouts restored successfully." msgstr "" -#. Label of the defaults_tab (Tab Break) field in DocType 'FCRM Settings' -#: crm/fcrm/doctype/fcrm_settings/fcrm_settings.json -msgid "Defaults" -msgstr "" - #: frontend/src/components/Activities/AttachmentArea.vue:135 #: frontend/src/components/Activities/NoteArea.vue:12 #: frontend/src/components/Activities/TaskArea.vue:55 @@ -1239,14 +1257,14 @@ msgstr "" #: frontend/src/components/ListBulkActions.vue:174 #: frontend/src/components/ViewControls.vue:1161 #: frontend/src/components/ViewControls.vue:1172 -#: frontend/src/pages/Contact.vue:105 frontend/src/pages/Contact.vue:324 +#: frontend/src/pages/Contact.vue:105 frontend/src/pages/Contact.vue:313 #: frontend/src/pages/MobileContact.vue:81 -#: frontend/src/pages/MobileContact.vue:295 -#: frontend/src/pages/MobileDeal.vue:513 +#: frontend/src/pages/MobileContact.vue:285 +#: frontend/src/pages/MobileDeal.vue:528 #: frontend/src/pages/MobileOrganization.vue:72 -#: frontend/src/pages/MobileOrganization.vue:278 +#: frontend/src/pages/MobileOrganization.vue:270 #: frontend/src/pages/Notes.vue:40 frontend/src/pages/Organization.vue:83 -#: frontend/src/pages/Organization.vue:320 frontend/src/pages/Tasks.vue:368 +#: frontend/src/pages/Organization.vue:305 frontend/src/pages/Tasks.vue:368 msgid "Delete" msgstr "" @@ -1268,12 +1286,12 @@ msgstr "" msgid "Delete attachment" msgstr "" -#: frontend/src/pages/Contact.vue:320 frontend/src/pages/MobileContact.vue:291 +#: frontend/src/pages/Contact.vue:309 frontend/src/pages/MobileContact.vue:281 msgid "Delete contact" msgstr "" -#: frontend/src/pages/MobileOrganization.vue:274 -#: frontend/src/pages/Organization.vue:316 +#: frontend/src/pages/MobileOrganization.vue:266 +#: frontend/src/pages/Organization.vue:301 msgid "Delete organization" msgstr "" @@ -1299,9 +1317,9 @@ msgstr "" #. Label of the details (Text Editor) field in DocType 'CRM Lead Source' #: crm/fcrm/doctype/crm_lead/crm_lead.json #: crm/fcrm/doctype/crm_lead_source/crm_lead_source.json -#: frontend/src/pages/MobileContact.vue:315 -#: frontend/src/pages/MobileDeal.vue:422 frontend/src/pages/MobileLead.vue:325 -#: frontend/src/pages/MobileOrganization.vue:346 +#: frontend/src/pages/MobileContact.vue:305 +#: frontend/src/pages/MobileDeal.vue:437 frontend/src/pages/MobileLead.vue:338 +#: frontend/src/pages/MobileOrganization.vue:329 msgid "Details" msgstr "" @@ -1358,11 +1376,11 @@ msgstr "" msgid "Document not found" msgstr "" -#: frontend/src/data/document.js:20 +#: frontend/src/data/document.js:23 msgid "Document updated successfully" msgstr "" -#: frontend/src/components/Modals/AboutModal.vue:78 +#: frontend/src/components/Modals/AboutModal.vue:62 msgid "Documentation" msgstr "" @@ -1456,7 +1474,7 @@ msgstr "" msgid "Edit" msgstr "" -#: frontend/src/components/Modals/CallLogModal.vue:81 +#: frontend/src/components/Modals/CallLogModal.vue:90 msgid "Edit Call Log" msgstr "" @@ -1520,12 +1538,12 @@ msgstr "" #: crm/fcrm/doctype/crm_contacts/crm_contacts.json #: crm/fcrm/doctype/crm_deal/crm_deal.json #: crm/fcrm/doctype/crm_invitation/crm_invitation.json -#: crm/fcrm/doctype/crm_lead/crm_lead.json frontend/src/pages/Contact.vue:624 -#: frontend/src/pages/MobileContact.vue:598 -#: frontend/src/pages/MobileOrganization.vue:488 -#: frontend/src/pages/MobileOrganization.vue:516 -#: frontend/src/pages/Organization.vue:527 -#: frontend/src/pages/Organization.vue:555 +#: crm/fcrm/doctype/crm_lead/crm_lead.json frontend/src/pages/Contact.vue:594 +#: frontend/src/pages/MobileContact.vue:569 +#: frontend/src/pages/MobileOrganization.vue:471 +#: frontend/src/pages/MobileOrganization.vue:499 +#: frontend/src/pages/Organization.vue:503 +#: frontend/src/pages/Organization.vue:531 msgid "Email" msgstr "" @@ -1567,8 +1585,8 @@ msgstr "" msgid "Email template" msgstr "" -#: frontend/src/pages/Deal.vue:532 frontend/src/pages/Lead.vue:524 -#: frontend/src/pages/MobileDeal.vue:433 frontend/src/pages/MobileLead.vue:336 +#: frontend/src/pages/Deal.vue:545 frontend/src/pages/Lead.vue:534 +#: frontend/src/pages/MobileDeal.vue:448 frontend/src/pages/MobileLead.vue:349 msgid "Emails" msgstr "" @@ -1588,6 +1606,11 @@ msgstr "" msgid "Enable" msgstr "" +#. Label of the enable_forecasting (Check) field in DocType 'FCRM Settings' +#: crm/fcrm/doctype/fcrm_settings/fcrm_settings.json +msgid "Enable Forecasting" +msgstr "" + #: frontend/src/components/Settings/emailConfig.js:28 msgid "Enable Incoming" msgstr "" @@ -1636,23 +1659,23 @@ msgstr "" msgid "Equals" msgstr "" -#: frontend/src/pages/Lead.vue:642 +#: frontend/src/pages/Lead.vue:654 msgid "Error converting to deal: {0}" msgstr "" -#: frontend/src/pages/MobileDeal.vue:367 +#: frontend/src/pages/MobileDeal.vue:382 msgid "Error updating deal" msgstr "" -#: frontend/src/pages/Deal.vue:472 +#: frontend/src/pages/Deal.vue:485 msgid "Error updating deal: {0}" msgstr "" -#: frontend/src/data/document.js:23 +#: frontend/src/data/document.js:26 msgid "Error updating document" msgstr "" -#: frontend/src/pages/Lead.vue:464 +#: frontend/src/pages/Lead.vue:474 msgid "Error updating lead" msgstr "" @@ -1714,6 +1737,11 @@ msgstr "" msgid "Expand" msgstr "" +#. Label of the close_date (Date) field in DocType 'CRM Deal' +#: crm/fcrm/doctype/crm_deal/crm_deal.json +msgid "Expected Closure Date" +msgstr "" + #. Option for the 'Status' (Select) field in DocType 'CRM Invitation' #: crm/fcrm/doctype/crm_invitation/crm_invitation.json msgid "Expired" @@ -1759,7 +1787,7 @@ msgstr "" msgid "Failed to create email account, Invalid credentials" msgstr "" -#: frontend/src/data/script.js:97 +#: frontend/src/data/script.js:110 msgid "Failed to load form controller: {0}" msgstr "" @@ -1819,7 +1847,7 @@ msgstr "" msgid "First Name" msgstr "" -#: frontend/src/components/Modals/LeadModal.vue:143 +#: frontend/src/components/Modals/LeadModal.vue:130 msgid "First Name is mandatory" msgstr "" @@ -1926,11 +1954,11 @@ msgstr "" msgid "General" msgstr "" -#: frontend/src/components/Modals/AboutModal.vue:73 +#: frontend/src/components/Modals/AboutModal.vue:57 msgid "GitHub Repository" msgstr "" -#: frontend/src/pages/Deal.vue:98 frontend/src/pages/Lead.vue:148 +#: frontend/src/pages/Deal.vue:113 frontend/src/pages/Lead.vue:163 msgid "Go to website" msgstr "" @@ -2124,6 +2152,7 @@ msgid "Integration Not Enabled" msgstr "" #: frontend/src/components/Settings/Settings.vue:115 +msgctxt "FCRM" msgid "Integrations" msgstr "" @@ -2136,8 +2165,8 @@ msgstr "" msgid "Invalid Account SID or Auth Token." msgstr "" -#: frontend/src/components/Modals/DealModal.vue:219 -#: frontend/src/components/Modals/LeadModal.vue:159 +#: frontend/src/components/Modals/DealModal.vue:207 +#: frontend/src/components/Modals/LeadModal.vue:149 msgid "Invalid Email" msgstr "" @@ -2220,6 +2249,12 @@ msgstr "" msgid "Is Standard" msgstr "" +#. Description of the 'Enable Forecasting' (Check) field in DocType 'FCRM +#. Settings' +#: crm/fcrm/doctype/fcrm_settings/fcrm_settings.json +msgid "It will make deal's \"Expected Closure Date\" mandatory to get accurate forecasting insights" +msgstr "" + #. Label of the json (JSON) field in DocType 'CRM Global Settings' #: crm/fcrm/doctype/crm_global_settings/crm_global_settings.json msgid "JSON" @@ -2307,11 +2342,11 @@ msgstr "" msgid "Last Year" msgstr "" -#: frontend/src/pages/Contact.vue:639 frontend/src/pages/MobileContact.vue:613 -#: frontend/src/pages/MobileOrganization.vue:503 -#: frontend/src/pages/MobileOrganization.vue:531 -#: frontend/src/pages/Organization.vue:542 -#: frontend/src/pages/Organization.vue:570 +#: frontend/src/pages/Contact.vue:609 frontend/src/pages/MobileContact.vue:584 +#: frontend/src/pages/MobileOrganization.vue:486 +#: frontend/src/pages/MobileOrganization.vue:514 +#: frontend/src/pages/Organization.vue:518 +#: frontend/src/pages/Organization.vue:546 msgid "Last modified" msgstr "" @@ -2357,13 +2392,13 @@ msgstr "" msgid "Lead Statuses" msgstr "" -#: frontend/src/pages/Lead.vue:460 frontend/src/pages/MobileLead.vue:266 +#: frontend/src/pages/Lead.vue:470 frontend/src/pages/MobileLead.vue:279 msgid "Lead updated successfully" msgstr "" #. Label of a shortcut in the Frappe CRM Workspace #: crm/fcrm/workspace/frappe_crm/frappe_crm.json -#: frontend/src/pages/Lead.vue:479 frontend/src/pages/MobileLead.vue:285 +#: frontend/src/pages/Lead.vue:489 frontend/src/pages/MobileLead.vue:298 msgid "Leads" msgstr "" @@ -2418,7 +2453,7 @@ msgstr "" #: frontend/src/components/Activities/Activities.vue:22 #: frontend/src/components/Activities/DataFields.vue:35 -#: frontend/src/pages/Deal.vue:176 frontend/src/pages/MobileDeal.vue:110 +#: frontend/src/pages/Deal.vue:192 frontend/src/pages/MobileDeal.vue:123 msgid "Loading..." msgstr "" @@ -2456,14 +2491,14 @@ msgstr "" msgid "Make Public" msgstr "" -#: frontend/src/components/Activities/Activities.vue:792 +#: frontend/src/components/Activities/Activities.vue:798 #: frontend/src/components/Activities/ActivityHeader.vue:142 #: frontend/src/components/Activities/ActivityHeader.vue:185 -#: frontend/src/pages/Deals.vue:512 frontend/src/pages/Leads.vue:535 +#: frontend/src/pages/Deals.vue:504 frontend/src/pages/Leads.vue:531 msgid "Make a Call" msgstr "" -#: frontend/src/pages/Deal.vue:77 frontend/src/pages/Lead.vue:119 +#: frontend/src/pages/Deal.vue:92 frontend/src/pages/Lead.vue:134 msgid "Make a call" msgstr "" @@ -2542,8 +2577,8 @@ msgstr "" msgid "Mobile No" msgstr "" -#: frontend/src/components/Modals/DealModal.vue:215 -#: frontend/src/components/Modals/LeadModal.vue:155 +#: frontend/src/components/Modals/DealModal.vue:203 +#: frontend/src/components/Modals/LeadModal.vue:145 msgid "Mobile No should be a number" msgstr "" @@ -2564,9 +2599,9 @@ msgstr "" msgid "Mobile app installation" msgstr "" -#: frontend/src/pages/Contact.vue:629 frontend/src/pages/MobileContact.vue:603 -#: frontend/src/pages/MobileOrganization.vue:493 -#: frontend/src/pages/Organization.vue:532 +#: frontend/src/pages/Contact.vue:599 frontend/src/pages/MobileContact.vue:574 +#: frontend/src/pages/MobileOrganization.vue:476 +#: frontend/src/pages/Organization.vue:508 msgid "Mobile no" msgstr "" @@ -2603,8 +2638,8 @@ msgstr "" #: crm/fcrm/doctype/fcrm_settings/fcrm_settings.json #: frontend/src/components/Modals/EmailTemplateModal.vue:24 #: frontend/src/components/ViewControls.vue:779 -#: frontend/src/pages/MobileOrganization.vue:511 -#: frontend/src/pages/Organization.vue:550 +#: frontend/src/pages/MobileOrganization.vue:494 +#: frontend/src/pages/Organization.vue:526 msgid "Name" msgstr "" @@ -2631,15 +2666,15 @@ msgstr "" msgid "New" msgstr "" -#: frontend/src/components/Modals/AddressModal.vue:95 +#: frontend/src/components/Modals/AddressModal.vue:87 msgid "New Address" msgstr "" -#: frontend/src/components/Modals/CallLogModal.vue:81 +#: frontend/src/components/Modals/CallLogModal.vue:90 msgid "New Call Log" msgstr "" -#: frontend/src/components/Activities/Activities.vue:389 +#: frontend/src/components/Activities/Activities.vue:393 #: frontend/src/components/Activities/ActivityHeader.vue:27 #: frontend/src/components/Activities/ActivityHeader.vue:132 msgid "New Comment" @@ -2649,7 +2684,7 @@ msgstr "" msgid "New Contact" msgstr "" -#: frontend/src/components/Activities/Activities.vue:384 +#: frontend/src/components/Activities/Activities.vue:388 #: frontend/src/components/Activities/ActivityHeader.vue:17 #: frontend/src/components/Activities/ActivityHeader.vue:127 msgid "New Email" @@ -2661,7 +2696,7 @@ msgstr "" #: frontend/src/components/Activities/ActivityHeader.vue:42 #: frontend/src/components/Activities/ActivityHeader.vue:148 -#: frontend/src/pages/Deals.vue:518 frontend/src/pages/Leads.vue:541 +#: frontend/src/pages/Deals.vue:510 frontend/src/pages/Leads.vue:537 msgid "New Note" msgstr "" @@ -2681,7 +2716,7 @@ msgstr "" #: frontend/src/components/Activities/ActivityHeader.vue:52 #: frontend/src/components/Activities/ActivityHeader.vue:153 -#: frontend/src/pages/Deals.vue:523 frontend/src/pages/Leads.vue:546 +#: frontend/src/pages/Deals.vue:515 frontend/src/pages/Leads.vue:542 msgid "New Task" msgstr "" @@ -2689,15 +2724,15 @@ msgstr "" msgid "New WhatsApp Message" msgstr "" -#: frontend/src/pages/Lead.vue:282 frontend/src/pages/MobileLead.vue:149 +#: frontend/src/pages/Lead.vue:298 frontend/src/pages/MobileLead.vue:162 msgid "New contact will be created based on the person's details" msgstr "" -#: frontend/src/pages/Lead.vue:257 frontend/src/pages/MobileLead.vue:123 +#: frontend/src/pages/Lead.vue:273 frontend/src/pages/MobileLead.vue:136 msgid "New organization will be created based on the data in details section" msgstr "" -#: frontend/src/components/Modals/CreateDocumentModal.vue:83 +#: frontend/src/components/Modals/CreateDocumentModal.vue:84 msgid "New {0}" msgstr "" @@ -2750,11 +2785,11 @@ msgid "No changes made" msgstr "" #: frontend/src/components/Modals/SidePanelModal.vue:51 -#: frontend/src/pages/Deal.vue:261 frontend/src/pages/MobileDeal.vue:198 +#: frontend/src/pages/Deal.vue:277 frontend/src/pages/MobileDeal.vue:211 msgid "No contacts added" msgstr "" -#: frontend/src/pages/Deal.vue:92 frontend/src/pages/Lead.vue:142 +#: frontend/src/pages/Deal.vue:107 frontend/src/pages/Lead.vue:157 msgid "No email set" msgstr "" @@ -2762,7 +2797,7 @@ msgstr "" msgid "No label" msgstr "" -#: frontend/src/pages/Deal.vue:691 +#: frontend/src/pages/Deal.vue:704 msgid "No mobile number set" msgstr "" @@ -2771,11 +2806,11 @@ msgstr "" msgid "No new notifications" msgstr "" -#: frontend/src/pages/Lead.vue:127 +#: frontend/src/pages/Lead.vue:142 msgid "No phone number set" msgstr "" -#: frontend/src/pages/Deal.vue:686 +#: frontend/src/pages/Deal.vue:699 msgid "No primary contact set" msgstr "" @@ -2788,16 +2823,16 @@ msgstr "" msgid "No templates found" msgstr "" -#: frontend/src/pages/MobileOrganization.vue:295 -#: frontend/src/pages/Organization.vue:341 +#: frontend/src/pages/MobileOrganization.vue:287 +#: frontend/src/pages/Organization.vue:326 msgid "No website found" msgstr "" -#: frontend/src/pages/Deal.vue:106 frontend/src/pages/Lead.vue:156 +#: frontend/src/pages/Deal.vue:121 frontend/src/pages/Lead.vue:171 msgid "No website set" msgstr "" -#: frontend/src/components/SidePanelLayout.vue:126 +#: frontend/src/components/SidePanelLayout.vue:129 msgid "No {0} Available" msgstr "" @@ -2859,7 +2894,7 @@ msgstr "" msgid "Not Saved" msgstr "" -#: crm/fcrm/doctype/crm_deal/crm_deal.py:214 +#: crm/fcrm/doctype/crm_deal/crm_deal.py:222 msgid "Not allowed to add contact to Deal" msgstr "" @@ -2867,16 +2902,16 @@ msgstr "" msgid "Not allowed to convert Lead to Deal" msgstr "" -#: crm/fcrm/doctype/crm_deal/crm_deal.py:225 +#: crm/fcrm/doctype/crm_deal/crm_deal.py:233 msgid "Not allowed to remove contact from Deal" msgstr "" -#: crm/fcrm/doctype/crm_deal/crm_deal.py:236 +#: crm/fcrm/doctype/crm_deal/crm_deal.py:244 msgid "Not allowed to set primary contact for Deal" msgstr "" -#: frontend/src/pages/Contact.vue:256 frontend/src/pages/Deal.vue:417 -#: frontend/src/pages/Lead.vue:427 frontend/src/pages/Organization.vue:245 +#: frontend/src/pages/Contact.vue:252 frontend/src/pages/Deal.vue:430 +#: frontend/src/pages/Lead.vue:437 frontend/src/pages/Organization.vue:237 msgid "Not permitted" msgstr "" @@ -2886,8 +2921,8 @@ msgstr "" msgid "Note" msgstr "" -#: frontend/src/pages/Deal.vue:557 frontend/src/pages/Lead.vue:549 -#: frontend/src/pages/MobileDeal.vue:459 frontend/src/pages/MobileLead.vue:362 +#: frontend/src/pages/Deal.vue:570 frontend/src/pages/Lead.vue:559 +#: frontend/src/pages/MobileDeal.vue:474 frontend/src/pages/MobileLead.vue:375 msgid "Notes" msgstr "" @@ -2934,14 +2969,11 @@ msgstr "" msgid "Old Parent" msgstr "" -#: frontend/src/pages/Contact.vue:304 frontend/src/pages/Lead.vue:583 -#: frontend/src/pages/MobileContact.vue:275 -#: frontend/src/pages/MobileOrganization.vue:258 -#: frontend/src/pages/Organization.vue:300 -msgid "Only PNG and JPG images are allowed" +#: frontend/src/utils/index.js:448 +msgid "Only image files are allowed" msgstr "" -#: crm/fcrm/doctype/crm_deal/crm_deal.py:56 +#: crm/fcrm/doctype/crm_deal/crm_deal.py:57 #: crm/fcrm/doctype/crm_telephony_agent/crm_telephony_agent.py:23 msgid "Only one {0} can be set as primary." msgstr "" @@ -2992,13 +3024,13 @@ msgstr "" #: crm/fcrm/doctype/crm_deal/crm_deal.json #: crm/fcrm/doctype/crm_lead/crm_lead.json #: frontend/src/components/Layouts/AppSidebar.vue:542 -#: frontend/src/pages/Contact.vue:608 frontend/src/pages/Lead.vue:240 -#: frontend/src/pages/MobileContact.vue:582 -#: frontend/src/pages/MobileLead.vue:105 -#: frontend/src/pages/MobileOrganization.vue:472 -#: frontend/src/pages/MobileOrganization.vue:526 -#: frontend/src/pages/Organization.vue:511 -#: frontend/src/pages/Organization.vue:565 +#: frontend/src/pages/Contact.vue:578 frontend/src/pages/Lead.vue:256 +#: frontend/src/pages/MobileContact.vue:553 +#: frontend/src/pages/MobileLead.vue:118 +#: frontend/src/pages/MobileOrganization.vue:455 +#: frontend/src/pages/MobileOrganization.vue:509 +#: frontend/src/pages/Organization.vue:487 +#: frontend/src/pages/Organization.vue:541 msgid "Organization" msgstr "" @@ -3021,14 +3053,14 @@ msgstr "" msgid "Organization Name" msgstr "" -#: frontend/src/pages/Deal.vue:60 +#: frontend/src/pages/Deal.vue:75 msgid "Organization logo" msgstr "" #. Label of a shortcut in the Frappe CRM Workspace #: crm/fcrm/workspace/frappe_crm/frappe_crm.json -#: frontend/src/pages/MobileOrganization.vue:212 -#: frontend/src/pages/Organization.vue:254 +#: frontend/src/pages/MobileOrganization.vue:211 +#: frontend/src/pages/Organization.vue:246 msgid "Organizations" msgstr "" @@ -3105,8 +3137,8 @@ msgstr "" #: crm/fcrm/doctype/crm_deal/crm_deal.json #: crm/fcrm/doctype/crm_lead/crm_lead.json #: crm/fcrm/doctype/crm_telephony_agent/crm_telephony_agent.json -#: frontend/src/pages/MobileOrganization.vue:521 -#: frontend/src/pages/Organization.vue:560 +#: frontend/src/pages/MobileOrganization.vue:504 +#: frontend/src/pages/Organization.vue:536 msgid "Phone" msgstr "" @@ -3148,11 +3180,11 @@ msgstr "" msgid "Please enter a valid URL" msgstr "" -#: frontend/src/pages/Lead.vue:619 frontend/src/pages/MobileLead.vue:424 +#: frontend/src/pages/Lead.vue:625 frontend/src/pages/MobileLead.vue:437 msgid "Please select an existing contact" msgstr "" -#: frontend/src/pages/Lead.vue:624 frontend/src/pages/MobileLead.vue:429 +#: frontend/src/pages/Lead.vue:630 frontend/src/pages/MobileLead.vue:442 msgid "Please select an existing organization" msgstr "" @@ -3167,11 +3199,11 @@ msgstr "" msgid "Position" msgstr "" -#: frontend/src/pages/Deal.vue:205 frontend/src/pages/MobileDeal.vue:142 +#: frontend/src/pages/Deal.vue:221 frontend/src/pages/MobileDeal.vue:155 msgid "Primary" msgstr "" -#: frontend/src/pages/Deal.vue:663 frontend/src/pages/MobileDeal.vue:564 +#: frontend/src/pages/Deal.vue:676 frontend/src/pages/MobileDeal.vue:579 msgid "Primary contact set" msgstr "" @@ -3193,7 +3225,9 @@ msgid "Private" msgstr "" #. Label of the probability (Percent) field in DocType 'CRM Deal' +#. Label of the probability (Percent) field in DocType 'CRM Deal Status' #: crm/fcrm/doctype/crm_deal/crm_deal.json +#: crm/fcrm/doctype/crm_deal_status/crm_deal_status.json msgid "Probability" msgstr "" @@ -3342,7 +3376,7 @@ msgstr "" msgid "Reject" msgstr "" -#: frontend/src/pages/Deal.vue:612 +#: frontend/src/pages/Deal.vue:625 msgid "Remove" msgstr "" @@ -3358,7 +3392,7 @@ msgstr "" msgid "Remove column" msgstr "" -#: frontend/src/pages/Contact.vue:47 frontend/src/pages/Lead.vue:92 +#: frontend/src/pages/Contact.vue:47 frontend/src/pages/Lead.vue:107 #: frontend/src/pages/MobileContact.vue:40 #: frontend/src/pages/MobileOrganization.vue:43 #: frontend/src/pages/Organization.vue:47 @@ -3382,7 +3416,7 @@ msgstr "" msgid "Reply All" msgstr "" -#: frontend/src/components/Modals/AboutModal.vue:88 +#: frontend/src/components/Modals/AboutModal.vue:72 msgid "Report an Issue" msgstr "" @@ -3588,8 +3622,8 @@ msgstr "" #: frontend/src/components/Controls/GridFieldsEditorModal.vue:87 #: frontend/src/components/Controls/GridRowFieldsModal.vue:26 #: frontend/src/components/DropdownItem.vue:21 -#: frontend/src/components/Modals/AddressModal.vue:100 -#: frontend/src/components/Modals/CallLogModal.vue:85 +#: frontend/src/components/Modals/AddressModal.vue:92 +#: frontend/src/components/Modals/CallLogModal.vue:94 #: frontend/src/components/Modals/DataFieldsModal.vue:26 #: frontend/src/components/Modals/QuickEntryModal.vue:26 #: frontend/src/components/Modals/SidePanelModal.vue:26 @@ -3646,7 +3680,7 @@ msgstr "" msgid "Send Template" msgstr "" -#: frontend/src/pages/Deal.vue:84 frontend/src/pages/Lead.vue:134 +#: frontend/src/pages/Deal.vue:99 frontend/src/pages/Lead.vue:149 msgid "Send an email" msgstr "" @@ -3676,11 +3710,11 @@ msgstr "" msgid "Set all as public" msgstr "" -#: frontend/src/pages/Deal.vue:71 +#: frontend/src/pages/Deal.vue:86 msgid "Set an organization" msgstr "" -#: frontend/src/pages/Deal.vue:620 frontend/src/pages/MobileDeal.vue:521 +#: frontend/src/pages/Deal.vue:633 frontend/src/pages/MobileDeal.vue:536 msgid "Set as Primary Contact" msgstr "" @@ -3688,7 +3722,7 @@ msgstr "" msgid "Set as default" msgstr "" -#: frontend/src/pages/Lead.vue:113 +#: frontend/src/pages/Lead.vue:128 msgid "Set first name" msgstr "" @@ -3736,6 +3770,8 @@ msgid "" "\t\t and app specific passwords. Read more " msgstr "" +#. Label of the defaults_tab (Tab Break) field in DocType 'FCRM Settings' +#: crm/fcrm/doctype/fcrm_settings/fcrm_settings.json #: frontend/src/components/Layouts/AppSidebar.vue:526 #: frontend/src/components/Settings/Settings.vue:11 #: frontend/src/components/Settings/Settings.vue:81 @@ -3850,10 +3886,10 @@ msgstr "" #: crm/fcrm/doctype/crm_invitation/crm_invitation.json #: crm/fcrm/doctype/crm_lead/crm_lead.json #: crm/fcrm/doctype/crm_lead_status/crm_lead_status.json -#: crm/fcrm/doctype/crm_task/crm_task.json frontend/src/pages/Contact.vue:619 -#: frontend/src/pages/MobileContact.vue:593 -#: frontend/src/pages/MobileOrganization.vue:483 -#: frontend/src/pages/Organization.vue:522 +#: crm/fcrm/doctype/crm_task/crm_task.json frontend/src/pages/Contact.vue:589 +#: frontend/src/pages/MobileContact.vue:564 +#: frontend/src/pages/MobileOrganization.vue:466 +#: frontend/src/pages/Organization.vue:498 msgid "Status" msgstr "" @@ -3864,8 +3900,8 @@ msgstr "" msgid "Status Change Log" msgstr "" -#: frontend/src/components/Modals/DealModal.vue:223 -#: frontend/src/components/Modals/LeadModal.vue:163 +#: frontend/src/components/Modals/DealModal.vue:211 +#: frontend/src/components/Modals/LeadModal.vue:153 msgid "Status is required" msgstr "" @@ -3929,12 +3965,12 @@ msgstr "" msgid "Task" msgstr "" -#: frontend/src/pages/Deal.vue:552 frontend/src/pages/Lead.vue:544 -#: frontend/src/pages/MobileDeal.vue:454 frontend/src/pages/MobileLead.vue:357 +#: frontend/src/pages/Deal.vue:565 frontend/src/pages/Lead.vue:554 +#: frontend/src/pages/MobileDeal.vue:469 frontend/src/pages/MobileLead.vue:370 msgid "Tasks" msgstr "" -#: frontend/src/components/Modals/AboutModal.vue:83 +#: frontend/src/components/Modals/AboutModal.vue:67 msgid "Telegram Channel" msgstr "" @@ -3983,6 +4019,11 @@ msgstr "" msgid "There can only be one default priority in Priorities table" msgstr "" +#: frontend/src/components/Modals/AddressModal.vue:120 +#: frontend/src/components/Modals/CallLogModal.vue:122 +msgid "These fields are required: {0}" +msgstr "" + #: frontend/src/components/Filter.vue:644 msgid "This Month" msgstr "" @@ -4219,7 +4260,7 @@ msgstr "" msgid "Upload" msgstr "" -#: frontend/src/components/Activities/Activities.vue:399 +#: frontend/src/components/Activities/Activities.vue:403 #: frontend/src/components/Activities/ActivityHeader.vue:62 #: frontend/src/components/Activities/ActivityHeader.vue:158 msgid "Upload Attachment" @@ -4237,7 +4278,7 @@ msgstr "" msgid "Upload Video" msgstr "" -#: frontend/src/pages/Contact.vue:42 frontend/src/pages/Lead.vue:87 +#: frontend/src/pages/Contact.vue:42 frontend/src/pages/Lead.vue:102 #: frontend/src/pages/MobileContact.vue:35 #: frontend/src/pages/MobileOrganization.vue:38 #: frontend/src/pages/Organization.vue:42 @@ -4290,7 +4331,7 @@ msgstr "" #: crm/fcrm/doctype/crm_deal/crm_deal.json #: crm/fcrm/doctype/crm_lead/crm_lead.json #: crm/fcrm/doctype/crm_organization/crm_organization.json -#: frontend/src/components/Modals/AboutModal.vue:68 +#: frontend/src/components/Modals/AboutModal.vue:52 msgid "Website" msgstr "" @@ -4320,8 +4361,8 @@ msgstr "" #: crm/fcrm/doctype/crm_notification/crm_notification.json #: frontend/src/components/Layouts/AppSidebar.vue:592 #: frontend/src/components/Settings/Settings.vue:124 -#: frontend/src/pages/Deal.vue:567 frontend/src/pages/Lead.vue:559 -#: frontend/src/pages/MobileDeal.vue:469 frontend/src/pages/MobileLead.vue:372 +#: frontend/src/pages/Deal.vue:580 frontend/src/pages/Lead.vue:569 +#: frontend/src/pages/MobileDeal.vue:484 frontend/src/pages/MobileLead.vue:385 msgid "WhatsApp" msgstr "" @@ -4475,16 +4516,80 @@ msgstr "" msgid "here" msgstr "" +#: frontend/src/utils/index.js:144 +msgid "in 1 hour" +msgstr "" + +#: frontend/src/utils/index.js:140 +msgid "in 1 minute" +msgstr "" + +#: frontend/src/utils/index.js:158 +msgid "in 1 year" +msgstr "" + +#: frontend/src/utils/index.js:110 +msgid "in {0} M" +msgstr "" + +#: frontend/src/utils/index.js:106 +msgid "in {0} d" +msgstr "" + +#: frontend/src/utils/index.js:152 +msgid "in {0} days" +msgstr "" + +#: frontend/src/utils/index.js:100 +msgid "in {0} h" +msgstr "" + +#: frontend/src/utils/index.js:146 +msgid "in {0} hours" +msgstr "" + +#: frontend/src/utils/index.js:98 +msgid "in {0} m" +msgstr "" + +#: frontend/src/utils/index.js:142 +msgid "in {0} minutes" +msgstr "" + +#: frontend/src/utils/index.js:156 +msgid "in {0} months" +msgstr "" + +#: frontend/src/utils/index.js:108 +msgid "in {0} w" +msgstr "" + +#: frontend/src/utils/index.js:154 +msgid "in {0} weeks" +msgstr "" + +#: frontend/src/utils/index.js:112 +msgid "in {0} y" +msgstr "" + +#: frontend/src/utils/index.js:160 +msgid "in {0} years" +msgstr "" + #: frontend/src/components/Settings/InviteMemberPage.vue:17 msgid "john@doe.com" msgstr "" +#: frontend/src/utils/index.js:138 frontend/src/utils/index.js:164 +msgid "just now" +msgstr "" + #. Option for the 'Type' (Select) field in DocType 'CRM View Settings' #: crm/fcrm/doctype/crm_view_settings/crm_view_settings.json msgid "kanban" msgstr "" -#: crm/api/doc.py:38 crm/api/doc.py:156 crm/api/doc.py:501 +#: crm/api/doc.py:39 crm/api/doc.py:157 crm/api/doc.py:502 msgid "label" msgstr "" @@ -4502,6 +4607,10 @@ msgstr "" msgid "next" msgstr "" +#: frontend/src/utils/index.js:96 frontend/src/utils/index.js:116 +msgid "now" +msgstr "" + #. Option for the 'Color' (Select) field in DocType 'CRM Deal Status' #. Option for the 'Color' (Select) field in DocType 'CRM Lead Status' #: crm/fcrm/doctype/crm_deal_status/crm_deal_status.json @@ -4558,6 +4667,10 @@ msgstr "" msgid "to" msgstr "" +#: frontend/src/utils/index.js:104 frontend/src/utils/index.js:150 +msgid "tomorrow" +msgstr "" + #. Option for the 'Color' (Select) field in DocType 'CRM Deal Status' #. Option for the 'Color' (Select) field in DocType 'CRM Lead Status' #: crm/fcrm/doctype/crm_deal_status/crm_deal_status.json @@ -4572,12 +4685,36 @@ msgstr "" msgid "yellow" msgstr "" +#: frontend/src/utils/index.js:176 +msgid "yesterday" +msgstr "" + +#: frontend/src/utils/index.js:128 +msgid "{0} M" +msgstr "" + #: crm/api/todo.py:50 msgid "{0} assigned a {1} {2} to you" msgstr "" -#: frontend/src/pages/Deal.vue:480 frontend/src/pages/Lead.vue:472 -#: frontend/src/pages/MobileDeal.vue:375 frontend/src/pages/MobileLead.vue:278 +#: frontend/src/utils/index.js:124 +msgid "{0} d" +msgstr "" + +#: frontend/src/utils/index.js:178 +msgid "{0} days ago" +msgstr "" + +#: frontend/src/utils/index.js:120 +msgid "{0} h" +msgstr "" + +#: frontend/src/utils/index.js:172 +msgid "{0} hours ago" +msgstr "" + +#: frontend/src/pages/Deal.vue:493 frontend/src/pages/Lead.vue:482 +#: frontend/src/pages/MobileDeal.vue:390 frontend/src/pages/MobileLead.vue:291 msgid "{0} is a required field" msgstr "" @@ -4588,23 +4725,51 @@ msgstr "" msgid "{0} is an invalid email address" msgstr "" -#: frontend/src/data/script.js:232 +#: frontend/src/utils/index.js:118 +msgid "{0} m" +msgstr "" + +#: frontend/src/utils/index.js:168 +msgid "{0} minutes ago" +msgstr "" + +#: frontend/src/utils/index.js:186 +msgid "{0} months ago" +msgstr "" + +#: frontend/src/utils/index.js:126 +msgid "{0} w" +msgstr "" + +#: frontend/src/utils/index.js:182 +msgid "{0} weeks ago" +msgstr "" + +#: frontend/src/utils/index.js:130 +msgid "{0} y" +msgstr "" + +#: frontend/src/utils/index.js:190 +msgid "{0} years ago" +msgstr "" + +#: frontend/src/data/script.js:330 msgid "⚠️ Avoid using \"trigger\" as a field name — it conflicts with the built-in trigger() method." msgstr "" -#: frontend/src/data/script.js:244 +#: frontend/src/data/script.js:342 msgid "⚠️ Method \"{0}\" not found in class." msgstr "" -#: frontend/src/data/script.js:74 +#: frontend/src/data/script.js:87 msgid "⚠️ No class found for doctype: {0}, it is mandatory to have a class for the parent doctype. it can be empty, but it should be present." msgstr "" -#: frontend/src/data/script.js:167 +#: frontend/src/data/script.js:184 msgid "⚠️ No data found for parent field: {0}" msgstr "" -#: frontend/src/data/script.js:175 +#: frontend/src/data/script.js:192 msgid "⚠️ No row found for idx: {0} in parent field: {1}" msgstr "" diff --git a/crm/patches.txt b/crm/patches.txt index 59f37bfb..ee56be84 100644 --- a/crm/patches.txt +++ b/crm/patches.txt @@ -12,4 +12,4 @@ crm.patches.v1_0.create_default_sidebar_fields_layout crm.patches.v1_0.update_deal_quick_entry_layout crm.patches.v1_0.update_layouts_to_new_format crm.patches.v1_0.move_twilio_agent_to_telephony_agent -crm.patches.v1_0.create_default_scripts \ No newline at end of file +crm.patches.v1_0.create_default_scripts # 13-06-2025 \ No newline at end of file diff --git a/crm/www/crm.py b/crm/www/crm.py index 94f8142a..070372dd 100644 --- a/crm/www/crm.py +++ b/crm/www/crm.py @@ -45,7 +45,6 @@ def get_boot(): "user": frappe.db.get_value("User", frappe.session.user, "time_zone") or get_system_timezone(), }, - "app_version": get_app_version(), } ) @@ -54,25 +53,6 @@ def get_default_route(): return "/crm" -def get_app_version(): - app = "crm" - branch = run_git_command(f"cd ../apps/{app} && git rev-parse --abbrev-ref HEAD") - commit = run_git_command(f"git -C ../apps/{app} rev-parse --short=7 HEAD") - tag = run_git_command(f"git -C ../apps/{app} describe --tags --abbrev=0") - dirty = run_git_command(f"git -C ../apps/{app} diff --quiet || echo 'dirty'") == "dirty" - commit_date = run_git_command(f"git -C ../apps/{app} log -1 --format=%cd") - commit_message = run_git_command(f"git -C ../apps/{app} log -1 --pretty=%B") - - return { - "branch": branch, - "commit": commit, - "commit_date": commit_date, - "commit_message": commit_message, - "tag": tag, - "dirty": dirty, - } - - def run_git_command(command): try: with open(os.devnull, "wb") as null_stream: diff --git a/frontend/components.d.ts b/frontend/components.d.ts index 9143acde..09597c6d 100644 --- a/frontend/components.d.ts +++ b/frontend/components.d.ts @@ -155,10 +155,7 @@ declare module 'vue' { ListIcon: typeof import('./src/components/Icons/ListIcon.vue')['default'] ListRows: typeof import('./src/components/ListViews/ListRows.vue')['default'] LoadingIndicator: typeof import('./src/components/Icons/LoadingIndicator.vue')['default'] - LucideCalendar: typeof import('~icons/lucide/calendar')['default'] - LucideInfo: typeof import('~icons/lucide/info')['default'] LucidePlus: typeof import('~icons/lucide/plus')['default'] - LucideSearch: typeof import('~icons/lucide/search')['default'] MarkAsDoneIcon: typeof import('./src/components/Icons/MarkAsDoneIcon.vue')['default'] MaximizeIcon: typeof import('./src/components/Icons/MaximizeIcon.vue')['default'] MenuIcon: typeof import('./src/components/Icons/MenuIcon.vue')['default'] diff --git a/frontend/package.json b/frontend/package.json index 40e1f47a..51a6ff41 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -13,7 +13,7 @@ "@tiptap/extension-paragraph": "^2.12.0", "@twilio/voice-sdk": "^2.10.2", "@vueuse/integrations": "^10.3.0", - "frappe-ui": "^0.1.145", + "frappe-ui": "^0.1.156", "gemoji": "^8.1.0", "lodash": "^4.17.21", "mime": "^4.0.1", diff --git a/frontend/src/components/Activities/Activities.vue b/frontend/src/components/Activities/Activities.vue index 847667a7..1c420e04 100644 --- a/frontend/src/components/Activities/Activities.vue +++ b/frontend/src/components/Activities/Activities.vue @@ -250,14 +250,14 @@ {{ __(activity.type) }} {{ __(activity.data.field_label) }} {{ __(activity.value) }}
{{ __('to') }}
{{ __(activity.data.field_label) }} @@ -320,7 +320,7 @@ {{ startCase(__(activity.type)) }}
{{ __('to') }}
- +
{ ) }) -defineExpose({ emailBox, all_activities }) +defineExpose({ emailBox, all_activities, changeTabTo }) diff --git a/frontend/src/components/Activities/AllModals.vue b/frontend/src/components/Activities/AllModals.vue index 9b312398..e4f7498b 100644 --- a/frontend/src/components/Activities/AllModals.vue +++ b/frontend/src/components/Activities/AllModals.vue @@ -16,8 +16,9 @@ @after="redirect('notes')" /> @@ -91,10 +92,8 @@ function createCallLog() { let doctype = props.doctype let docname = props.doc.data?.name callLog.value = { - data: { - reference_doctype: doctype, - reference_docname: docname, - }, + reference_doctype: doctype, + reference_docname: docname, } showCallLogModal.value = true } diff --git a/frontend/src/components/Activities/CallArea.vue b/frontend/src/components/Activities/CallArea.vue index ba54557e..d7771879 100644 --- a/frontend/src/components/Activities/CallArea.vue +++ b/frontend/src/components/Activities/CallArea.vue @@ -97,7 +97,11 @@ v-model:callLogModal="showCallLogModal" v-model:callLog="callLog" /> - +
diff --git a/frontend/src/components/ListViews/LeadsListView.vue b/frontend/src/components/ListViews/LeadsListView.vue index 401094ef..a45e9e7d 100644 --- a/frontend/src/components/ListViews/LeadsListView.vue +++ b/frontend/src/components/ListViews/LeadsListView.vue @@ -72,15 +72,6 @@ size="sm" />
-
- -

Frappe CRM

-
-
- {{ appVersion.branch != 'main' ? appVersion.branch : '' }} - - -
- - - - -

@@ -95,6 +79,4 @@ let links = [ icon: LucideHeadset, }, ] - -let appVersion = window.app_version diff --git a/frontend/src/components/Modals/AddressModal.vue b/frontend/src/components/Modals/AddressModal.vue index b9f97750..e7c4543f 100644 --- a/frontend/src/components/Modals/AddressModal.vue +++ b/frontend/src/components/Modals/AddressModal.vue @@ -15,15 +15,23 @@ class="w-7" @click="openQuickEntryModal" > - +
-
- +
+
@@ -41,24 +49,24 @@
- diff --git a/frontend/src/components/Modals/ContactModal.vue b/frontend/src/components/Modals/ContactModal.vue index 840fa14f..25a0c022 100644 --- a/frontend/src/components/Modals/ContactModal.vue +++ b/frontend/src/components/Modals/ContactModal.vue @@ -15,17 +15,21 @@ class="w-7" @click="openQuickEntryModal" > - + @@ -49,9 +53,16 @@ import FieldLayout from '@/components/FieldLayout/FieldLayout.vue' import EditIcon from '@/components/Icons/EditIcon.vue' import { usersStore } from '@/stores/users' import { isMobileView } from '@/composables/settings' +import { + showQuickEntryModal, + quickEntryProps, + showAddressModal, + addressProps, +} from '@/composables/modals' +import { useDocument } from '@/data/document' import { capture } from '@/telemetry' import { call, createResource } from 'frappe-ui' -import { ref, nextTick, watch } from 'vue' +import { ref, nextTick, onMounted } from 'vue' import { useRouter } from 'vue-router' const props = defineProps({ @@ -68,8 +79,6 @@ const props = defineProps({ }, }) -const emit = defineEmits(['openAddressModal']) - const { isManager } = usersStore() const router = useRouter() @@ -77,23 +86,23 @@ const show = defineModel() const loading = ref(false) -let _contact = ref({}) +const { document: _contact } = useDocument('Contact') async function createContact() { - if (_contact.value.email_id) { - _contact.value.email_ids = [{ email_id: _contact.value.email_id }] - delete _contact.value.email_id + if (_contact.doc.email_id) { + _contact.doc.email_ids = [{ email_id: _contact.doc.email_id }] + delete _contact.doc.email_id } - if (_contact.value.mobile_no) { - _contact.value.phone_nos = [{ phone: _contact.value.mobile_no }] - delete _contact.value.mobile_no + if (_contact.doc.mobile_no) { + _contact.doc.phone_nos = [{ phone: _contact.doc.mobile_no }] + delete _contact.doc.mobile_no } const doc = await call('frappe.client.insert', { doc: { doctype: 'Contact', - ..._contact.value, + ..._contact.doc, }, }) if (doc.name) { @@ -130,17 +139,13 @@ const tabs = createResource({ field.read_only = false } else if (field.fieldname == 'address') { field.create = (value, close) => { - _contact.value.address = value - emit('openAddressModal') - show.value = false + _contact.doc.address = value + openAddressModal() close() } - field.edit = (address) => { - emit('openAddressModal', address) - show.value = false - } + field.edit = (address) => openAddressModal(address) } else if (field.fieldtype === 'Table') { - _contact.value[field.fieldname] = [] + _contact.doc[field.fieldname] = [] } }) }) @@ -149,20 +154,23 @@ const tabs = createResource({ }, }) -watch( - () => show.value, - (value) => { - if (!value) return - nextTick(() => { - _contact.value = { ...props.contact.data } - }) - }, -) - -const showQuickEntryModal = defineModel('showQuickEntryModal') +onMounted(() => { + _contact.doc = {} + Object.assign(_contact.doc, props.contact.data || props.contact) +}) function openQuickEntryModal() { showQuickEntryModal.value = true + quickEntryProps.value = { doctype: 'Contact' } + nextTick(() => (show.value = false)) +} + +function openAddressModal(_address) { + showAddressModal.value = true + addressProps.value = { + doctype: 'Address', + address: _address, + } nextTick(() => (show.value = false)) } diff --git a/frontend/src/components/Modals/CreateDocumentModal.vue b/frontend/src/components/Modals/CreateDocumentModal.vue index 0c3e5eb1..be5bbf01 100644 --- a/frontend/src/components/Modals/CreateDocumentModal.vue +++ b/frontend/src/components/Modals/CreateDocumentModal.vue @@ -15,10 +15,14 @@ class="w-7" @click="openQuickEntryModal" > - + @@ -48,6 +52,7 @@ import FieldLayout from '@/components/FieldLayout/FieldLayout.vue' import EditIcon from '@/components/Icons/EditIcon.vue' import { usersStore } from '@/stores/users' import { isMobileView } from '@/composables/settings' +import { showQuickEntryModal, quickEntryProps } from '@/composables/modals' import { FeatherIcon, createResource, ErrorMessage, call } from 'frappe-ui' import { ref, nextTick, watch, computed } from 'vue' @@ -62,7 +67,7 @@ const props = defineProps({ }, }) -const emit = defineEmits(['showQuickEntryModal', 'callback']) +const emit = defineEmits(['callback']) const { isManager } = usersStore() @@ -139,9 +144,8 @@ watch( ) function openQuickEntryModal() { - emit('showQuickEntryModal', props.doctype) - nextTick(() => { - show.value = false - }) + showQuickEntryModal.value = true + quickEntryProps.value = { doctype: props.doctype } + nextTick(() => (show.value = false)) } diff --git a/frontend/src/components/Modals/DealModal.vue b/frontend/src/components/Modals/DealModal.vue index 7bd0b592..8bf9498b 100644 --- a/frontend/src/components/Modals/DealModal.vue +++ b/frontend/src/components/Modals/DealModal.vue @@ -15,10 +15,14 @@ class="w-7" @click="openQuickEntryModal" > - + @@ -50,7 +54,7 @@ ref="fieldLayoutRef" v-if="tabs.data?.length" :tabs="tabs.data" - :data="deal" + :data="deal.doc" doctype="CRM Deal" /> @@ -76,9 +80,11 @@ import FieldLayout from '@/components/FieldLayout/FieldLayout.vue' import { usersStore } from '@/stores/users' import { statusesStore } from '@/stores/statuses' import { isMobileView } from '@/composables/settings' +import { showQuickEntryModal, quickEntryProps } from '@/composables/modals' +import { useDocument } from '@/data/document' import { capture } from '@/telemetry' import { Switch, createResource } from 'frappe-ui' -import { computed, ref, reactive, onMounted, nextTick, watch } from 'vue' +import { computed, ref, onMounted, nextTick, watch } from 'vue' import { useRouter } from 'vue-router' const props = defineProps({ @@ -92,24 +98,7 @@ const show = defineModel() const router = useRouter() const error = ref(null) -const deal = reactive({ - organization: '', - organization_name: '', - website: '', - no_of_employees: '', - territory: '', - annual_revenue: '', - industry: '', - contact: '', - salutation: '', - first_name: '', - last_name: '', - email: '', - mobile_no: '', - gender: '', - status: '', - deal_owner: '', -}) +const { document: deal, triggerOnChange } = useDocument('CRM Deal') const hasOrganizationSections = ref(true) const hasContactSections = ref(true) @@ -165,11 +154,11 @@ const tabs = createResource({ if (field.fieldname == 'status') { field.fieldtype = 'Select' field.options = dealStatuses.value - field.prefix = getDealStatus(deal.status).color + field.prefix = getDealStatus(deal.doc.status).color } if (field.fieldtype === 'Table') { - deal[field.fieldname] = [] + deal.doc[field.fieldname] = [] } }) }) @@ -179,47 +168,50 @@ const tabs = createResource({ }) const dealStatuses = computed(() => { - let statuses = statusOptions('deal') - if (!deal.status) { - deal.status = statuses[0].value + let statuses = statusOptions('deal', null, [], triggerOnChange) + if (!deal.doc.status) { + deal.doc.status = statuses[0].value } return statuses }) function createDeal() { - if (deal.website && !deal.website.startsWith('http')) { - deal.website = 'https://' + deal.website + if (deal.doc.website && !deal.doc.website.startsWith('http')) { + deal.doc.website = 'https://' + deal.doc.website } if (chooseExistingContact.value) { - deal['first_name'] = null - deal['last_name'] = null - deal['email'] = null - deal['mobile_no'] = null - } else deal['contact'] = null + deal.doc['first_name'] = null + deal.doc['last_name'] = null + deal.doc['email'] = null + deal.doc['mobile_no'] = null + } else deal.doc['contact'] = null createResource({ url: 'crm.fcrm.doctype.crm_deal.crm_deal.create_deal', - params: { args: deal }, + params: { args: deal.doc }, auto: true, validate() { error.value = null - if (deal.annual_revenue) { - if (typeof deal.annual_revenue === 'string') { - deal.annual_revenue = deal.annual_revenue.replace(/,/g, '') - } else if (isNaN(deal.annual_revenue)) { + if (deal.doc.annual_revenue) { + if (typeof deal.doc.annual_revenue === 'string') { + deal.doc.annual_revenue = deal.doc.annual_revenue.replace(/,/g, '') + } else if (isNaN(deal.doc.annual_revenue)) { error.value = __('Annual Revenue should be a number') return error.value } } - if (deal.mobile_no && isNaN(deal.mobile_no.replace(/[-+() ]/g, ''))) { + if ( + deal.doc.mobile_no && + isNaN(deal.doc.mobile_no.replace(/[-+() ]/g, '')) + ) { error.value = __('Mobile No should be a number') return error.value } - if (deal.email && !deal.email.includes('@')) { + if (deal.doc.email && !deal.doc.email.includes('@')) { error.value = __('Invalid Email') return error.value } - if (!deal.status) { + if (!deal.doc.status) { error.value = __('Status is required') return error.value } @@ -242,22 +234,21 @@ function createDeal() { }) } -const showQuickEntryModal = defineModel('quickEntry') - function openQuickEntryModal() { showQuickEntryModal.value = true - nextTick(() => { - show.value = false - }) + quickEntryProps.value = { doctype: 'CRM Deal' } + nextTick(() => (show.value = false)) } onMounted(() => { - Object.assign(deal, props.defaults) - if (!deal.deal_owner) { - deal.deal_owner = getUser().name + deal.doc = { no_of_employees: '1-10' } + Object.assign(deal.doc, props.defaults) + + if (!deal.doc.deal_owner) { + deal.doc.deal_owner = getUser().name } - if (!deal.status && dealStatuses.value[0].value) { - deal.status = dealStatuses.value[0].value + if (!deal.doc.status && dealStatuses.value[0].value) { + deal.doc.status = dealStatuses.value[0].value } }) diff --git a/frontend/src/components/Modals/GlobalModals.vue b/frontend/src/components/Modals/GlobalModals.vue index d108e81f..310b3d5d 100644 --- a/frontend/src/components/Modals/GlobalModals.vue +++ b/frontend/src/components/Modals/GlobalModals.vue @@ -4,19 +4,24 @@ v-model="showCreateDocumentModal" :doctype="createDocumentDoctype" :data="createDocumentData" - @showQuickEntryModal="(dt) => openQuickEntryModal(dt)" @callback="(data) => createDocumentCallback(data)" /> + diff --git a/frontend/src/components/Modals/LeadModal.vue b/frontend/src/components/Modals/LeadModal.vue index 0950b401..d82dbe52 100644 --- a/frontend/src/components/Modals/LeadModal.vue +++ b/frontend/src/components/Modals/LeadModal.vue @@ -15,15 +15,19 @@ class="w-7" @click="openQuickEntryModal" > - +
- +
@@ -48,10 +52,12 @@ import { usersStore } from '@/stores/users' import { statusesStore } from '@/stores/statuses' import { sessionStore } from '@/stores/session' import { isMobileView } from '@/composables/settings' +import { showQuickEntryModal, quickEntryProps } from '@/composables/modals' import { capture } from '@/telemetry' import { createResource } from 'frappe-ui' import { useOnboarding } from 'frappe-ui/frappe' -import { computed, onMounted, ref, reactive, nextTick } from 'vue' +import { useDocument } from '@/data/document' +import { computed, onMounted, ref, nextTick } from 'vue' import { useRouter } from 'vue-router' const props = defineProps({ @@ -68,6 +74,16 @@ const router = useRouter() const error = ref(null) const isLeadCreating = ref(false) +const { document: lead, triggerOnChange } = useDocument('CRM Lead') + +const leadStatuses = computed(() => { + let statuses = statusOptions('lead', null, [], triggerOnChange) + if (!lead.doc.status) { + lead.doc.status = statuses?.[0]?.value + } + return statuses +}) + const tabs = createResource({ url: 'crm.fcrm.doctype.crm_fields_layout.crm_fields_layout.get_fields_layout', cache: ['QuickEntry', 'CRM Lead'], @@ -81,11 +97,11 @@ const tabs = createResource({ if (field.fieldname == 'status') { field.fieldtype = 'Select' field.options = leadStatuses.value - field.prefix = getLeadStatus(lead.status).color + field.prefix = getLeadStatus(lead.doc.status).color } if (field.fieldtype === 'Table') { - lead[field.fieldname] = [] + lead.doc[field.fieldname] = [] } }) }) @@ -94,23 +110,6 @@ const tabs = createResource({ }, }) -const lead = reactive({ - salutation: '', - first_name: '', - last_name: '', - email: '', - mobile_no: '', - gender: '', - organization: '', - website: '', - no_of_employees: '', - territory: '', - annual_revenue: '', - industry: '', - status: '', - lead_owner: '', -}) - const createLead = createResource({ url: 'frappe.client.insert', makeParams(values) { @@ -123,43 +122,38 @@ const createLead = createResource({ }, }) -const leadStatuses = computed(() => { - let statuses = statusOptions('lead') - if (!lead.status) { - lead.status = statuses?.[0]?.value - } - return statuses -}) - function createNewLead() { - if (lead.website && !lead.website.startsWith('http')) { - lead.website = 'https://' + lead.website + if (lead.doc.website && !lead.doc.website.startsWith('http')) { + lead.doc.website = 'https://' + lead.doc.website } - createLead.submit(lead, { + createLead.submit(lead.doc, { validate() { error.value = null - if (!lead.first_name) { + if (!lead.doc.first_name) { error.value = __('First Name is mandatory') return error.value } - if (lead.annual_revenue) { - if (typeof lead.annual_revenue === 'string') { - lead.annual_revenue = lead.annual_revenue.replace(/,/g, '') - } else if (isNaN(lead.annual_revenue)) { + if (lead.doc.annual_revenue) { + if (typeof lead.doc.annual_revenue === 'string') { + lead.doc.annual_revenue = lead.doc.annual_revenue.replace(/,/g, '') + } else if (isNaN(lead.doc.annual_revenue)) { error.value = __('Annual Revenue should be a number') return error.value } } - if (lead.mobile_no && isNaN(lead.mobile_no.replace(/[-+() ]/g, ''))) { + if ( + lead.doc.mobile_no && + isNaN(lead.doc.mobile_no.replace(/[-+() ]/g, '')) + ) { error.value = __('Mobile No should be a number') return error.value } - if (lead.email && !lead.email.includes('@')) { + if (lead.doc.email && !lead.doc.email.includes('@')) { error.value = __('Invalid Email') return error.value } - if (!lead.status) { + if (!lead.doc.status) { error.value = __('Status is required') return error.value } @@ -185,22 +179,21 @@ function createNewLead() { }) } -const showQuickEntryModal = defineModel('quickEntry') - function openQuickEntryModal() { showQuickEntryModal.value = true - nextTick(() => { - show.value = false - }) + quickEntryProps.value = { doctype: 'CRM Lead' } + nextTick(() => (show.value = false)) } onMounted(() => { - Object.assign(lead, props.defaults) - if (!lead.lead_owner) { - lead.lead_owner = getUser().name + lead.doc = { no_of_employees: '1-10' } + Object.assign(lead.doc, props.defaults) + + if (!lead.doc?.lead_owner) { + lead.doc.lead_owner = getUser().name } - if (!lead.status && leadStatuses.value[0]?.value) { - lead.status = leadStatuses.value[0].value + if (!lead.doc?.status && leadStatuses.value[0]?.value) { + lead.doc.status = leadStatuses.value[0].value } }) diff --git a/frontend/src/components/Modals/OrganizationModal.vue b/frontend/src/components/Modals/OrganizationModal.vue index 40105697..d038011c 100644 --- a/frontend/src/components/Modals/OrganizationModal.vue +++ b/frontend/src/components/Modals/OrganizationModal.vue @@ -9,20 +9,40 @@
-
- +
-
@@ -34,56 +54,59 @@ import FieldLayout from '@/components/FieldLayout/FieldLayout.vue' import EditIcon from '@/components/Icons/EditIcon.vue' import { usersStore } from '@/stores/users' import { isMobileView } from '@/composables/settings' +import { + showQuickEntryModal, + quickEntryProps, + showAddressModal, + addressProps, +} from '@/composables/modals' +import { useDocument } from '@/data/document' import { capture } from '@/telemetry' import { call, FeatherIcon, createResource } from 'frappe-ui' -import { ref, nextTick, watch } from 'vue' +import { ref, nextTick, onMounted } from 'vue' import { useRouter } from 'vue-router' const props = defineProps({ + data: { + type: Object, + default: () => ({}), + }, options: { type: Object, default: { redirect: true, - afterInsert: () => { }, + afterInsert: () => {}, }, }, }) -const emit = defineEmits(['openAddressModal']) - const { isManager } = usersStore() const router = useRouter() const show = defineModel() -const organization = defineModel('organization') const loading = ref(false) -const title = ref(null) - -let _organization = ref({ - organization_name: '', - website: '', - annual_revenue: '', - no_of_employees: '1-10', - industry: '', -}) - -let doc = ref({}) const error = ref(null) +const { document: organization } = useDocument('CRM Organization') + async function createOrganization() { - const doc = await call('frappe.client.insert', { - doc: { - doctype: 'CRM Organization', - ..._organization.value, + const doc = await call( + 'frappe.client.insert', + { + doc: { + doctype: 'CRM Organization', + ...organization.doc, + }, }, - }, { - onError: (err) => { - if (err.error.exc_type == 'ValidationError') { - error.value = err.error?.messages?.[0] - } - } - }) + { + onError: (err) => { + if (err.error.exc_type == 'ValidationError') { + error.value = err.error?.messages?.[0] + } + }, + }, + ) loading.value = false if (doc.name) { capture('organization_created') @@ -97,8 +120,6 @@ function handleOrganizationUpdate(doc) { name: 'Organization', params: { organizationId: doc.name }, }) - } else { - organization.value?.reload?.() } show.value = false props.options.afterInsert && props.options.afterInsert(doc) @@ -116,17 +137,13 @@ const tabs = createResource({ column.fields.forEach((field) => { if (field.fieldname == 'address') { field.create = (value, close) => { - _organization.value.address = value - emit('openAddressModal') - show.value = false + organization.doc.address = value + openAddressModal() close() } - field.edit = (address) => { - emit('openAddressModal', address) - show.value = false - } + field.edit = (address) => openAddressModal(address) } else if (field.fieldtype === 'Table') { - _organization.value[field.fieldname] = [] + organization.doc[field.fieldname] = [] } }) }) @@ -135,23 +152,23 @@ const tabs = createResource({ }, }) -watch( - () => show.value, - (value) => { - if (!value) return - nextTick(() => { - // TODO: Issue with FormControl - // title.value.el.focus() - doc.value = organization.value?.doc || organization.value || {} - _organization.value = { ...doc.value } - }) - }, -) - -const showQuickEntryModal = defineModel('showQuickEntryModal') +onMounted(() => { + organization.doc = { no_of_employees: '1-10' } + Object.assign(organization.doc, props.data) +}) function openQuickEntryModal() { showQuickEntryModal.value = true + quickEntryProps.value = { doctype: 'CRM Organization' } + nextTick(() => (show.value = false)) +} + +function openAddressModal(_address) { + showAddressModal.value = true + addressProps.value = { + doctype: 'Address', + address: _address, + } nextTick(() => (show.value = false)) } diff --git a/frontend/src/components/Settings/ProfileImageEditor.vue b/frontend/src/components/Settings/ProfileImageEditor.vue index 8703561b..26a14e88 100644 --- a/frontend/src/components/Settings/ProfileImageEditor.vue +++ b/frontend/src/components/Settings/ProfileImageEditor.vue @@ -1,7 +1,7 @@ @@ -44,18 +46,21 @@ >
- {{ __(field.label) }} - + {{ __(field.label) }} +
+
* + * +
@@ -245,6 +250,7 @@ " :placeholder="field.placeholder" placement="left-start" + :hideIcon="true" @change="(v) => fieldChange(v, field)" />
@@ -258,6 +264,7 @@ :formatter="(date) => getFormat(date, '', true)" :placeholder="field.placeholder" placement="left-start" + :hideIcon="true" @change="(v) => fieldChange(v, field)" /> @@ -417,13 +424,13 @@ const props = defineProps({ }, }) +const emit = defineEmits(['afterFieldChange', 'reload']) + const { getFormattedPercent, getFormattedFloat, getFormattedCurrency } = getMeta(props.doctype) const { isManager, getUser } = usersStore() -const emit = defineEmits(['reload']) - const showSidePanelModal = ref(false) let document = { doc: {} } @@ -489,11 +496,15 @@ function parsedField(field) { async function fieldChange(value, df) { if (props.preview) return - document.doc[df.fieldname] = value + await triggerOnChange(df.fieldname, value) - await triggerOnChange(df.fieldname) - - document.save.submit() + document.save.submit(null, { + onSuccess: () => { + emit('afterFieldChange', { + [df.fieldname]: value, + }) + }, + }) } function parsedSection(section, editButtonAdded) { diff --git a/frontend/src/components/SidePanelLayoutEditor.vue b/frontend/src/components/SidePanelLayoutEditor.vue index 3b7ab0a3..09493c61 100644 --- a/frontend/src/components/SidePanelLayoutEditor.vue +++ b/frontend/src/components/SidePanelLayoutEditor.vue @@ -41,7 +41,9 @@ variant="ghost" @click="section.editingLabel = true" > - +