├── .devcontainer
├── devcontainer.json
└── docker-compose.yml
├── .github
├── crm_logo.png
├── helper
│ └── update_pot_file.sh
├── logo.png
├── logo.svg
├── screenshots
│ ├── CallLog.png
│ ├── CallUI.png
│ ├── EmailTemplate.png
│ ├── FrappeCRMHeroImage.png
│ ├── LeadList.png
│ ├── LeadPage.png
│ └── MainDealPage.png
└── workflows
│ ├── builds.yml
│ ├── ci.yml
│ ├── generate-pot-file.yml
│ ├── on_release.yml
│ └── release_notes.yml
├── .gitignore
├── .gitmodules
├── .mergify.yml
├── .releaserc
├── .vscode
├── launch.json
└── settings.json
├── LICENSE
├── README.md
├── crm
├── __init__.py
├── api
│ ├── __init__.py
│ ├── activities.py
│ ├── auth.py
│ ├── comment.py
│ ├── contact.py
│ ├── demo.py
│ ├── doc.py
│ ├── notifications.py
│ ├── onboarding.py
│ ├── session.py
│ ├── settings.py
│ ├── todo.py
│ ├── views.py
│ └── whatsapp.py
├── config
│ └── __init__.py
├── crowdin.yml
├── fcrm
│ ├── __init__.py
│ ├── doctype
│ │ ├── __init__.py
│ │ ├── crm_call_log
│ │ │ ├── __init__.py
│ │ │ ├── crm_call_log.js
│ │ │ ├── crm_call_log.json
│ │ │ ├── crm_call_log.py
│ │ │ └── test_crm_call_log.py
│ │ ├── crm_communication_status
│ │ │ ├── __init__.py
│ │ │ ├── crm_communication_status.js
│ │ │ ├── crm_communication_status.json
│ │ │ ├── crm_communication_status.py
│ │ │ └── test_crm_communication_status.py
│ │ ├── crm_contacts
│ │ │ ├── __init__.py
│ │ │ ├── crm_contacts.json
│ │ │ └── crm_contacts.py
│ │ ├── crm_deal
│ │ │ ├── __init__.py
│ │ │ ├── api.py
│ │ │ ├── crm_deal.js
│ │ │ ├── crm_deal.json
│ │ │ ├── crm_deal.py
│ │ │ └── test_crm_deal.py
│ │ ├── crm_deal_status
│ │ │ ├── __init__.py
│ │ │ ├── crm_deal_status.js
│ │ │ ├── crm_deal_status.json
│ │ │ ├── crm_deal_status.py
│ │ │ └── test_crm_deal_status.py
│ │ ├── crm_dropdown_item
│ │ │ ├── __init__.py
│ │ │ ├── crm_dropdown_item.json
│ │ │ └── crm_dropdown_item.py
│ │ ├── crm_exotel_settings
│ │ │ ├── __init__.py
│ │ │ ├── crm_exotel_settings.js
│ │ │ ├── crm_exotel_settings.json
│ │ │ ├── crm_exotel_settings.py
│ │ │ └── test_crm_exotel_settings.py
│ │ ├── crm_fields_layout
│ │ │ ├── __init__.py
│ │ │ ├── crm_fields_layout.js
│ │ │ ├── crm_fields_layout.json
│ │ │ ├── crm_fields_layout.py
│ │ │ └── test_crm_fields_layout.py
│ │ ├── crm_form_script
│ │ │ ├── __init__.py
│ │ │ ├── crm_form_script.js
│ │ │ ├── crm_form_script.json
│ │ │ ├── crm_form_script.py
│ │ │ └── test_crm_form_script.py
│ │ ├── crm_global_settings
│ │ │ ├── __init__.py
│ │ │ ├── crm_global_settings.js
│ │ │ ├── crm_global_settings.json
│ │ │ ├── crm_global_settings.py
│ │ │ └── test_crm_global_settings.py
│ │ ├── crm_holiday
│ │ │ ├── __init__.py
│ │ │ ├── crm_holiday.json
│ │ │ └── crm_holiday.py
│ │ ├── crm_holiday_list
│ │ │ ├── __init__.py
│ │ │ ├── crm_holiday_list.js
│ │ │ ├── crm_holiday_list.json
│ │ │ ├── crm_holiday_list.py
│ │ │ └── test_crm_holiday_list.py
│ │ ├── crm_industry
│ │ │ ├── __init__.py
│ │ │ ├── crm_industry.js
│ │ │ ├── crm_industry.json
│ │ │ ├── crm_industry.py
│ │ │ └── test_crm_industry.py
│ │ ├── crm_invitation
│ │ │ ├── __init__.py
│ │ │ ├── crm_invitation.js
│ │ │ ├── crm_invitation.json
│ │ │ ├── crm_invitation.py
│ │ │ └── test_crm_invitation.py
│ │ ├── crm_lead
│ │ │ ├── __init__.py
│ │ │ ├── api.py
│ │ │ ├── crm_lead.js
│ │ │ ├── crm_lead.json
│ │ │ ├── crm_lead.py
│ │ │ └── test_crm_lead.py
│ │ ├── crm_lead_source
│ │ │ ├── __init__.py
│ │ │ ├── crm_lead_source.js
│ │ │ ├── crm_lead_source.json
│ │ │ ├── crm_lead_source.py
│ │ │ └── test_crm_lead_source.py
│ │ ├── crm_lead_status
│ │ │ ├── __init__.py
│ │ │ ├── crm_lead_status.js
│ │ │ ├── crm_lead_status.json
│ │ │ ├── crm_lead_status.py
│ │ │ └── test_crm_lead_status.py
│ │ ├── crm_notification
│ │ │ ├── __init__.py
│ │ │ ├── crm_notification.js
│ │ │ ├── crm_notification.json
│ │ │ ├── crm_notification.py
│ │ │ └── test_crm_notification.py
│ │ ├── crm_organization
│ │ │ ├── __init__.py
│ │ │ ├── crm_organization.js
│ │ │ ├── crm_organization.json
│ │ │ ├── crm_organization.py
│ │ │ └── test_crm_organization.py
│ │ ├── crm_product
│ │ │ ├── __init__.py
│ │ │ ├── crm_product.js
│ │ │ ├── crm_product.json
│ │ │ ├── crm_product.py
│ │ │ └── test_crm_product.py
│ │ ├── crm_products
│ │ │ ├── __init__.py
│ │ │ ├── crm_products.json
│ │ │ └── crm_products.py
│ │ ├── crm_service_day
│ │ │ ├── __init__.py
│ │ │ ├── crm_service_day.json
│ │ │ └── crm_service_day.py
│ │ ├── crm_service_level_agreement
│ │ │ ├── __init__.py
│ │ │ ├── crm_service_level_agreement.js
│ │ │ ├── crm_service_level_agreement.json
│ │ │ ├── crm_service_level_agreement.py
│ │ │ ├── test_crm_service_level_agreement.py
│ │ │ └── utils.py
│ │ ├── crm_service_level_priority
│ │ │ ├── __init__.py
│ │ │ ├── crm_service_level_priority.js
│ │ │ ├── crm_service_level_priority.json
│ │ │ ├── crm_service_level_priority.py
│ │ │ └── test_crm_service_level_priority.py
│ │ ├── crm_status_change_log
│ │ │ ├── __init__.py
│ │ │ ├── crm_status_change_log.json
│ │ │ └── crm_status_change_log.py
│ │ ├── crm_task
│ │ │ ├── __init__.py
│ │ │ ├── crm_task.js
│ │ │ ├── crm_task.json
│ │ │ ├── crm_task.py
│ │ │ └── test_crm_task.py
│ │ ├── crm_telephony_agent
│ │ │ ├── __init__.py
│ │ │ ├── crm_telephony_agent.js
│ │ │ ├── crm_telephony_agent.json
│ │ │ ├── crm_telephony_agent.py
│ │ │ └── test_crm_telephony_agent.py
│ │ ├── crm_telephony_phone
│ │ │ ├── __init__.py
│ │ │ ├── crm_telephony_phone.json
│ │ │ └── crm_telephony_phone.py
│ │ ├── crm_territory
│ │ │ ├── __init__.py
│ │ │ ├── crm_territory.js
│ │ │ ├── crm_territory.json
│ │ │ ├── crm_territory.py
│ │ │ └── test_crm_territory.py
│ │ ├── crm_twilio_settings
│ │ │ ├── __init__.py
│ │ │ ├── crm_twilio_settings.js
│ │ │ ├── crm_twilio_settings.json
│ │ │ ├── crm_twilio_settings.py
│ │ │ └── test_crm_twilio_settings.py
│ │ ├── crm_view_settings
│ │ │ ├── __init__.py
│ │ │ ├── crm_view_settings.js
│ │ │ ├── crm_view_settings.json
│ │ │ ├── crm_view_settings.py
│ │ │ └── test_crm_view_settings.py
│ │ ├── erpnext_crm_settings
│ │ │ ├── __init__.py
│ │ │ ├── erpnext_crm_settings.js
│ │ │ ├── erpnext_crm_settings.json
│ │ │ ├── erpnext_crm_settings.py
│ │ │ └── test_erpnext_crm_settings.py
│ │ ├── fcrm_note
│ │ │ ├── __init__.py
│ │ │ ├── fcrm_note.js
│ │ │ ├── fcrm_note.json
│ │ │ ├── fcrm_note.py
│ │ │ └── test_fcrm_note.py
│ │ └── fcrm_settings
│ │ │ ├── __init__.py
│ │ │ ├── fcrm_settings.js
│ │ │ ├── fcrm_settings.json
│ │ │ ├── fcrm_settings.py
│ │ │ └── test_fcrm_settings.py
│ └── workspace
│ │ └── frappe_crm
│ │ └── frappe_crm.json
├── hooks.py
├── install.py
├── integrations
│ ├── __init__.py
│ ├── api.py
│ ├── exotel
│ │ └── handler.py
│ └── twilio
│ │ ├── api.py
│ │ ├── twilio_handler.py
│ │ └── utils.py
├── locale
│ ├── ar.po
│ ├── bs.po
│ ├── de.po
│ ├── eo.po
│ ├── es.po
│ ├── fa.po
│ ├── fr.po
│ ├── hr.po
│ ├── hu.po
│ ├── main.pot
│ ├── pl.po
│ ├── pt.po
│ ├── ru.po
│ ├── sv.po
│ ├── th.po
│ ├── tr.po
│ └── zh.po
├── modules.txt
├── overrides
│ ├── contact.py
│ └── email_template.py
├── patches.txt
├── patches
│ └── v1_0
│ │ ├── __init__.py
│ │ ├── create_default_fields_layout.py
│ │ ├── create_default_scripts.py
│ │ ├── create_default_sidebar_fields_layout.py
│ │ ├── create_email_template_custom_fields.py
│ │ ├── move_crm_note_data_to_fcrm_note.py
│ │ ├── move_twilio_agent_to_telephony_agent.py
│ │ ├── rename_twilio_settings_to_crm_twilio_settings.py
│ │ ├── update_deal_quick_entry_layout.py
│ │ └── update_layouts_to_new_format.py
├── public
│ ├── .gitkeep
│ ├── images
│ │ ├── desk.png
│ │ ├── logo.png
│ │ └── logo.svg
│ ├── manifest
│ │ ├── apple-icon-180.png
│ │ ├── apple-splash-1125-2436.jpg
│ │ ├── apple-splash-1136-640.jpg
│ │ ├── apple-splash-1170-2532.jpg
│ │ ├── apple-splash-1179-2556.jpg
│ │ ├── apple-splash-1242-2208.jpg
│ │ ├── apple-splash-1242-2688.jpg
│ │ ├── apple-splash-1284-2778.jpg
│ │ ├── apple-splash-1290-2796.jpg
│ │ ├── apple-splash-1334-750.jpg
│ │ ├── apple-splash-1488-2266.jpg
│ │ ├── apple-splash-1536-2048.jpg
│ │ ├── apple-splash-1620-2160.jpg
│ │ ├── apple-splash-1640-2360.jpg
│ │ ├── apple-splash-1668-2224.jpg
│ │ ├── apple-splash-1668-2388.jpg
│ │ ├── apple-splash-1792-828.jpg
│ │ ├── apple-splash-2048-1536.jpg
│ │ ├── apple-splash-2048-2732.jpg
│ │ ├── apple-splash-2160-1620.jpg
│ │ ├── apple-splash-2208-1242.jpg
│ │ ├── apple-splash-2224-1668.jpg
│ │ ├── apple-splash-2266-1488.jpg
│ │ ├── apple-splash-2360-1640.jpg
│ │ ├── apple-splash-2388-1668.jpg
│ │ ├── apple-splash-2436-1125.jpg
│ │ ├── apple-splash-2532-1170.jpg
│ │ ├── apple-splash-2556-1179.jpg
│ │ ├── apple-splash-2688-1242.jpg
│ │ ├── apple-splash-2732-2048.jpg
│ │ ├── apple-splash-2778-1284.jpg
│ │ ├── apple-splash-2796-1290.jpg
│ │ ├── apple-splash-640-1136.jpg
│ │ ├── apple-splash-750-1334.jpg
│ │ ├── apple-splash-828-1792.jpg
│ │ ├── manifest-icon-192.maskable.png
│ │ └── manifest-icon-512.maskable.png
│ └── videos
│ │ ├── changeDealStatus.mov
│ │ └── convertToDeal.mov
├── templates
│ ├── __init__.py
│ ├── emails
│ │ └── crm_invitation.html
│ └── pages
│ │ └── __init__.py
├── uninstall.py
├── utils
│ └── __init__.py
└── www
│ ├── __init__.py
│ └── crm.py
├── crowdin.yml
├── docker
├── docker-compose.yml
└── init.sh
├── frontend
├── .gitignore
├── .prettierrc.json
├── README.md
├── components.d.ts
├── index.html
├── package.json
├── postcss.config.js
├── public
│ └── favicon.png
├── src
│ ├── App.vue
│ ├── components
│ │ ├── Activities
│ │ │ ├── Activities.vue
│ │ │ ├── ActivityHeader.vue
│ │ │ ├── AllModals.vue
│ │ │ ├── AttachmentArea.vue
│ │ │ ├── AudioPlayer.vue
│ │ │ ├── CallArea.vue
│ │ │ ├── CommentArea.vue
│ │ │ ├── DataFields.vue
│ │ │ ├── EmailArea.vue
│ │ │ ├── EmailContent.vue
│ │ │ ├── NoteArea.vue
│ │ │ ├── PlaybackSpeedOption.vue
│ │ │ ├── TaskArea.vue
│ │ │ ├── WhatsAppArea.vue
│ │ │ └── WhatsAppBox.vue
│ │ ├── Apps.vue
│ │ ├── AssignTo.vue
│ │ ├── AttachmentItem.vue
│ │ ├── BrandLogo.vue
│ │ ├── ColumnSettings.vue
│ │ ├── CommentBox.vue
│ │ ├── CommunicationArea.vue
│ │ ├── Controls
│ │ │ ├── FormattedInput.vue
│ │ │ ├── Grid.vue
│ │ │ ├── GridFieldsEditorModal.vue
│ │ │ ├── GridRowFieldsModal.vue
│ │ │ ├── GridRowModal.vue
│ │ │ ├── ImageUploader.vue
│ │ │ ├── Link.vue
│ │ │ ├── MultiSelectEmailInput.vue
│ │ │ ├── Password.vue
│ │ │ └── TableMultiselectInput.vue
│ │ ├── CountUpTimer.vue
│ │ ├── CustomActions.vue
│ │ ├── DropdownItem.vue
│ │ ├── EmailEditor.vue
│ │ ├── ErrorPage.vue
│ │ ├── FadedScrollableDiv.vue
│ │ ├── FieldLayout
│ │ │ ├── Column.vue
│ │ │ ├── Field.vue
│ │ │ ├── FieldLayout.vue
│ │ │ └── Section.vue
│ │ ├── FieldLayoutEditor.vue
│ │ ├── FilesUploader
│ │ │ ├── FilesUploader.vue
│ │ │ ├── FilesUploaderArea.vue
│ │ │ └── filesUploaderHandler.ts
│ │ ├── Filter.vue
│ │ ├── GroupBy.vue
│ │ ├── Icon.vue
│ │ ├── IconPicker.vue
│ │ ├── Icons
│ │ │ ├── ActivityIcon.vue
│ │ │ ├── AddressIcon.vue
│ │ │ ├── AppsIcon.vue
│ │ │ ├── ArrowUpRightIcon.vue
│ │ │ ├── AscendingIcon.vue
│ │ │ ├── AttachmentIcon.vue
│ │ │ ├── AvatarIcon.vue
│ │ │ ├── CRMLogo.vue
│ │ │ ├── CalendarIcon.vue
│ │ │ ├── CameraIcon.vue
│ │ │ ├── CertificateIcon.vue
│ │ │ ├── CheckCircleIcon.vue
│ │ │ ├── CheckIcon.vue
│ │ │ ├── CollapseSidebar.vue
│ │ │ ├── ColumnsIcon.vue
│ │ │ ├── CommentIcon.vue
│ │ │ ├── ContactIcon.vue
│ │ │ ├── ContactsIcon.vue
│ │ │ ├── ConvertIcon.vue
│ │ │ ├── DashboardIcon.vue
│ │ │ ├── DealsIcon.vue
│ │ │ ├── DeclinedCallIcon.vue
│ │ │ ├── DesendingIcon.vue
│ │ │ ├── DetailsIcon.vue
│ │ │ ├── DialpadIcon.vue
│ │ │ ├── DocumentIcon.vue
│ │ │ ├── DotIcon.vue
│ │ │ ├── DoubleCheckIcon.vue
│ │ │ ├── DragIcon.vue
│ │ │ ├── DragVerticalIcon.vue
│ │ │ ├── DuplicateIcon.vue
│ │ │ ├── DurationIcon.vue
│ │ │ ├── ERPNextIcon.vue
│ │ │ ├── EditIcon.vue
│ │ │ ├── Email2Icon.vue
│ │ │ ├── EmailAtIcon.vue
│ │ │ ├── EmailIcon.vue
│ │ │ ├── ExportIcon.vue
│ │ │ ├── ExternalLinkIcon.vue
│ │ │ ├── FileAudioIcon.vue
│ │ │ ├── FileIcon.vue
│ │ │ ├── FileImageIcon.vue
│ │ │ ├── FileSpreadsheetIcon.vue
│ │ │ ├── FileTextIcon.vue
│ │ │ ├── FileTypeIcon.vue
│ │ │ ├── FileVideoIcon.vue
│ │ │ ├── FilterIcon.vue
│ │ │ ├── FrappeCloudIcon.vue
│ │ │ ├── GenderIcon.vue
│ │ │ ├── GoogleIcon.vue
│ │ │ ├── GroupByIcon.vue
│ │ │ ├── HeartIcon.vue
│ │ │ ├── HelpIcon.vue
│ │ │ ├── InboundCallIcon.vue
│ │ │ ├── InboxIcon.vue
│ │ │ ├── IndicatorIcon.vue
│ │ │ ├── InviteIcon.vue
│ │ │ ├── KanbanIcon.vue
│ │ │ ├── LeadsIcon.vue
│ │ │ ├── LightningIcon.vue
│ │ │ ├── LinkIcon.vue
│ │ │ ├── ListIcon.vue
│ │ │ ├── LoadingIndicator.vue
│ │ │ ├── MarkAsDoneIcon.vue
│ │ │ ├── MaximizeIcon.vue
│ │ │ ├── MenuIcon.vue
│ │ │ ├── MinimizeIcon.vue
│ │ │ ├── MissedCallIcon.vue
│ │ │ ├── MoneyIcon.vue
│ │ │ ├── MuteIcon.vue
│ │ │ ├── NoteIcon.vue
│ │ │ ├── NotificationsIcon.vue
│ │ │ ├── OrganizationsIcon.vue
│ │ │ ├── OutboundCallIcon.vue
│ │ │ ├── PauseIcon.vue
│ │ │ ├── PhoneIcon.vue
│ │ │ ├── PinIcon.vue
│ │ │ ├── PlayIcon.vue
│ │ │ ├── PlaybackSpeedIcon.vue
│ │ │ ├── QuickFilterIcon.vue
│ │ │ ├── ReactIcon.vue
│ │ │ ├── RefreshIcon.vue
│ │ │ ├── ReloadIcon.vue
│ │ │ ├── ReplyAllIcon.vue
│ │ │ ├── ReplyIcon.vue
│ │ │ ├── RightSideLayoutIcon.vue
│ │ │ ├── SelectIcon.vue
│ │ │ ├── SettingsIcon.vue
│ │ │ ├── SmileIcon.vue
│ │ │ ├── SortIcon.vue
│ │ │ ├── SquareAsterisk.vue
│ │ │ ├── StepsIcon.vue
│ │ │ ├── SuccessIcon.vue
│ │ │ ├── TaskIcon.vue
│ │ │ ├── TaskPriorityIcon.vue
│ │ │ ├── TaskStatusIcon.vue
│ │ │ ├── TelegramIcon.vue
│ │ │ ├── TerritoryIcon.vue
│ │ │ ├── UnpinIcon.vue
│ │ │ ├── VolumnHighIcon.vue
│ │ │ ├── VolumnLowIcon.vue
│ │ │ ├── WebsiteIcon.vue
│ │ │ └── WhatsAppIcon.vue
│ │ ├── Kanban
│ │ │ ├── KanbanSettings.vue
│ │ │ └── KanbanView.vue
│ │ ├── LayoutHeader.vue
│ │ ├── Layouts
│ │ │ ├── AppHeader.vue
│ │ │ ├── AppSidebar.vue
│ │ │ ├── DesktopLayout.vue
│ │ │ └── MobileLayout.vue
│ │ ├── ListBulkActions.vue
│ │ ├── ListViews
│ │ │ ├── CallLogsListView.vue
│ │ │ ├── ContactsListView.vue
│ │ │ ├── DealsListView.vue
│ │ │ ├── EmailTemplatesListView.vue
│ │ │ ├── LeadsListView.vue
│ │ │ ├── ListRows.vue
│ │ │ ├── OrganizationsListView.vue
│ │ │ └── TasksListView.vue
│ │ ├── Mobile
│ │ │ ├── MobileAppHeader.vue
│ │ │ └── MobileSidebar.vue
│ │ ├── Modals
│ │ │ ├── AboutModal.vue
│ │ │ ├── AddressModal.vue
│ │ │ ├── AssignmentModal.vue
│ │ │ ├── CallLogDetailModal.vue
│ │ │ ├── CallLogModal.vue
│ │ │ ├── ContactModal.vue
│ │ │ ├── CreateDocumentModal.vue
│ │ │ ├── DataFieldsModal.vue
│ │ │ ├── DealModal.vue
│ │ │ ├── EditValueModal.vue
│ │ │ ├── EmailTemplateModal.vue
│ │ │ ├── EmailTemplateSelectorModal.vue
│ │ │ ├── GlobalModals.vue
│ │ │ ├── LeadModal.vue
│ │ │ ├── NoteModal.vue
│ │ │ ├── OrganizationModal.vue
│ │ │ ├── QuickEntryModal.vue
│ │ │ ├── SidePanelModal.vue
│ │ │ ├── TaskModal.vue
│ │ │ ├── ViewModal.vue
│ │ │ └── WhatsappTemplateSelectorModal.vue
│ │ ├── MultiActionButton.vue
│ │ ├── MultipleAvatar.vue
│ │ ├── NestedPopover.vue
│ │ ├── Notifications.vue
│ │ ├── QuickFilterField.vue
│ │ ├── Resizer.vue
│ │ ├── SLASection.vue
│ │ ├── Section.vue
│ │ ├── Settings
│ │ │ ├── ERPNextSettings.vue
│ │ │ ├── EmailAccountCard.vue
│ │ │ ├── EmailAccountList.vue
│ │ │ ├── EmailAdd.vue
│ │ │ ├── EmailConfig.vue
│ │ │ ├── EmailEdit.vue
│ │ │ ├── EmailProviderIcon.vue
│ │ │ ├── GeneralSettings.vue
│ │ │ ├── InviteMemberPage.vue
│ │ │ ├── ProfileImageEditor.vue
│ │ │ ├── ProfileSettings.vue
│ │ │ ├── Settings.vue
│ │ │ ├── SettingsPage.vue
│ │ │ ├── TelephonySettings.vue
│ │ │ ├── WhatsAppSettings.vue
│ │ │ └── emailConfig.js
│ │ ├── SidePanelLayout.vue
│ │ ├── SidePanelLayoutEditor.vue
│ │ ├── SidebarLink.vue
│ │ ├── SortBy.vue
│ │ ├── Telephony
│ │ │ ├── CallUI.vue
│ │ │ ├── ExotelCallUI.vue
│ │ │ ├── TaskPanel.vue
│ │ │ └── TwilioCallUI.vue
│ │ ├── UserAvatar.vue
│ │ ├── UserDropdown.vue
│ │ ├── ViewBreadcrumbs.vue
│ │ ├── ViewControls.vue
│ │ └── frappe-ui
│ │ │ ├── Autocomplete.vue
│ │ │ ├── Dropdown.vue
│ │ │ └── Popover.vue
│ ├── composables
│ │ ├── document.js
│ │ ├── frappecloud.js
│ │ ├── settings.js
│ │ ├── twilio.js
│ │ └── useActiveTabManager.js
│ ├── data
│ │ ├── document.js
│ │ └── script.js
│ ├── images
│ │ ├── frappe-mail.svg
│ │ ├── gmail.png
│ │ ├── outlook.png
│ │ ├── sendgrid.png
│ │ ├── sparkpost.webp
│ │ ├── yahoo.png
│ │ └── yandex.png
│ ├── index.css
│ ├── main.js
│ ├── pages
│ │ ├── CallLogs.vue
│ │ ├── Contact.vue
│ │ ├── Contacts.vue
│ │ ├── Dashboard.vue
│ │ ├── Deal.vue
│ │ ├── Deals.vue
│ │ ├── EmailTemplate.vue
│ │ ├── EmailTemplates.vue
│ │ ├── InvalidPage.vue
│ │ ├── Lead.vue
│ │ ├── Leads.vue
│ │ ├── MobileContact.vue
│ │ ├── MobileDeal.vue
│ │ ├── MobileLead.vue
│ │ ├── MobileNotification.vue
│ │ ├── MobileOrganization.vue
│ │ ├── Notes.vue
│ │ ├── Organization.vue
│ │ ├── Organizations.vue
│ │ ├── Tasks.vue
│ │ └── Welcome.vue
│ ├── router.js
│ ├── socket.js
│ ├── stores
│ │ ├── global.js
│ │ ├── meta.js
│ │ ├── notifications.js
│ │ ├── organizations.js
│ │ ├── session.js
│ │ ├── settings.js
│ │ ├── statuses.js
│ │ ├── theme.js
│ │ ├── user.js
│ │ ├── users.js
│ │ └── views.js
│ ├── telemetry.ts
│ ├── translation.js
│ ├── types.ts
│ └── utils
│ │ ├── callLog.js
│ │ ├── dialogs.jsx
│ │ ├── index.js
│ │ ├── numberFormat.js
│ │ └── view.js
├── tailwind.config.js
└── vite.config.js
├── package.json
├── pyproject.toml
├── scripts
└── init.sh
└── yarn.lock
/.devcontainer/devcontainer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Frappe Bench",
3 | "forwardPorts": [8000, 9000, 6787],
4 | "remoteUser": "frappe",
5 | "settings": {
6 | "terminal.integrated.defaultProfile.linux": "bash",
7 | "debug.node.autoAttach": "disabled"
8 | },
9 | "dockerComposeFile": "./docker-compose.yml",
10 | "service": "frappe",
11 | "workspaceFolder": "/workspace/frappe-bench",
12 | "postCreateCommand": "bash /workspace/scripts/init.sh",
13 | "shutdownAction": "stopCompose",
14 | "extensions": [
15 | "ms-python.python",
16 | "ms-vscode.live-server",
17 | "grapecity.gc-excelviewer",
18 | "mtxr.sqltools",
19 | "visualstudioexptteam.vscodeintellicode"
20 | ]
21 | }
--------------------------------------------------------------------------------
/.devcontainer/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: "3.7"
2 | services:
3 | mariadb:
4 | image: mariadb:10.6
5 | command:
6 | - --character-set-server=utf8mb4
7 | - --collation-server=utf8mb4_unicode_ci
8 | - --skip-character-set-client-handshake
9 | - --skip-innodb-read-only-compressed # Temporary fix for MariaDB 10.6
10 | environment:
11 | MYSQL_ROOT_PASSWORD: 123
12 | volumes:
13 | - mariadb-data:/var/lib/mysql
14 |
15 | # Enable PostgreSQL only if you use it, see development/README.md for more information.
16 | # postgresql:
17 | # image: postgres:11.8
18 | # environment:
19 | # POSTGRES_PASSWORD: 123
20 | # volumes:
21 | # - postgresql-data:/var/lib/postgresql/data
22 |
23 | redis-cache:
24 | image: redis:alpine
25 |
26 | redis-queue:
27 | image: redis:alpine
28 |
29 | redis-socketio:
30 | image: redis:alpine
31 |
32 | frappe:
33 | image: frappe/bench:latest
34 | command: sleep infinity
35 | environment:
36 | - SHELL=/bin/bash
37 | volumes:
38 | - ..:/workspace:cached
39 | working_dir: /workspace/frappe-bench
40 | ports:
41 | - 8000-8005:8000-8005
42 | - 9000-9005:9000-9005
43 |
44 | volumes:
45 | mariadb-data:
46 | postgresql-data:
--------------------------------------------------------------------------------
/.github/crm_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/.github/crm_logo.png
--------------------------------------------------------------------------------
/.github/helper/update_pot_file.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -e
3 | cd ~ || exit
4 |
5 | echo "Setting Up Bench..."
6 |
7 | pip install frappe-bench
8 | bench -v init frappe-bench --skip-assets --skip-redis-config-generation --python "$(which python)" --frappe-branch "${BASE_BRANCH}"
9 | cd ./frappe-bench || exit
10 |
11 | echo "Get FCRM..."
12 | bench get-app --skip-assets crm "${GITHUB_WORKSPACE}"
13 |
14 | echo "Generating POT file..."
15 | bench generate-pot-file --app crm
16 |
17 | cd ./apps/crm || exit
18 |
19 | echo "Configuring git user..."
20 | git config user.email "developers@erpnext.com"
21 | git config user.name "frappe-pr-bot"
22 |
23 | echo "Setting the correct git remote..."
24 | # Here, the git remote is a local file path by default. Let's change it to the upstream repo.
25 | git remote set-url upstream https://github.com/frappe/crm.git
26 |
27 | echo "Creating a new branch..."
28 | isodate=$(date -u +"%Y-%m-%d")
29 | branch_name="pot_${BASE_BRANCH}_${isodate}"
30 | git checkout -b "${branch_name}"
31 |
32 | echo "Commiting changes..."
33 | git add crm/locale/main.pot
34 | git commit -m "chore: update POT file"
35 |
36 | gh auth setup-git
37 | git push -u upstream "${branch_name}"
38 |
39 | echo "Creating a PR..."
40 | gh pr create --fill --base "${BASE_BRANCH}" --head "${branch_name}" -R frappe/crm
--------------------------------------------------------------------------------
/.github/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/.github/logo.png
--------------------------------------------------------------------------------
/.github/logo.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/.github/screenshots/CallLog.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/.github/screenshots/CallLog.png
--------------------------------------------------------------------------------
/.github/screenshots/CallUI.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/.github/screenshots/CallUI.png
--------------------------------------------------------------------------------
/.github/screenshots/EmailTemplate.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/.github/screenshots/EmailTemplate.png
--------------------------------------------------------------------------------
/.github/screenshots/FrappeCRMHeroImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/.github/screenshots/FrappeCRMHeroImage.png
--------------------------------------------------------------------------------
/.github/screenshots/LeadList.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/.github/screenshots/LeadList.png
--------------------------------------------------------------------------------
/.github/screenshots/LeadPage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/.github/screenshots/LeadPage.png
--------------------------------------------------------------------------------
/.github/screenshots/MainDealPage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/.github/screenshots/MainDealPage.png
--------------------------------------------------------------------------------
/.github/workflows/generate-pot-file.yml:
--------------------------------------------------------------------------------
1 | name: Regenerate POT file (translatable strings)
2 | on:
3 | schedule:
4 | # 9:30 UTC => 3 PM IST Sunday
5 | - cron: "30 9 * * 0"
6 | workflow_dispatch:
7 |
8 | jobs:
9 | regenerate-pot-file:
10 | name: Regenerate POT file
11 | runs-on: ubuntu-latest
12 | strategy:
13 | fail-fast: false
14 | matrix:
15 | branch: ["develop"]
16 | permissions:
17 | contents: write
18 |
19 | steps:
20 | - name: Checkout
21 | uses: actions/checkout@v4
22 | with:
23 | ref: ${{ matrix.branch }}
24 |
25 | - name: Setup Python
26 | uses: actions/setup-python@v5
27 | with:
28 | python-version: "3.12"
29 |
30 | - name: Run script to update POT file
31 | run: |
32 | bash ${GITHUB_WORKSPACE}/.github/helper/update_pot_file.sh
33 | env:
34 | GH_TOKEN: ${{ secrets.RELEASE_TOKEN }}
35 | BASE_BRANCH: ${{ matrix.branch }}
--------------------------------------------------------------------------------
/.github/workflows/on_release.yml:
--------------------------------------------------------------------------------
1 | name: Generate Semantic Release
2 | on:
3 | workflow_dispatch:
4 | push:
5 | branches:
6 | - main
7 | jobs:
8 | release:
9 | name: Release
10 | runs-on: ubuntu-latest
11 | steps:
12 | - name: Checkout Entire Repository
13 | uses: actions/checkout@v2
14 | with:
15 | fetch-depth: 0
16 | persist-credentials: false
17 | - name: Setup Node.js
18 | uses: actions/setup-node@v2
19 | with:
20 | node-version: 20
21 | - name: Setup dependencies
22 | run: |
23 | npm install @semantic-release/git @semantic-release/exec --no-save
24 | - name: Create Release
25 | env:
26 | GH_TOKEN: ${{ secrets.RELEASE_TOKEN }}
27 | GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
28 | GIT_AUTHOR_NAME: "Frappe PR Bot"
29 | GIT_AUTHOR_EMAIL: "developers@frappe.io"
30 | GIT_COMMITTER_NAME: "Frappe PR Bot"
31 | GIT_COMMITTER_EMAIL: "developers@frappe.io"
32 | run: npx semantic-release
--------------------------------------------------------------------------------
/.github/workflows/release_notes.yml:
--------------------------------------------------------------------------------
1 | # This action:
2 | #
3 | # 1. Generates release notes using github API.
4 | # 2. Strips unnecessary info like chore/style etc from notes.
5 | # 3. Updates release info.
6 |
7 | name: 'Release Notes'
8 |
9 | on:
10 | workflow_dispatch:
11 | inputs:
12 | tag_name:
13 | description: 'Tag of release like v1.0.0'
14 | required: true
15 | type: string
16 | release:
17 | types: [released]
18 |
19 | permissions:
20 | contents: read
21 |
22 | jobs:
23 | regen-notes:
24 | name: 'Regenerate release notes'
25 | runs-on: ubuntu-latest
26 |
27 | steps:
28 | - name: Update notes
29 | run: |
30 | NEW_NOTES=$(gh api --method POST -H "Accept: application/vnd.github+json" /repos/frappe/crm/releases/generate-notes -f tag_name=$RELEASE_TAG \
31 | | jq -r '.body' \
32 | | sed -E '/^\* (chore|ci|test|docs|style)/d' \
33 | | sed -E 's/by @mergify //'
34 | )
35 | RELEASE_ID=$(gh api -H "Accept: application/vnd.github+json" /repos/frappe/crm/releases/tags/$RELEASE_TAG | jq -r '.id')
36 | gh api --method PATCH -H "Accept: application/vnd.github+json" /repos/frappe/crm/releases/$RELEASE_ID -f body="$NEW_NOTES"
37 |
38 | env:
39 | GH_TOKEN: ${{ secrets.RELEASE_TOKEN }}
40 | RELEASE_TAG: ${{ github.event.inputs.tag_name || github.event.release.tag_name }}
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | *.pyc
3 | *.egg-info
4 | *.swp
5 | __pycache__
6 | dev-dist
7 | tags
8 | node_modules
9 | crm/public/frontend
10 | frontend/yarn.lock
11 | crm/www/crm.html
12 | build
13 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "frappe-ui"]
2 | path = frappe-ui
3 | url = https://github.com/frappe/frappe-ui
4 |
--------------------------------------------------------------------------------
/.mergify.yml:
--------------------------------------------------------------------------------
1 | pull_request_rules:
2 | - name: Auto-close PRs on stable branch
3 | conditions:
4 | - and:
5 | - and:
6 | - author!=shariquerik
7 | - author!=frappe-pr-bot
8 | - author!=mergify[bot]
9 | - or:
10 | - base=main
11 | actions:
12 | close:
13 | comment:
14 | message: |
15 | @{{author}}, thanks for the contribution, but we do not accept pull requests on a stable branch. Please raise PR on develop branch.
16 |
17 | - name: backport to develop
18 | conditions:
19 | - label="backport develop"
20 | actions:
21 | backport:
22 | branches:
23 | - develop
24 | assignees:
25 | - "{{ author }}"
26 |
27 | - name: backport to main-hotfix
28 | conditions:
29 | - label="backport main-hotfix"
30 | actions:
31 | backport:
32 | branches:
33 | - main-hotfix
34 | assignees:
35 | - "{{ author }}"
36 |
37 | - name: backport to main
38 | conditions:
39 | - label="backport main"
40 | actions:
41 | backport:
42 | branches:
43 | - main
44 | assignees:
45 | - "{{ author }}"
46 |
--------------------------------------------------------------------------------
/.releaserc:
--------------------------------------------------------------------------------
1 | {
2 | "branches": ["main"],
3 | "plugins": [
4 | "@semantic-release/commit-analyzer", {
5 | "preset": "angular"
6 | },
7 | "@semantic-release/release-notes-generator",
8 | [
9 | "@semantic-release/exec", {
10 | "prepareCmd": 'sed -ir "s/[0-9]*\.[0-9]*\.[0-9]*/${nextRelease.version}/" crm/__init__.py'
11 | }
12 | ],
13 | [
14 | "@semantic-release/git", {
15 | "assets": ["crm/__init__.py"],
16 | "message": "chore(release): Bumped to Version ${nextRelease.version}"
17 | }
18 | ],
19 | "@semantic-release/github"
20 | ]
21 | }
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "python.defaultInterpreterPath": "frappe-bench/env/bin/python",
3 | "debug.node.autoAttach": "disabled",
4 | "sqltools.connections": [
5 | {
6 | "mysqlOptions": {
7 | "authProtocol": "default"
8 | },
9 | "previewLimit": 50,
10 | "server": "mariadb",
11 | "port": 3306,
12 | "driver": "MariaDB",
13 | "name": "MariaDB",
14 | "username": "root",
15 | "password": "123"
16 | }
17 | ]
18 | }
--------------------------------------------------------------------------------
/crm/__init__.py:
--------------------------------------------------------------------------------
1 |
2 | __version__ = "2.0.0-dev"
3 | __title__ = "Frappe CRM"
4 |
5 |
--------------------------------------------------------------------------------
/crm/api/auth.py:
--------------------------------------------------------------------------------
1 | import frappe
2 |
3 | @frappe.whitelist(allow_guest=True)
4 | def oauth_providers():
5 | from frappe.utils.html_utils import get_icon_html
6 | from frappe.utils.password import get_decrypted_password
7 | from frappe.utils.oauth import get_oauth2_authorize_url, get_oauth_keys
8 |
9 | out = []
10 | providers = frappe.get_all(
11 | "Social Login Key",
12 | filters={"enable_social_login": 1},
13 | fields=["name", "client_id", "base_url", "provider_name", "icon"],
14 | order_by="name",
15 | )
16 |
17 | for provider in providers:
18 | client_secret = get_decrypted_password("Social Login Key", provider.name, "client_secret")
19 | if not client_secret:
20 | continue
21 |
22 | icon = None
23 | if provider.icon:
24 | if provider.provider_name == "Custom":
25 | icon = get_icon_html(provider.icon, small=True)
26 | else:
27 | icon = f""
28 |
29 | if provider.client_id and provider.base_url and get_oauth_keys(provider.name):
30 | out.append(
31 | {
32 | "name": provider.name,
33 | "provider_name": provider.provider_name,
34 | "auth_url": get_oauth2_authorize_url(provider.name, "/crm"),
35 | "icon": icon,
36 | }
37 | )
38 | return out
--------------------------------------------------------------------------------
/crm/api/demo.py:
--------------------------------------------------------------------------------
1 | import frappe
2 | from frappe import _
3 | from frappe.auth import LoginManager
4 |
5 |
6 | @frappe.whitelist(allow_guest=True)
7 | def login():
8 | if not frappe.conf.demo_username or not frappe.conf.demo_password:
9 | return
10 | frappe.local.response["redirect_to"] = "/crm"
11 | login_manager = LoginManager()
12 | login_manager.authenticate(frappe.conf.demo_username, frappe.conf.demo_password)
13 | login_manager.post_login()
14 | frappe.local.response["type"] = "redirect"
15 | frappe.local.response["location"] = frappe.local.response["redirect_to"]
16 |
17 |
18 | def validate_reset_password(doc, event):
19 | if frappe.conf.demo_username and frappe.session.user == frappe.conf.demo_username:
20 | frappe.throw(
21 | _("Password cannot be reset by Demo User {}").format(frappe.bold(frappe.conf.demo_username)),
22 | frappe.PermissionError,
23 | )
24 |
25 |
26 | def validate_user(doc, event):
27 | if frappe.conf.demo_username and frappe.session.user == frappe.conf.demo_username and doc.new_password:
28 | frappe.throw(
29 | _("Password cannot be reset by Demo User {}").format(frappe.bold(frappe.conf.demo_username)),
30 | frappe.PermissionError,
31 | )
32 |
--------------------------------------------------------------------------------
/crm/api/onboarding.py:
--------------------------------------------------------------------------------
1 | import frappe
2 |
3 |
4 | @frappe.whitelist()
5 | def get_first_lead():
6 | lead = frappe.get_all(
7 | "CRM Lead",
8 | filters={"converted": 0},
9 | fields=["name"],
10 | order_by="creation",
11 | limit=1,
12 | )
13 | return lead[0].name if lead else None
14 |
15 |
16 | @frappe.whitelist()
17 | def get_first_deal():
18 | deal = frappe.get_all(
19 | "CRM Deal",
20 | fields=["name"],
21 | order_by="creation",
22 | limit=1,
23 | )
24 | return deal[0].name if deal else None
25 |
--------------------------------------------------------------------------------
/crm/api/session.py:
--------------------------------------------------------------------------------
1 | import frappe
2 |
3 |
4 | @frappe.whitelist()
5 | def get_users():
6 | users = frappe.qb.get_query(
7 | "User",
8 | fields=[
9 | "name",
10 | "email",
11 | "enabled",
12 | "user_image",
13 | "first_name",
14 | "last_name",
15 | "full_name",
16 | "user_type",
17 | ],
18 | order_by="full_name asc",
19 | distinct=True,
20 | ).run(as_dict=1)
21 |
22 | for user in users:
23 | if frappe.session.user == user.name:
24 | user.session_user = True
25 |
26 | user.is_manager = "Sales Manager" in frappe.get_roles(user.name) or user.name == "Administrator"
27 |
28 | user.is_agent = frappe.db.exists("CRM Telephony Agent", {"user": user.name})
29 |
30 | return users
31 |
32 |
33 | @frappe.whitelist()
34 | def get_organizations():
35 | organizations = frappe.qb.get_query(
36 | "CRM Organization",
37 | fields=["*"],
38 | order_by="name asc",
39 | distinct=True,
40 | ).run(as_dict=1)
41 |
42 | return organizations
43 |
--------------------------------------------------------------------------------
/crm/api/views.py:
--------------------------------------------------------------------------------
1 | import frappe
2 | from pypika import Criterion
3 |
4 |
5 | @frappe.whitelist()
6 | def get_views(doctype):
7 | View = frappe.qb.DocType("CRM View Settings")
8 | query = (
9 | frappe.qb.from_(View)
10 | .select("*")
11 | .where(Criterion.any([View.user == "", View.user == frappe.session.user]))
12 | )
13 | if doctype:
14 | query = query.where(View.dt == doctype)
15 | views = query.run(as_dict=True)
16 | return views
17 |
--------------------------------------------------------------------------------
/crm/config/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/config/__init__.py
--------------------------------------------------------------------------------
/crm/crowdin.yml:
--------------------------------------------------------------------------------
1 | files:
2 | - source: /crm/locale/main.pot
3 | translation: /crm/locale/%two_letters_code%.po
4 | pull_request_title: "chore: sync translations from crowdin"
5 | pull_request_labels:
6 | - translation
7 | commit_message: "chore: %language% translations"
8 | append_commit_message: false
--------------------------------------------------------------------------------
/crm/fcrm/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/fcrm/__init__.py
--------------------------------------------------------------------------------
/crm/fcrm/doctype/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/fcrm/doctype/__init__.py
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_call_log/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/fcrm/doctype/crm_call_log/__init__.py
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_call_log/crm_call_log.js:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
2 | // For license information, please see license.txt
3 |
4 | // frappe.ui.form.on("CRM Call Log", {
5 | // refresh(frm) {
6 |
7 | // },
8 | // });
9 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_call_log/test_crm_call_log.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors
2 | # See license.txt
3 |
4 | # import frappe
5 | from frappe.tests import UnitTestCase
6 |
7 |
8 | class TestCRMCallLog(UnitTestCase):
9 | pass
10 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_communication_status/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/fcrm/doctype/crm_communication_status/__init__.py
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_communication_status/crm_communication_status.js:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
2 | // For license information, please see license.txt
3 |
4 | // frappe.ui.form.on("CRM Communication Status", {
5 | // refresh(frm) {
6 |
7 | // },
8 | // });
9 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_communication_status/crm_communication_status.json:
--------------------------------------------------------------------------------
1 | {
2 | "actions": [],
3 | "allow_rename": 1,
4 | "autoname": "field:status",
5 | "creation": "2023-12-13 13:25:07.213100",
6 | "doctype": "DocType",
7 | "engine": "InnoDB",
8 | "field_order": [
9 | "status"
10 | ],
11 | "fields": [
12 | {
13 | "fieldname": "status",
14 | "fieldtype": "Data",
15 | "in_list_view": 1,
16 | "label": "Status",
17 | "reqd": 1,
18 | "unique": 1
19 | }
20 | ],
21 | "index_web_pages_for_search": 1,
22 | "links": [],
23 | "modified": "2024-01-19 21:55:17.952032",
24 | "modified_by": "Administrator",
25 | "module": "FCRM",
26 | "name": "CRM Communication Status",
27 | "naming_rule": "By fieldname",
28 | "owner": "Administrator",
29 | "permissions": [
30 | {
31 | "create": 1,
32 | "delete": 1,
33 | "email": 1,
34 | "export": 1,
35 | "print": 1,
36 | "read": 1,
37 | "report": 1,
38 | "role": "Sales User",
39 | "share": 1,
40 | "write": 1
41 | },
42 | {
43 | "create": 1,
44 | "delete": 1,
45 | "email": 1,
46 | "export": 1,
47 | "print": 1,
48 | "read": 1,
49 | "report": 1,
50 | "role": "Sales Manager",
51 | "share": 1,
52 | "write": 1
53 | }
54 | ],
55 | "quick_entry": 1,
56 | "sort_field": "modified",
57 | "sort_order": "DESC",
58 | "states": []
59 | }
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_communication_status/crm_communication_status.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
2 | # For license information, please see license.txt
3 |
4 | # import frappe
5 | from frappe.model.document import Document
6 |
7 |
8 | class CRMCommunicationStatus(Document):
9 | pass
10 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_communication_status/test_crm_communication_status.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors
2 | # See license.txt
3 |
4 | # import frappe
5 | from frappe.tests import UnitTestCase
6 |
7 |
8 | class TestCRMCommunicationStatus(UnitTestCase):
9 | pass
10 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_contacts/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/fcrm/doctype/crm_contacts/__init__.py
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_contacts/crm_contacts.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
2 | # For license information, please see license.txt
3 |
4 | # import frappe
5 | from frappe.model.document import Document
6 |
7 |
8 | class CRMContacts(Document):
9 | pass
10 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_deal/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/fcrm/doctype/crm_deal/__init__.py
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_deal/test_crm_deal.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors
2 | # See license.txt
3 |
4 | # import frappe
5 | from frappe.tests import UnitTestCase
6 |
7 |
8 | class TestCRMDeal(UnitTestCase):
9 | pass
10 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_deal_status/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/fcrm/doctype/crm_deal_status/__init__.py
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_deal_status/crm_deal_status.js:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
2 | // For license information, please see license.txt
3 |
4 | // frappe.ui.form.on("CRM Deal Status", {
5 | // refresh(frm) {
6 |
7 | // },
8 | // });
9 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_deal_status/crm_deal_status.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
2 | # For license information, please see license.txt
3 |
4 | # import frappe
5 | from frappe.model.document import Document
6 |
7 |
8 | class CRMDealStatus(Document):
9 | pass
10 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_deal_status/test_crm_deal_status.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors
2 | # See license.txt
3 |
4 | # import frappe
5 | from frappe.tests import UnitTestCase
6 |
7 |
8 | class TestCRMDealStatus(UnitTestCase):
9 | pass
10 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_dropdown_item/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/fcrm/doctype/crm_dropdown_item/__init__.py
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_dropdown_item/crm_dropdown_item.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors
2 | # For license information, please see license.txt
3 |
4 | # import frappe
5 | from frappe.model.document import Document
6 |
7 |
8 | class CRMDropdownItem(Document):
9 | pass
10 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_exotel_settings/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/fcrm/doctype/crm_exotel_settings/__init__.py
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_exotel_settings/crm_exotel_settings.js:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors
2 | // For license information, please see license.txt
3 |
4 | // frappe.ui.form.on("CRM Exotel Settings", {
5 | // refresh(frm) {
6 |
7 | // },
8 | // });
9 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_exotel_settings/crm_exotel_settings.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors
2 | # For license information, please see license.txt
3 |
4 | import frappe
5 | import requests
6 | from frappe import _
7 | from frappe.model.document import Document
8 |
9 |
10 | class CRMExotelSettings(Document):
11 | def validate(self):
12 | self.verify_credentials()
13 |
14 | def verify_credentials(self):
15 | if self.enabled:
16 | response = requests.get(
17 | "https://{subdomain}/v1/Accounts/{sid}".format(
18 | subdomain=self.subdomain, sid=self.account_sid
19 | ),
20 | auth=(self.api_key, self.get_password("api_token")),
21 | )
22 | if response.status_code != 200:
23 | frappe.throw(
24 | _(f"Please enter valid exotel Account SID, API key & API token: {response.reason}"),
25 | title=_("Invalid credentials"),
26 | )
27 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_exotel_settings/test_crm_exotel_settings.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and Contributors
2 | # See license.txt
3 |
4 | # import frappe
5 | from frappe.tests import IntegrationTestCase, UnitTestCase
6 |
7 | # On IntegrationTestCase, the doctype test records and all
8 | # link-field test record dependencies are recursively loaded
9 | # Use these module variables to add/remove to/from that list
10 | EXTRA_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"]
11 | IGNORE_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"]
12 |
13 |
14 | class UnitTestCRMExotelSettings(UnitTestCase):
15 | """
16 | Unit tests for CRMExotelSettings.
17 | Use this class for testing individual functions and methods.
18 | """
19 |
20 | pass
21 |
22 |
23 | class IntegrationTestCRMExotelSettings(IntegrationTestCase):
24 | """
25 | Integration tests for CRMExotelSettings.
26 | Use this class for testing interactions between multiple components.
27 | """
28 |
29 | pass
30 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_fields_layout/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/fcrm/doctype/crm_fields_layout/__init__.py
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_fields_layout/crm_fields_layout.js:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors
2 | // For license information, please see license.txt
3 |
4 | // frappe.ui.form.on("CRM Fields Layout", {
5 | // refresh(frm) {
6 |
7 | // },
8 | // });
9 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_fields_layout/test_crm_fields_layout.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and Contributors
2 | # See license.txt
3 |
4 | # import frappe
5 | from frappe.tests import UnitTestCase
6 |
7 |
8 | class TestCRMFieldsLayout(UnitTestCase):
9 | pass
10 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_form_script/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/fcrm/doctype/crm_form_script/__init__.py
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_form_script/crm_form_script.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
2 | # For license information, please see license.txt
3 |
4 | import frappe
5 | from frappe import _
6 | from frappe.model.document import Document
7 |
8 |
9 | class CRMFormScript(Document):
10 | def validate(self):
11 | in_user_env = not (
12 | frappe.flags.in_install
13 | or frappe.flags.in_patch
14 | or frappe.flags.in_test
15 | or frappe.flags.in_fixtures
16 | )
17 | if in_user_env and not self.is_new() and self.is_standard and not frappe.conf.developer_mode:
18 | # only enabled can be changed for standard form scripts
19 | if self.has_value_changed("enabled"):
20 | enabled_value = self.enabled
21 | self.reload()
22 | self.enabled = enabled_value
23 | else:
24 | frappe.throw(_("You need to be in developer mode to edit a Standard Form Script"))
25 |
26 | def get_form_script(dt, view="Form"):
27 | """Returns the form script for the given doctype"""
28 | FormScript = frappe.qb.DocType("CRM Form Script")
29 | query = (
30 | frappe.qb.from_(FormScript)
31 | .select("script")
32 | .where(FormScript.dt == dt)
33 | .where(FormScript.view == view)
34 | .where(FormScript.enabled == 1)
35 | )
36 |
37 | doc = query.run(as_dict=True)
38 | if doc:
39 | return [d.script for d in doc] if len(doc) > 1 else doc[0].script
40 | else:
41 | return None
42 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_form_script/test_crm_form_script.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors
2 | # See license.txt
3 |
4 | # import frappe
5 | from frappe.tests import UnitTestCase
6 |
7 |
8 | class TestCRMFormScript(UnitTestCase):
9 | pass
10 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_global_settings/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/fcrm/doctype/crm_global_settings/__init__.py
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_global_settings/crm_global_settings.js:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors
2 | // For license information, please see license.txt
3 |
4 | // frappe.ui.form.on("CRM Global Settings", {
5 | // refresh(frm) {
6 |
7 | // },
8 | // });
9 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_global_settings/crm_global_settings.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors
2 | # For license information, please see license.txt
3 |
4 | # import frappe
5 | from frappe.model.document import Document
6 |
7 |
8 | class CRMGlobalSettings(Document):
9 | pass
10 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_global_settings/test_crm_global_settings.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and Contributors
2 | # See license.txt
3 |
4 | # import frappe
5 | from frappe.tests import IntegrationTestCase, UnitTestCase
6 |
7 |
8 | # On IntegrationTestCase, the doctype test records and all
9 | # link-field test record dependencies are recursively loaded
10 | # Use these module variables to add/remove to/from that list
11 | EXTRA_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"]
12 | IGNORE_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"]
13 |
14 |
15 | class UnitTestCRMGlobalSettings(UnitTestCase):
16 | """
17 | Unit tests for CRMGlobalSettings.
18 | Use this class for testing individual functions and methods.
19 | """
20 |
21 | pass
22 |
23 |
24 | class IntegrationTestCRMGlobalSettings(IntegrationTestCase):
25 | """
26 | Integration tests for CRMGlobalSettings.
27 | Use this class for testing interactions between multiple components.
28 | """
29 |
30 | pass
31 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_holiday/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/fcrm/doctype/crm_holiday/__init__.py
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_holiday/crm_holiday.json:
--------------------------------------------------------------------------------
1 | {
2 | "actions": [],
3 | "allow_rename": 1,
4 | "creation": "2023-12-14 11:16:15.476366",
5 | "doctype": "DocType",
6 | "editable_grid": 1,
7 | "engine": "InnoDB",
8 | "field_order": [
9 | "date",
10 | "column_break_xzyo",
11 | "weekly_off",
12 | "section_break_zenz",
13 | "description"
14 | ],
15 | "fields": [
16 | {
17 | "fieldname": "date",
18 | "fieldtype": "Date",
19 | "in_list_view": 1,
20 | "label": "Date",
21 | "reqd": 1
22 | },
23 | {
24 | "fieldname": "column_break_xzyo",
25 | "fieldtype": "Column Break"
26 | },
27 | {
28 | "default": "0",
29 | "fieldname": "weekly_off",
30 | "fieldtype": "Check",
31 | "label": "Weekly Off"
32 | },
33 | {
34 | "fieldname": "section_break_zenz",
35 | "fieldtype": "Section Break"
36 | },
37 | {
38 | "fieldname": "description",
39 | "fieldtype": "Text Editor",
40 | "in_list_view": 1,
41 | "label": "Description",
42 | "reqd": 1
43 | }
44 | ],
45 | "index_web_pages_for_search": 1,
46 | "istable": 1,
47 | "links": [],
48 | "modified": "2023-12-14 11:17:41.745419",
49 | "modified_by": "Administrator",
50 | "module": "FCRM",
51 | "name": "CRM Holiday",
52 | "owner": "Administrator",
53 | "permissions": [],
54 | "sort_field": "modified",
55 | "sort_order": "DESC",
56 | "states": []
57 | }
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_holiday/crm_holiday.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
2 | # For license information, please see license.txt
3 |
4 | # import frappe
5 | from frappe.model.document import Document
6 |
7 |
8 | class CRMHoliday(Document):
9 | pass
10 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_holiday_list/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/fcrm/doctype/crm_holiday_list/__init__.py
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_holiday_list/crm_holiday_list.js:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
2 | // For license information, please see license.txt
3 |
4 | // frappe.ui.form.on("CRM Holiday List", {
5 | // refresh(frm) {
6 |
7 | // },
8 | // });
9 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_holiday_list/crm_holiday_list.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
2 | # For license information, please see license.txt
3 |
4 | # import frappe
5 | from frappe.model.document import Document
6 |
7 |
8 | class CRMHolidayList(Document):
9 | pass
10 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_holiday_list/test_crm_holiday_list.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors
2 | # See license.txt
3 |
4 | # import frappe
5 | from frappe.tests import UnitTestCase
6 |
7 |
8 | class TestCRMHolidayList(UnitTestCase):
9 | pass
10 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_industry/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/fcrm/doctype/crm_industry/__init__.py
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_industry/crm_industry.js:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
2 | // For license information, please see license.txt
3 |
4 | // frappe.ui.form.on("CRM Industry", {
5 | // refresh(frm) {
6 |
7 | // },
8 | // });
9 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_industry/crm_industry.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
2 | # For license information, please see license.txt
3 |
4 | # import frappe
5 | from frappe.model.document import Document
6 |
7 |
8 | class CRMIndustry(Document):
9 | pass
10 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_industry/test_crm_industry.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors
2 | # See license.txt
3 |
4 | # import frappe
5 | from frappe.tests import UnitTestCase
6 |
7 |
8 | class TestCRMIndustry(UnitTestCase):
9 | pass
10 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_invitation/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/fcrm/doctype/crm_invitation/__init__.py
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_invitation/crm_invitation.js:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors
2 | // For license information, please see license.txt
3 |
4 | frappe.ui.form.on("CRM Invitation", {
5 | refresh(frm) {
6 | if (frm.doc.status != "Accepted") {
7 | frm.add_custom_button(__("Accept Invitation"), () => {
8 | return frm.call("accept_invitation");
9 | });
10 | }
11 | },
12 | });
13 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_invitation/test_crm_invitation.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and Contributors
2 | # See license.txt
3 |
4 | # import frappe
5 | from frappe.tests import UnitTestCase
6 |
7 |
8 | class TestCRMInvitation(UnitTestCase):
9 | pass
10 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_lead/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/fcrm/doctype/crm_lead/__init__.py
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_lead/api.py:
--------------------------------------------------------------------------------
1 | import frappe
2 |
3 | from crm.api.doc import get_assigned_users, get_fields_meta
4 | from crm.fcrm.doctype.crm_form_script.crm_form_script import get_form_script
5 |
6 |
7 | @frappe.whitelist()
8 | def get_lead(name):
9 | lead = frappe.get_doc("CRM Lead", name)
10 | lead.check_permission("read")
11 |
12 | lead = lead.as_dict()
13 |
14 | lead["fields_meta"] = get_fields_meta("CRM Lead")
15 | lead["_form_script"] = get_form_script("CRM Lead")
16 | lead["_assign"] = get_assigned_users("CRM Lead", lead.name)
17 | return lead
18 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_lead/test_crm_lead.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors
2 | # See license.txt
3 |
4 | # import frappe
5 | from frappe.tests import UnitTestCase
6 |
7 |
8 | class TestCRMLead(UnitTestCase):
9 | pass
10 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_lead_source/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/fcrm/doctype/crm_lead_source/__init__.py
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_lead_source/crm_lead_source.js:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
2 | // For license information, please see license.txt
3 |
4 | // frappe.ui.form.on("CRM Lead Source", {
5 | // refresh(frm) {
6 |
7 | // },
8 | // });
9 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_lead_source/crm_lead_source.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
2 | # For license information, please see license.txt
3 |
4 | # import frappe
5 | from frappe.model.document import Document
6 |
7 |
8 | class CRMLeadSource(Document):
9 | pass
10 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_lead_source/test_crm_lead_source.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors
2 | # See license.txt
3 |
4 | # import frappe
5 | from frappe.tests import UnitTestCase
6 |
7 |
8 | class TestCRMLeadSource(UnitTestCase):
9 | pass
10 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_lead_status/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/fcrm/doctype/crm_lead_status/__init__.py
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_lead_status/crm_lead_status.js:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
2 | // For license information, please see license.txt
3 |
4 | // frappe.ui.form.on("CRM Lead Status", {
5 | // refresh(frm) {
6 |
7 | // },
8 | // });
9 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_lead_status/crm_lead_status.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
2 | # For license information, please see license.txt
3 |
4 | # import frappe
5 | from frappe.model.document import Document
6 |
7 |
8 | class CRMLeadStatus(Document):
9 | pass
10 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_lead_status/test_crm_lead_status.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors
2 | # See license.txt
3 |
4 | # import frappe
5 | from frappe.tests import UnitTestCase
6 |
7 |
8 | class TestCRMLeadStatus(UnitTestCase):
9 | pass
10 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_notification/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/fcrm/doctype/crm_notification/__init__.py
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_notification/crm_notification.js:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors
2 | // For license information, please see license.txt
3 |
4 | // frappe.ui.form.on("CRM Notification", {
5 | // refresh(frm) {
6 |
7 | // },
8 | // });
9 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_notification/crm_notification.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors
2 | # For license information, please see license.txt
3 |
4 | import frappe
5 | from frappe import _
6 | from frappe.model.document import Document
7 |
8 |
9 | class CRMNotification(Document):
10 | def on_update(self):
11 | if self.to_user:
12 | frappe.publish_realtime("crm_notification", user= self.to_user)
13 |
14 | def notify_user(args):
15 | """
16 | Notify the assigned user
17 | """
18 | args = frappe._dict(args)
19 | if args.owner == args.assigned_to:
20 | return
21 |
22 | values = frappe._dict(
23 | doctype="CRM Notification",
24 | from_user=args.owner,
25 | to_user=args.assigned_to,
26 | type=args.notification_type,
27 | message=args.message,
28 | notification_text=args.notification_text,
29 | notification_type_doctype=args.reference_doctype,
30 | notification_type_doc=args.reference_docname,
31 | reference_doctype=args.redirect_to_doctype,
32 | reference_name=args.redirect_to_docname,
33 | )
34 |
35 | if frappe.db.exists("CRM Notification", values):
36 | return
37 | frappe.get_doc(values).insert(ignore_permissions=True)
38 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_notification/test_crm_notification.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and Contributors
2 | # See license.txt
3 |
4 | # import frappe
5 | from frappe.tests import UnitTestCase
6 |
7 |
8 | class TestCRMNotification(UnitTestCase):
9 | pass
10 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_organization/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/fcrm/doctype/crm_organization/__init__.py
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_organization/crm_organization.js:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
2 | // For license information, please see license.txt
3 |
4 | // frappe.ui.form.on("CRM Organization", {
5 | // refresh(frm) {
6 |
7 | // },
8 | // });
9 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_organization/crm_organization.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
2 | # For license information, please see license.txt
3 |
4 | import frappe
5 | from frappe.model.document import Document
6 |
7 |
8 | class CRMOrganization(Document):
9 | @staticmethod
10 | def default_list_data():
11 | columns = [
12 | {
13 | 'label': 'Organization',
14 | 'type': 'Data',
15 | 'key': 'organization_name',
16 | 'width': '16rem',
17 | },
18 | {
19 | 'label': 'Website',
20 | 'type': 'Data',
21 | 'key': 'website',
22 | 'width': '14rem',
23 | },
24 | {
25 | 'label': 'Industry',
26 | 'type': 'Link',
27 | 'key': 'industry',
28 | 'options': 'CRM Industry',
29 | 'width': '14rem',
30 | },
31 | {
32 | 'label': 'Annual Revenue',
33 | 'type': 'Currency',
34 | 'key': 'annual_revenue',
35 | 'width': '14rem',
36 | },
37 | {
38 | 'label': 'Last Modified',
39 | 'type': 'Datetime',
40 | 'key': 'modified',
41 | 'width': '8rem',
42 | },
43 | ]
44 | rows = [
45 | "name",
46 | "organization_name",
47 | "organization_logo",
48 | "website",
49 | "industry",
50 | "currency",
51 | "annual_revenue",
52 | "modified",
53 | ]
54 | return {'columns': columns, 'rows': rows}
55 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_organization/test_crm_organization.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors
2 | # See license.txt
3 |
4 | # import frappe
5 | from frappe.tests import UnitTestCase
6 |
7 |
8 | class TestCRMOrganization(UnitTestCase):
9 | pass
10 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_product/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/fcrm/doctype/crm_product/__init__.py
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_product/crm_product.js:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors
2 | // For license information, please see license.txt
3 |
4 | frappe.ui.form.on("CRM Product", {
5 | product_code: function (frm) {
6 | if (!frm.doc.product_name)
7 | frm.set_value("product_name", frm.doc.product_code);
8 | }
9 | });
10 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_product/crm_product.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors
2 | # For license information, please see license.txt
3 |
4 | import frappe
5 | from frappe.model.document import Document
6 |
7 |
8 | class CRMProduct(Document):
9 | def validate(self):
10 | self.set_product_name()
11 |
12 | def set_product_name(self):
13 | if not self.product_name:
14 | self.product_name = self.product_code
15 | else:
16 | self.product_name = self.product_name.strip()
17 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_product/test_crm_product.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and Contributors
2 | # See license.txt
3 |
4 | # import frappe
5 | from frappe.tests import IntegrationTestCase, UnitTestCase
6 |
7 | # On IntegrationTestCase, the doctype test records and all
8 | # link-field test record dependencies are recursively loaded
9 | # Use these module variables to add/remove to/from that list
10 | EXTRA_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"]
11 | IGNORE_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"]
12 |
13 |
14 | class UnitTestCRMProduct(UnitTestCase):
15 | """
16 | Unit tests for CRMProduct.
17 | Use this class for testing individual functions and methods.
18 | """
19 |
20 | pass
21 |
22 |
23 | class IntegrationTestCRMProduct(IntegrationTestCase):
24 | """
25 | Integration tests for CRMProduct.
26 | Use this class for testing interactions between multiple components.
27 | """
28 |
29 | pass
30 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_products/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/fcrm/doctype/crm_products/__init__.py
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_service_day/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/fcrm/doctype/crm_service_day/__init__.py
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_service_day/crm_service_day.json:
--------------------------------------------------------------------------------
1 | {
2 | "actions": [],
3 | "allow_rename": 1,
4 | "creation": "2023-12-04 16:07:20.400084",
5 | "doctype": "DocType",
6 | "editable_grid": 1,
7 | "engine": "InnoDB",
8 | "field_order": [
9 | "workday",
10 | "section_break_uegc",
11 | "start_time",
12 | "column_break_maie",
13 | "end_time"
14 | ],
15 | "fields": [
16 | {
17 | "fieldname": "workday",
18 | "fieldtype": "Select",
19 | "in_list_view": 1,
20 | "label": "Workday",
21 | "options": "Monday\nTuesday\nWednesday\nThursday\nFriday\nSaturday\nSunday",
22 | "reqd": 1
23 | },
24 | {
25 | "fieldname": "section_break_uegc",
26 | "fieldtype": "Section Break"
27 | },
28 | {
29 | "fieldname": "start_time",
30 | "fieldtype": "Time",
31 | "in_list_view": 1,
32 | "label": "Start Time",
33 | "reqd": 1
34 | },
35 | {
36 | "fieldname": "column_break_maie",
37 | "fieldtype": "Column Break"
38 | },
39 | {
40 | "fieldname": "end_time",
41 | "fieldtype": "Time",
42 | "in_list_view": 1,
43 | "label": "End Time",
44 | "reqd": 1
45 | }
46 | ],
47 | "index_web_pages_for_search": 1,
48 | "istable": 1,
49 | "links": [],
50 | "modified": "2023-12-04 16:09:22.928308",
51 | "modified_by": "Administrator",
52 | "module": "FCRM",
53 | "name": "CRM Service Day",
54 | "owner": "Administrator",
55 | "permissions": [],
56 | "sort_field": "modified",
57 | "sort_order": "DESC",
58 | "states": []
59 | }
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_service_day/crm_service_day.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
2 | # For license information, please see license.txt
3 |
4 | # import frappe
5 | from frappe.model.document import Document
6 |
7 |
8 | class CRMServiceDay(Document):
9 | pass
10 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_service_level_agreement/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/fcrm/doctype/crm_service_level_agreement/__init__.py
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_service_level_agreement/crm_service_level_agreement.js:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
2 | // For license information, please see license.txt
3 |
4 | frappe.ui.form.on("CRM Service Level Agreement", {
5 | validate(frm) {
6 | let default_priority_count = 0;
7 | frm.doc.priorities.forEach(function (row) {
8 | if (row.default_priority) {
9 | default_priority_count++;
10 | }
11 | });
12 | if (default_priority_count > 1) {
13 | frappe.throw(
14 | __("There can only be one default priority in Priorities table")
15 | );
16 | }
17 | },
18 | });
19 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_service_level_agreement/test_crm_service_level_agreement.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors
2 | # See license.txt
3 |
4 | # import frappe
5 | from frappe.tests import UnitTestCase
6 |
7 |
8 | class TestCRMServiceLevelAgreement(UnitTestCase):
9 | pass
10 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_service_level_priority/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/fcrm/doctype/crm_service_level_priority/__init__.py
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_service_level_priority/crm_service_level_priority.js:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
2 | // For license information, please see license.txt
3 |
4 | // frappe.ui.form.on("CRM Service Level Priority", {
5 | // refresh(frm) {
6 |
7 | // },
8 | // });
9 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_service_level_priority/crm_service_level_priority.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
2 | # For license information, please see license.txt
3 |
4 | # import frappe
5 | from frappe.model.document import Document
6 |
7 |
8 | class CRMServiceLevelPriority(Document):
9 | pass
10 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_service_level_priority/test_crm_service_level_priority.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors
2 | # See license.txt
3 |
4 | # import frappe
5 | from frappe.tests import UnitTestCase
6 |
7 |
8 | class TestCRMServiceLevelPriority(UnitTestCase):
9 | pass
10 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_status_change_log/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/fcrm/doctype/crm_status_change_log/__init__.py
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_task/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/fcrm/doctype/crm_task/__init__.py
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_task/crm_task.js:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
2 | // For license information, please see license.txt
3 |
4 | // frappe.ui.form.on("CRM Task", {
5 | // refresh(frm) {
6 |
7 | // },
8 | // });
9 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_task/test_crm_task.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors
2 | # See license.txt
3 |
4 | # import frappe
5 | from frappe.tests import UnitTestCase
6 |
7 |
8 | class TestCRMTask(UnitTestCase):
9 | pass
10 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_telephony_agent/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/fcrm/doctype/crm_telephony_agent/__init__.py
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_telephony_agent/crm_telephony_agent.js:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors
2 | // For license information, please see license.txt
3 |
4 | // frappe.ui.form.on("CRM Telephony Agent", {
5 | // refresh(frm) {
6 |
7 | // },
8 | // });
9 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_telephony_agent/crm_telephony_agent.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors
2 | # For license information, please see license.txt
3 |
4 | import frappe
5 | from frappe import _
6 | from frappe.model.document import Document
7 |
8 |
9 | class CRMTelephonyAgent(Document):
10 | def validate(self):
11 | self.set_primary()
12 |
13 | def set_primary(self):
14 | # Used to set primary mobile no.
15 | if len(self.phone_nos) == 0:
16 | self.mobile_no = ""
17 | return
18 |
19 | is_primary = [phone.number for phone in self.phone_nos if phone.get("is_primary")]
20 |
21 | if len(is_primary) > 1:
22 | frappe.throw(
23 | _("Only one {0} can be set as primary.").format(frappe.bold(frappe.unscrub("mobile_no")))
24 | )
25 |
26 | primary_number_exists = False
27 | for d in self.phone_nos:
28 | if d.get("is_primary") == 1:
29 | primary_number_exists = True
30 | self.mobile_no = d.number
31 | break
32 |
33 | if not primary_number_exists:
34 | self.mobile_no = ""
35 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_telephony_agent/test_crm_telephony_agent.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and Contributors
2 | # See license.txt
3 |
4 | # import frappe
5 | from frappe.tests import IntegrationTestCase, UnitTestCase
6 |
7 | # On IntegrationTestCase, the doctype test records and all
8 | # link-field test record dependencies are recursively loaded
9 | # Use these module variables to add/remove to/from that list
10 | EXTRA_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"]
11 | IGNORE_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"]
12 |
13 |
14 | class UnitTestCRMTelephonyAgent(UnitTestCase):
15 | """
16 | Unit tests for CRMTelephonyAgent.
17 | Use this class for testing individual functions and methods.
18 | """
19 |
20 | pass
21 |
22 |
23 | class IntegrationTestCRMTelephonyAgent(IntegrationTestCase):
24 | """
25 | Integration tests for CRMTelephonyAgent.
26 | Use this class for testing interactions between multiple components.
27 | """
28 |
29 | pass
30 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_telephony_phone/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/fcrm/doctype/crm_telephony_phone/__init__.py
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_telephony_phone/crm_telephony_phone.json:
--------------------------------------------------------------------------------
1 | {
2 | "actions": [],
3 | "allow_rename": 1,
4 | "creation": "2025-01-19 13:57:01.702519",
5 | "doctype": "DocType",
6 | "editable_grid": 1,
7 | "engine": "InnoDB",
8 | "field_order": [
9 | "number",
10 | "is_primary"
11 | ],
12 | "fields": [
13 | {
14 | "fieldname": "number",
15 | "fieldtype": "Data",
16 | "in_list_view": 1,
17 | "label": "Number",
18 | "reqd": 1
19 | },
20 | {
21 | "default": "0",
22 | "fieldname": "is_primary",
23 | "fieldtype": "Check",
24 | "in_list_view": 1,
25 | "label": "Is Primary"
26 | }
27 | ],
28 | "index_web_pages_for_search": 1,
29 | "istable": 1,
30 | "links": [],
31 | "modified": "2025-01-19 13:58:59.063775",
32 | "modified_by": "Administrator",
33 | "module": "FCRM",
34 | "name": "CRM Telephony Phone",
35 | "owner": "Administrator",
36 | "permissions": [],
37 | "sort_field": "creation",
38 | "sort_order": "DESC",
39 | "states": []
40 | }
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_telephony_phone/crm_telephony_phone.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors
2 | # For license information, please see license.txt
3 |
4 | # import frappe
5 | from frappe.model.document import Document
6 |
7 |
8 | class CRMTelephonyPhone(Document):
9 | pass
10 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_territory/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/fcrm/doctype/crm_territory/__init__.py
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_territory/crm_territory.js:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors
2 | // For license information, please see license.txt
3 |
4 | // frappe.ui.form.on("CRM Territory", {
5 | // refresh(frm) {
6 |
7 | // },
8 | // });
9 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_territory/crm_territory.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors
2 | # For license information, please see license.txt
3 |
4 | # import frappe
5 | from frappe.model.document import Document
6 |
7 |
8 | class CRMTerritory(Document):
9 | pass
10 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_territory/test_crm_territory.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and Contributors
2 | # See license.txt
3 |
4 | # import frappe
5 | from frappe.tests import UnitTestCase
6 |
7 |
8 | class TestCRMTerritory(UnitTestCase):
9 | pass
10 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_twilio_settings/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/fcrm/doctype/crm_twilio_settings/__init__.py
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_twilio_settings/crm_twilio_settings.js:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
2 | // For license information, please see license.txt
3 |
4 | // frappe.ui.form.on("CRM Twilio Settings", {
5 | // refresh(frm) {
6 |
7 | // },
8 | // });
9 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_twilio_settings/test_crm_twilio_settings.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors
2 | # See license.txt
3 |
4 | # import frappe
5 | from frappe.tests import UnitTestCase
6 |
7 |
8 | class TestCRMTwilioSettings(UnitTestCase):
9 | pass
10 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_view_settings/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/fcrm/doctype/crm_view_settings/__init__.py
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_view_settings/crm_view_settings.js:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
2 | // For license information, please see license.txt
3 |
4 | // frappe.ui.form.on("CRM View Settings", {
5 | // refresh(frm) {
6 |
7 | // },
8 | // });
9 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/crm_view_settings/test_crm_view_settings.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors
2 | # See license.txt
3 |
4 | # import frappe
5 | from frappe.tests import UnitTestCase
6 |
7 |
8 | class TestCRMViewSettings(UnitTestCase):
9 | pass
10 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/erpnext_crm_settings/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/fcrm/doctype/erpnext_crm_settings/__init__.py
--------------------------------------------------------------------------------
/crm/fcrm/doctype/erpnext_crm_settings/erpnext_crm_settings.js:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
2 | // For license information, please see license.txt
3 |
4 | frappe.ui.form.on("ERPNext CRM Settings", {
5 | refresh(frm) {
6 | if (!frm.doc.enabled) return;
7 | frm.add_custom_button(__("Reset ERPNext Form Script"), () => {
8 | frappe.confirm(
9 | __(
10 | "Are you sure you want to reset 'Create Quotation from CRM Deal' Form Script?"
11 | ),
12 | () => frm.trigger("reset_erpnext_form_script")
13 | );
14 | });
15 | },
16 | async reset_erpnext_form_script(frm) {
17 | let script = await frm.call("reset_erpnext_form_script");
18 | script.message &&
19 | frappe.msgprint(__("Form Script updated successfully"));
20 | },
21 | });
22 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/erpnext_crm_settings/test_erpnext_crm_settings.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and Contributors
2 | # See license.txt
3 |
4 | # import frappe
5 | from frappe.tests import UnitTestCase
6 |
7 |
8 | class TestERPNextCRMSettings(UnitTestCase):
9 | pass
10 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/fcrm_note/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/fcrm/doctype/fcrm_note/__init__.py
--------------------------------------------------------------------------------
/crm/fcrm/doctype/fcrm_note/fcrm_note.js:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
2 | // For license information, please see license.txt
3 |
4 | // frappe.ui.form.on("FCRM Note", {
5 | // refresh(frm) {
6 |
7 | // },
8 | // });
9 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/fcrm_note/fcrm_note.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
2 | # For license information, please see license.txt
3 |
4 | # import frappe
5 | from frappe.model.document import Document
6 |
7 |
8 | class FCRMNote(Document):
9 | @staticmethod
10 | def default_list_data():
11 | rows = [
12 | "name",
13 | "title",
14 | "content",
15 | "reference_doctype",
16 | "reference_docname",
17 | "owner",
18 | "modified",
19 | ]
20 | return {'columns': [], 'rows': rows}
21 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/fcrm_note/test_fcrm_note.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors
2 | # See license.txt
3 |
4 | # import frappe
5 | from frappe.tests import UnitTestCase
6 |
7 |
8 | class TestFCRMNote(UnitTestCase):
9 | pass
10 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/fcrm_settings/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/fcrm/doctype/fcrm_settings/__init__.py
--------------------------------------------------------------------------------
/crm/fcrm/doctype/fcrm_settings/fcrm_settings.js:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors
2 | // For license information, please see license.txt
3 |
4 | frappe.ui.form.on("FCRM Settings", {
5 | // refresh(frm) {
6 |
7 | // },
8 | restore_defaults: function (frm) {
9 | let message = __(
10 | "This will restore (if not exist) all the default statuses, custom fields and layouts. Delete & Restore will delete default layouts and then restore them."
11 | );
12 | let d = new frappe.ui.Dialog({
13 | title: __("Restore Defaults"),
14 | primary_action_label: __("Restore"),
15 | primary_action: () => {
16 | frm.call("restore_defaults", { force: false }, () => {
17 | frappe.show_alert({
18 | message: __(
19 | "Default statuses, custom fields and layouts restored successfully."
20 | ),
21 | indicator: "green",
22 | });
23 | });
24 | d.hide();
25 | },
26 | secondary_action_label: __("Delete & Restore"),
27 | secondary_action: () => {
28 | frm.call("restore_defaults", { force: true }, () => {
29 | frappe.show_alert({
30 | message: __(
31 | "Default statuses, custom fields and layouts restored successfully."
32 | ),
33 | indicator: "green",
34 | });
35 | });
36 | d.hide();
37 | },
38 | });
39 | d.show();
40 | d.set_message(message);
41 | },
42 | });
43 |
--------------------------------------------------------------------------------
/crm/fcrm/doctype/fcrm_settings/test_fcrm_settings.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and Contributors
2 | # See license.txt
3 |
4 | # import frappe
5 | from frappe.tests import UnitTestCase
6 |
7 |
8 | class TestFCRMSettings(UnitTestCase):
9 | pass
10 |
--------------------------------------------------------------------------------
/crm/integrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/integrations/__init__.py
--------------------------------------------------------------------------------
/crm/integrations/twilio/utils.py:
--------------------------------------------------------------------------------
1 | from frappe.utils import get_url
2 |
3 |
4 | def get_public_url(path: str | None = None):
5 | return get_url().split(":8", 1)[0] + path
6 |
7 |
8 | def merge_dicts(d1: dict, d2: dict):
9 | """Merge dicts of dictionaries.
10 | >>> merge_dicts(
11 | {'name1': {'age': 20}, 'name2': {'age': 30}},
12 | {'name1': {'phone': '+xxx'}, 'name2': {'phone': '+yyy'}, 'name3': {'phone': '+zzz'}}
13 | )
14 | ... {'name1': {'age': 20, 'phone': '+xxx'}, 'name2': {'age': 30, 'phone': '+yyy'}}
15 | """
16 | return {k: {**v, **d2.get(k, {})} for k, v in d1.items()}
17 |
18 |
--------------------------------------------------------------------------------
/crm/modules.txt:
--------------------------------------------------------------------------------
1 | FCRM
--------------------------------------------------------------------------------
/crm/overrides/contact.py:
--------------------------------------------------------------------------------
1 | # import frappe
2 | from frappe import _
3 | from frappe.contacts.doctype.contact.contact import Contact
4 |
5 |
6 | class CustomContact(Contact):
7 | @staticmethod
8 | def default_list_data():
9 | columns = [
10 | {
11 | 'label': 'Name',
12 | 'type': 'Data',
13 | 'key': 'full_name',
14 | 'width': '17rem',
15 | },
16 | {
17 | 'label': 'Email',
18 | 'type': 'Data',
19 | 'key': 'email_id',
20 | 'width': '12rem',
21 | },
22 | {
23 | 'label': 'Phone',
24 | 'type': 'Data',
25 | 'key': 'mobile_no',
26 | 'width': '12rem',
27 | },
28 | {
29 | 'label': 'Organization',
30 | 'type': 'Data',
31 | 'key': 'company_name',
32 | 'width': '12rem',
33 | },
34 | {
35 | 'label': 'Last Modified',
36 | 'type': 'Datetime',
37 | 'key': 'modified',
38 | 'width': '8rem',
39 | },
40 | ]
41 | rows = [
42 | "name",
43 | "full_name",
44 | "company_name",
45 | "email_id",
46 | "mobile_no",
47 | "modified",
48 | "image",
49 | ]
50 | return {'columns': columns, 'rows': rows}
51 |
--------------------------------------------------------------------------------
/crm/overrides/email_template.py:
--------------------------------------------------------------------------------
1 | # import frappe
2 | from frappe import _
3 | from frappe.email.doctype.email_template.email_template import EmailTemplate
4 |
5 |
6 | class CustomEmailTemplate(EmailTemplate):
7 | @staticmethod
8 | def default_list_data():
9 | columns = [
10 | {
11 | 'label': 'Name',
12 | 'type': 'Data',
13 | 'key': 'name',
14 | 'width': '17rem',
15 | },
16 | {
17 | 'label': 'Subject',
18 | 'type': 'Data',
19 | 'key': 'subject',
20 | 'width': '12rem',
21 | },
22 | {
23 | 'label': 'Enabled',
24 | 'type': 'Check',
25 | 'key': 'enabled',
26 | 'width': '6rem',
27 | },
28 | {
29 | 'label': 'Doctype',
30 | 'type': 'Link',
31 | 'key': 'reference_doctype',
32 | 'width': '12rem',
33 | },
34 | {
35 | 'label': 'Last Modified',
36 | 'type': 'Datetime',
37 | 'key': 'modified',
38 | 'width': '8rem',
39 | },
40 | ]
41 | rows = [
42 | "name",
43 | "enabled",
44 | "use_html",
45 | "reference_doctype",
46 | "subject",
47 | "response",
48 | "response_html",
49 | "modified",
50 | ]
51 | return {'columns': columns, 'rows': rows}
52 |
--------------------------------------------------------------------------------
/crm/patches.txt:
--------------------------------------------------------------------------------
1 | [pre_model_sync]
2 | # Patches added in this section will be executed before doctypes are migrated
3 | # Read docs to understand patches: https://frappeframework.com/docs/v14/user/en/database-migrations
4 | crm.patches.v1_0.move_crm_note_data_to_fcrm_note
5 | crm.patches.v1_0.rename_twilio_settings_to_crm_twilio_settings
6 |
7 | [post_model_sync]
8 | # Patches added in this section will be executed after doctypes are migrated
9 | crm.patches.v1_0.create_email_template_custom_fields
10 | crm.patches.v1_0.create_default_fields_layout #22/01/2025
11 | crm.patches.v1_0.create_default_sidebar_fields_layout
12 | crm.patches.v1_0.update_deal_quick_entry_layout
13 | crm.patches.v1_0.update_layouts_to_new_format
14 | crm.patches.v1_0.move_twilio_agent_to_telephony_agent
15 | crm.patches.v1_0.create_default_scripts
--------------------------------------------------------------------------------
/crm/patches/v1_0/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/patches/v1_0/__init__.py
--------------------------------------------------------------------------------
/crm/patches/v1_0/create_default_fields_layout.py:
--------------------------------------------------------------------------------
1 | from crm.install import add_default_fields_layout
2 |
3 |
4 | def execute():
5 | add_default_fields_layout()
6 |
--------------------------------------------------------------------------------
/crm/patches/v1_0/create_default_scripts.py:
--------------------------------------------------------------------------------
1 | from crm.install import add_default_scripts
2 |
3 |
4 | def execute():
5 | add_default_scripts()
6 |
--------------------------------------------------------------------------------
/crm/patches/v1_0/create_email_template_custom_fields.py:
--------------------------------------------------------------------------------
1 |
2 | from crm.install import add_email_template_custom_fields
3 |
4 | def execute():
5 | add_email_template_custom_fields()
--------------------------------------------------------------------------------
/crm/patches/v1_0/move_crm_note_data_to_fcrm_note.py:
--------------------------------------------------------------------------------
1 | import frappe
2 | from frappe.model.rename_doc import rename_doc
3 |
4 |
5 | def execute():
6 |
7 | if not frappe.db.exists("DocType", "FCRM Note"):
8 | frappe.flags.ignore_route_conflict_validation = True
9 | rename_doc("DocType", "CRM Note", "FCRM Note")
10 | frappe.flags.ignore_route_conflict_validation = False
11 |
12 | frappe.reload_doctype("FCRM Note", force=True)
13 |
14 | if frappe.db.exists("DocType", "FCRM Note") and frappe.db.count("FCRM Note") > 0:
15 | return
16 |
17 | notes = frappe.db.sql("SELECT * FROM `tabCRM Note`", as_dict=True)
18 | if notes:
19 | for note in notes:
20 | doc = frappe.get_doc({
21 | "doctype": "FCRM Note",
22 | "creation": note.get("creation"),
23 | "modified": note.get("modified"),
24 | "modified_by": note.get("modified_by"),
25 | "owner": note.get("owner"),
26 | "title": note.get("title"),
27 | "content": note.get("content"),
28 | "reference_doctype": note.get("reference_doctype"),
29 | "reference_docname": note.get("reference_docname"),
30 | })
31 | doc.db_insert()
--------------------------------------------------------------------------------
/crm/patches/v1_0/move_twilio_agent_to_telephony_agent.py:
--------------------------------------------------------------------------------
1 | import frappe
2 |
3 |
4 | def execute():
5 | if not frappe.db.exists("DocType", "CRM Telephony Agent"):
6 | frappe.reload_doctype("CRM Telephony Agent", force=True)
7 |
8 | if frappe.db.exists("DocType", "Twilio Agents") and frappe.db.count("Twilio Agents") == 0:
9 | return
10 |
11 | agents = frappe.db.sql("SELECT * FROM `tabTwilio Agents`", as_dict=True)
12 | if agents:
13 | for agent in agents:
14 | doc = frappe.get_doc(
15 | {
16 | "doctype": "CRM Telephony Agent",
17 | "creation": agent.get("creation"),
18 | "modified": agent.get("modified"),
19 | "modified_by": agent.get("modified_by"),
20 | "owner": agent.get("owner"),
21 | "user": agent.get("user"),
22 | "twilio_number": agent.get("twilio_number"),
23 | "user_name": agent.get("user_name"),
24 | "twilio": True,
25 | }
26 | )
27 | doc.db_insert()
28 |
--------------------------------------------------------------------------------
/crm/patches/v1_0/rename_twilio_settings_to_crm_twilio_settings.py:
--------------------------------------------------------------------------------
1 | import frappe
2 | from frappe.model.rename_doc import rename_doc
3 |
4 |
5 | def execute():
6 | if frappe.db.exists("DocType", "Twilio Settings"):
7 | frappe.flags.ignore_route_conflict_validation = True
8 | rename_doc("DocType", "Twilio Settings", "CRM Twilio Settings")
9 | frappe.flags.ignore_route_conflict_validation = False
10 |
11 | frappe.reload_doctype("CRM Twilio Settings", force=True)
12 |
13 | if frappe.db.exists("__Auth", {"doctype": "Twilio Settings"}):
14 | Auth = frappe.qb.DocType("__Auth")
15 | result = frappe.qb.from_(Auth).select("*").where(Auth.doctype == "Twilio Settings").run(as_dict=True)
16 |
17 | for row in result:
18 | frappe.qb.into(Auth).insert(
19 | "CRM Twilio Settings", "CRM Twilio Settings", row.fieldname, row.password, row.encrypted
20 | ).run()
21 |
--------------------------------------------------------------------------------
/crm/patches/v1_0/update_deal_quick_entry_layout.py:
--------------------------------------------------------------------------------
1 | import json
2 | import frappe
3 |
4 | def execute():
5 | if not frappe.db.exists("CRM Fields Layout", "CRM Deal-Quick Entry"):
6 | return
7 |
8 | deal = frappe.db.get_value("CRM Fields Layout", "CRM Deal-Quick Entry", "layout")
9 |
10 | layout = json.loads(deal)
11 | for section in layout:
12 | if section.get("label") in ["Select Organization", "Organization Details", "Select Contact", "Contact Details"]:
13 | section["editable"] = False
14 |
15 | frappe.db.set_value("CRM Fields Layout", "CRM Deal-Quick Entry", "layout", json.dumps(layout))
--------------------------------------------------------------------------------
/crm/public/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/public/.gitkeep
--------------------------------------------------------------------------------
/crm/public/images/desk.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/public/images/desk.png
--------------------------------------------------------------------------------
/crm/public/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/public/images/logo.png
--------------------------------------------------------------------------------
/crm/public/images/logo.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/crm/public/manifest/apple-icon-180.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/public/manifest/apple-icon-180.png
--------------------------------------------------------------------------------
/crm/public/manifest/apple-splash-1125-2436.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/public/manifest/apple-splash-1125-2436.jpg
--------------------------------------------------------------------------------
/crm/public/manifest/apple-splash-1136-640.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/public/manifest/apple-splash-1136-640.jpg
--------------------------------------------------------------------------------
/crm/public/manifest/apple-splash-1170-2532.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/public/manifest/apple-splash-1170-2532.jpg
--------------------------------------------------------------------------------
/crm/public/manifest/apple-splash-1179-2556.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/public/manifest/apple-splash-1179-2556.jpg
--------------------------------------------------------------------------------
/crm/public/manifest/apple-splash-1242-2208.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/public/manifest/apple-splash-1242-2208.jpg
--------------------------------------------------------------------------------
/crm/public/manifest/apple-splash-1242-2688.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/public/manifest/apple-splash-1242-2688.jpg
--------------------------------------------------------------------------------
/crm/public/manifest/apple-splash-1284-2778.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/public/manifest/apple-splash-1284-2778.jpg
--------------------------------------------------------------------------------
/crm/public/manifest/apple-splash-1290-2796.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/public/manifest/apple-splash-1290-2796.jpg
--------------------------------------------------------------------------------
/crm/public/manifest/apple-splash-1334-750.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/public/manifest/apple-splash-1334-750.jpg
--------------------------------------------------------------------------------
/crm/public/manifest/apple-splash-1488-2266.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/public/manifest/apple-splash-1488-2266.jpg
--------------------------------------------------------------------------------
/crm/public/manifest/apple-splash-1536-2048.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/public/manifest/apple-splash-1536-2048.jpg
--------------------------------------------------------------------------------
/crm/public/manifest/apple-splash-1620-2160.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/public/manifest/apple-splash-1620-2160.jpg
--------------------------------------------------------------------------------
/crm/public/manifest/apple-splash-1640-2360.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/public/manifest/apple-splash-1640-2360.jpg
--------------------------------------------------------------------------------
/crm/public/manifest/apple-splash-1668-2224.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/public/manifest/apple-splash-1668-2224.jpg
--------------------------------------------------------------------------------
/crm/public/manifest/apple-splash-1668-2388.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/public/manifest/apple-splash-1668-2388.jpg
--------------------------------------------------------------------------------
/crm/public/manifest/apple-splash-1792-828.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/public/manifest/apple-splash-1792-828.jpg
--------------------------------------------------------------------------------
/crm/public/manifest/apple-splash-2048-1536.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/public/manifest/apple-splash-2048-1536.jpg
--------------------------------------------------------------------------------
/crm/public/manifest/apple-splash-2048-2732.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/public/manifest/apple-splash-2048-2732.jpg
--------------------------------------------------------------------------------
/crm/public/manifest/apple-splash-2160-1620.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/public/manifest/apple-splash-2160-1620.jpg
--------------------------------------------------------------------------------
/crm/public/manifest/apple-splash-2208-1242.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/public/manifest/apple-splash-2208-1242.jpg
--------------------------------------------------------------------------------
/crm/public/manifest/apple-splash-2224-1668.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/public/manifest/apple-splash-2224-1668.jpg
--------------------------------------------------------------------------------
/crm/public/manifest/apple-splash-2266-1488.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/public/manifest/apple-splash-2266-1488.jpg
--------------------------------------------------------------------------------
/crm/public/manifest/apple-splash-2360-1640.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/public/manifest/apple-splash-2360-1640.jpg
--------------------------------------------------------------------------------
/crm/public/manifest/apple-splash-2388-1668.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/public/manifest/apple-splash-2388-1668.jpg
--------------------------------------------------------------------------------
/crm/public/manifest/apple-splash-2436-1125.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/public/manifest/apple-splash-2436-1125.jpg
--------------------------------------------------------------------------------
/crm/public/manifest/apple-splash-2532-1170.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/public/manifest/apple-splash-2532-1170.jpg
--------------------------------------------------------------------------------
/crm/public/manifest/apple-splash-2556-1179.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/public/manifest/apple-splash-2556-1179.jpg
--------------------------------------------------------------------------------
/crm/public/manifest/apple-splash-2688-1242.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/public/manifest/apple-splash-2688-1242.jpg
--------------------------------------------------------------------------------
/crm/public/manifest/apple-splash-2732-2048.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/public/manifest/apple-splash-2732-2048.jpg
--------------------------------------------------------------------------------
/crm/public/manifest/apple-splash-2778-1284.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/public/manifest/apple-splash-2778-1284.jpg
--------------------------------------------------------------------------------
/crm/public/manifest/apple-splash-2796-1290.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/public/manifest/apple-splash-2796-1290.jpg
--------------------------------------------------------------------------------
/crm/public/manifest/apple-splash-640-1136.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/public/manifest/apple-splash-640-1136.jpg
--------------------------------------------------------------------------------
/crm/public/manifest/apple-splash-750-1334.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/public/manifest/apple-splash-750-1334.jpg
--------------------------------------------------------------------------------
/crm/public/manifest/apple-splash-828-1792.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/public/manifest/apple-splash-828-1792.jpg
--------------------------------------------------------------------------------
/crm/public/manifest/manifest-icon-192.maskable.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/public/manifest/manifest-icon-192.maskable.png
--------------------------------------------------------------------------------
/crm/public/manifest/manifest-icon-512.maskable.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/public/manifest/manifest-icon-512.maskable.png
--------------------------------------------------------------------------------
/crm/public/videos/changeDealStatus.mov:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/public/videos/changeDealStatus.mov
--------------------------------------------------------------------------------
/crm/public/videos/convertToDeal.mov:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/public/videos/convertToDeal.mov
--------------------------------------------------------------------------------
/crm/templates/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/templates/__init__.py
--------------------------------------------------------------------------------
/crm/templates/emails/crm_invitation.html:
--------------------------------------------------------------------------------
1 |
You have been invited to join Frappe CRM
2 |3 | Accept Invitation 4 |
5 | -------------------------------------------------------------------------------- /crm/templates/pages/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/templates/pages/__init__.py -------------------------------------------------------------------------------- /crm/uninstall.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors 2 | # MIT License. See license.txt 3 | import click 4 | import frappe 5 | 6 | 7 | def before_uninstall(): 8 | delete_email_template_custom_fields() 9 | 10 | 11 | def delete_email_template_custom_fields(): 12 | if frappe.get_meta("Email Template").has_field("enabled"): 13 | click.secho("* Uninstalling Custom Fields from Email Template") 14 | 15 | fieldnames = ( 16 | "enabled", 17 | "reference_doctype", 18 | ) 19 | 20 | for fieldname in fieldnames: 21 | frappe.db.delete("Custom Field", {"name": "Email Template-" + fieldname}) 22 | 23 | frappe.clear_cache(doctype="Email Template") 24 | -------------------------------------------------------------------------------- /crm/www/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/crm/www/__init__.py -------------------------------------------------------------------------------- /crowdin.yml: -------------------------------------------------------------------------------- 1 | files: 2 | - source: /crm/locale/main.pot 3 | translation: /crm/locale/%two_letters_code%.po 4 | -------------------------------------------------------------------------------- /docker/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.7" 2 | name: crm 3 | services: 4 | mariadb: 5 | image: mariadb:10.8 6 | command: 7 | - --character-set-server=utf8mb4 8 | - --collation-server=utf8mb4_unicode_ci 9 | - --skip-character-set-client-handshake 10 | - --skip-innodb-read-only-compressed # Temporary fix for MariaDB 10.6 11 | environment: 12 | MYSQL_ROOT_PASSWORD: 123 13 | volumes: 14 | - mariadb-data:/var/lib/mysql 15 | 16 | redis: 17 | image: redis:alpine 18 | 19 | frappe: 20 | image: frappe/bench:latest 21 | command: bash /workspace/init.sh 22 | environment: 23 | - SHELL=/bin/bash 24 | working_dir: /home/frappe 25 | volumes: 26 | - .:/workspace 27 | ports: 28 | - 8000:8000 29 | - 9000:9000 30 | 31 | volumes: 32 | mariadb-data: -------------------------------------------------------------------------------- /docker/init.sh: -------------------------------------------------------------------------------- 1 | #!bin/bash 2 | 3 | if [ -d "/home/frappe/frappe-bench/apps/frappe" ]; then 4 | echo "Bench already exists, skipping init" 5 | cd frappe-bench 6 | bench start 7 | else 8 | echo "Creating new bench..." 9 | fi 10 | 11 | bench init --skip-redis-config-generation frappe-bench 12 | 13 | cd frappe-bench 14 | 15 | # Use containers instead of localhost 16 | bench set-mariadb-host mariadb 17 | bench set-redis-cache-host redis:6379 18 | bench set-redis-queue-host redis:6379 19 | bench set-redis-socketio-host redis:6379 20 | 21 | # Remove redis, watch from Procfile 22 | sed -i '/redis/d' ./Procfile 23 | sed -i '/watch/d' ./Procfile 24 | 25 | bench get-app crm --branch develop 26 | 27 | bench new-site crm.localhost \ 28 | --force \ 29 | --mariadb-root-password 123 \ 30 | --admin-password admin \ 31 | --no-mariadb-socket 32 | 33 | bench --site crm.localhost install-app crm 34 | bench --site crm.localhost set-config developer_mode 1 35 | bench --site crm.localhost clear-cache 36 | bench --site crm.localhost set-config mute_emails 1 37 | bench use crm.localhost 38 | 39 | bench start -------------------------------------------------------------------------------- /frontend/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | dist-ssr 5 | *.local -------------------------------------------------------------------------------- /frontend/.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "singleQuote": true 4 | } 5 | -------------------------------------------------------------------------------- /frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "crm-ui", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build --base=/assets/crm/frontend/ && yarn copy-html-entry", 9 | "copy-html-entry": "cp ../crm/public/frontend/index.html ../crm/www/crm.html", 10 | "serve": "vite preview" 11 | }, 12 | "dependencies": { 13 | "@tiptap/extension-paragraph": "^2.12.0", 14 | "@twilio/voice-sdk": "^2.10.2", 15 | "@vueuse/integrations": "^10.3.0", 16 | "frappe-ui": "^0.1.145", 17 | "gemoji": "^8.1.0", 18 | "lodash": "^4.17.21", 19 | "mime": "^4.0.1", 20 | "pinia": "^2.0.33", 21 | "socket.io-client": "^4.7.2", 22 | "sortablejs": "^1.15.0", 23 | "vue": "^3.5.13", 24 | "vue-router": "^4.2.2", 25 | "vuedraggable": "^4.1.0" 26 | }, 27 | "devDependencies": { 28 | "@vitejs/plugin-vue": "^4.2.3", 29 | "@vitejs/plugin-vue-jsx": "^3.0.1", 30 | "autoprefixer": "^10.4.14", 31 | "postcss": "^8.4.5", 32 | "tailwindcss": "^3.4.15", 33 | "vite": "^4.4.9", 34 | "vite-plugin-pwa": "^0.15.0" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /frontend/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /frontend/public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/crm/895da1a8125581e8b9476ace591123ab59d7b3d3/frontend/public/favicon.png -------------------------------------------------------------------------------- /frontend/src/App.vue: -------------------------------------------------------------------------------- 1 | 2 |11 | {{ attrs.description }} 12 |
13 |12 | {{ serviceName }} 13 |
14 | 15 | 16 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /frontend/src/components/Settings/WhatsAppSettings.vue: -------------------------------------------------------------------------------- 1 | 2 |Here is a list of email templates
5 |