Compare commits

..

691 Commits

Author SHA1 Message Date
frappe-pr-bot
fa14d4ad15 chore: update POT file 2025-08-03 09:38:21 +00:00
Shariq Ansari
44deb5878c
Merge pull request #1103 from frappe/l10n_develop3
chore: sync translations from crowdin
2025-08-03 13:59:40 +05:30
Shariq Ansari
2601c1d059 chore: Persian translations 2025-08-03 13:43:25 +05:30
Shariq Ansari
7e51d96379 chore: Indonesian translations 2025-08-03 13:43:12 +05:30
Shariq Ansari
4c53dd6ea3 chore: Persian translations 2025-08-01 13:09:10 +05:30
Shariq Ansari
c64dcb43b4
Merge pull request #1095 from frappe/l10n_develop3 2025-07-31 16:38:53 +05:30
Shariq Ansari
7dafba9fc3
Merge pull request #1100 from shariquerik/bug-fix 2025-07-31 16:32:03 +05:30
Shariq Ansari
abc501825a fix: update delete button click handler in Deal component 2025-07-31 16:30:25 +05:30
Shariq Ansari
3ad2a56efb
Merge pull request #1094 from C-L-STARK/patch-1 2025-07-31 16:28:38 +05:30
Shariq Ansari
c53e486bf0 fix: update init scripts for consistent Redis configuration and branch usage 2025-07-31 16:02:00 +05:30
Shariq Ansari
d852fe1e9f
Merge pull request #1096 from shariquerik/refactor-2 2025-07-30 18:13:41 +05:30
Shariq Ansari
d431d5b4b1 refactor: remove unused get_lead function and related imports from api.py 2025-07-30 17:55:47 +05:30
Shariq Ansari
ba99d14f68 refactor: clean up unused variables and streamline tab management in Deal, Lead, and MobileLead components 2025-07-30 17:50:16 +05:30
Shariq Ansari
7e42599b49 refactor: update mobile lead/deal components 2025-07-30 17:31:22 +05:30
Shariq Ansari
c38c190d42 fix: update docname references to use dealId and leadId in Deal and Lead components 2025-07-30 16:43:40 +05:30
Shariq Ansari
6e8d869afb fix: update whatsappMessages auto property based on whatsappEnabled state 2025-07-30 16:24:19 +05:30
Shariq Ansari
63d6062673 refactor: update button icon rendering in AttachmentArea component 2025-07-30 16:12:21 +05:30
Shariq Ansari
af830b8782 refactor: use doc in activity & child components 2025-07-30 16:11:49 +05:30
Shariq Ansari
0605cf5fd0 refactor: updateField & deleteDeal/Lead 2025-07-30 15:16:13 +05:30
Shariq Ansari
4acb4dd3a7 fix: remount on route change with param and hash change 2025-07-30 15:13:38 +05:30
Shariq Ansari
72c31b9f21 chore: Croatian translations 2025-07-30 13:11:43 +05:30
Shariq Ansari
f2b1c24d19 chore: Serbian (Latin) translations 2025-07-30 13:11:28 +05:30
Shariq Ansari
f86bb0ada2 chore: Bosnian translations 2025-07-30 13:11:27 +05:30
Shariq Ansari
ec4866f39a chore: Indonesian translations 2025-07-30 13:11:26 +05:30
Shariq Ansari
7961b614f1 chore: Swedish translations 2025-07-30 13:11:24 +05:30
Shariq Ansari
901d84d070 chore: Serbian (Cyrillic) translations 2025-07-30 13:11:22 +05:30
Shariq Ansari
0144bc109a fix: use document.doc instead of lead.data/deal.data 2025-07-30 12:56:27 +05:30
Shariq Ansari
ca5d82f5be fix: handle errors when creating document resources 2025-07-30 12:50:35 +05:30
C.L.STARK
b6a6152a49
Update init.sh
Fixed: docker setup error in crm-frappe container.
2025-07-30 05:11:40 +08:00
Shariq Ansari
5e19a83f8a
Merge pull request #1090 from frappe/l10n_develop3 2025-07-29 16:10:10 +05:30
Shariq Ansari
cd30e9d533
Merge pull request #1091 from shariquerik/modified-error 2025-07-29 14:13:59 +05:30
Shariq Ansari
ea0011771b fix: reload doc after sending email 2025-07-29 14:08:11 +05:30
Shariq Ansari
94b0077b2a chore: Esperanto translations 2025-07-29 12:32:06 +05:30
Shariq Ansari
ea815d0147 chore: Croatian translations 2025-07-29 12:32:04 +05:30
Shariq Ansari
0ae048d396 chore: Thai translations 2025-07-29 12:32:03 +05:30
Shariq Ansari
972558396e chore: Persian translations 2025-07-29 12:32:02 +05:30
Shariq Ansari
c218241e80 chore: Vietnamese translations 2025-07-29 12:32:00 +05:30
Shariq Ansari
e616e69aa4 chore: Chinese Simplified translations 2025-07-29 12:31:59 +05:30
Shariq Ansari
2627471b23 chore: Turkish translations 2025-07-29 12:31:58 +05:30
Shariq Ansari
a2c42ee5a7 chore: Russian translations 2025-07-29 12:31:56 +05:30
Shariq Ansari
8d89caeba7 chore: Portuguese translations 2025-07-29 12:31:55 +05:30
Shariq Ansari
b0393b532c chore: Dutch translations 2025-07-29 12:31:54 +05:30
Shariq Ansari
2ea875f2cb chore: Hungarian translations 2025-07-29 12:31:53 +05:30
Shariq Ansari
d1d2900847 chore: Czech translations 2025-07-29 12:31:51 +05:30
Shariq Ansari
98d851d76d chore: Arabic translations 2025-07-29 12:31:50 +05:30
Shariq Ansari
30fdc1db4c chore: Spanish translations 2025-07-29 12:31:49 +05:30
Shariq Ansari
12df15154d chore: French translations 2025-07-29 12:31:47 +05:30
Shariq Ansari
0bf6c01f91 chore: German translations 2025-07-29 12:31:46 +05:30
Shariq Ansari
497263f367 chore: Serbian (Latin) translations 2025-07-29 12:31:44 +05:30
Shariq Ansari
cd458d6d22 chore: Bosnian translations 2025-07-29 12:31:43 +05:30
Shariq Ansari
a2b55166ed chore: Indonesian translations 2025-07-29 12:31:41 +05:30
Shariq Ansari
570d31b6d1 chore: Portuguese, Brazilian translations 2025-07-29 12:31:40 +05:30
Shariq Ansari
2483dd6828 chore: Swedish translations 2025-07-29 12:31:38 +05:30
Shariq Ansari
1adef98c57 chore: Serbian (Cyrillic) translations 2025-07-29 12:31:37 +05:30
Shariq Ansari
dfd3c8f2bf chore: Polish translations 2025-07-29 12:31:36 +05:30
Shariq Ansari
015c592978 chore: Italian translations 2025-07-29 12:31:34 +05:30
Shariq Ansari
8984cea367
Merge pull request #1087 from shariquerik/bug 2025-07-29 11:34:25 +05:30
Shariq Ansari
c3cf63dfb7 fix: format NumberChart component for better readability 2025-07-29 11:32:49 +05:30
Shariq Ansari
c532b61ef6 fix: fields should not be mandatory in single doctype 2025-07-29 11:32:37 +05:30
Shariq Ansari
491fbb7801
Merge pull request #1084 from shariquerik/exchange_rate_fix-1 2025-07-28 18:05:34 +05:30
Shariq Ansari
4c7a40d8bc fix: fixed labels 2025-07-28 18:01:36 +05:30
Shariq Ansari
c866d1e836
Merge pull request #1082 from shariquerik/exchange_rate_fix 2025-07-28 17:57:59 +05:30
Shariq Ansari
c72160b2a4 fix: remove unused Link component import from GeneralSettings.vue 2025-07-28 17:52:58 +05:30
Shariq Ansari
3143f14e0b
Merge branch 'develop' into exchange_rate_fix 2025-07-28 17:51:03 +05:30
Shariq Ansari
e06a365029 fix: update description for mandatory fields in deal value forecasting 2025-07-28 17:49:52 +05:30
Shariq Ansari
36e79e49da fix: added exchange rate provider & currency together in settings modal 2025-07-28 17:48:26 +05:30
Shariq Ansari
5bee7022b2 fix: removed currency exchange rate settings and moved it to crm settings 2025-07-28 17:47:20 +05:30
Shariq Ansari
e578513eaf fix: Improve error handling in document update process 2025-07-28 16:43:46 +05:30
Meer Uzair
8de2a69a99 fix: Correct field name from 'agent' to 'user' in CRM Telephony Agent creation
- The `CRM Telephony Agent` doctype requires the field `user` (not `agent`) as per its schema (`reqd: 1` + `autoname: field:user`).
- This fixes the `ValidationError: User is required` by using the correct field name when creating a new agent.
2025-07-28 16:23:52 +05:30
frappe-pr-bot
890299c2f3 chore: update POT file 2025-07-28 16:23:52 +05:30
Shariq Ansari
2a6f1c402b chore: Serbian (Latin) translations 2025-07-28 16:23:52 +05:30
Shariq Ansari
25d2b1889f chore: Serbian (Cyrillic) translations 2025-07-28 16:23:52 +05:30
Shariq Ansari
ee843fae26 chore: Serbian (Latin) translations 2025-07-28 16:23:52 +05:30
Shariq Ansari
fea35205b7 chore: Bosnian translations 2025-07-28 16:23:52 +05:30
Shariq Ansari
ee28543180 chore: Indonesian translations 2025-07-28 16:23:52 +05:30
Shariq Ansari
4d7a66f1d8 chore: Swedish translations 2025-07-28 16:23:52 +05:30
Shariq Ansari
3b9ff8d58f chore: Serbian (Cyrillic) translations 2025-07-28 16:23:52 +05:30
Shariq Ansari
b46d5a4e5e fix: update exchange rate fetching logic to include service provider context and improve error handling 2025-07-28 16:20:08 +05:30
Shariq Ansari
23a823f2bb feat: enhance document handling with error triggering and settings helpers via app 2025-07-28 16:19:49 +05:30
Shariq Ansari
56ef00536a
Merge pull request #1077 from MeerUzairWasHere/patch-1 2025-07-28 15:56:07 +05:30
Shariq Ansari
0081006a64
Merge pull request #1076 from frappe/pot_develop_2025-07-27 2025-07-28 15:53:51 +05:30
Shariq Ansari
1c98d81c2c
Merge pull request #1074 from frappe/l10n_develop3 2025-07-28 15:53:33 +05:30
Shariq Ansari
7e9d9a5fed fix: moved get_exchange_rate api to exchange rate settings 2025-07-28 13:00:31 +05:30
Shariq Ansari
45826e0a88 fix: added currency exchange rate settings 2025-07-28 12:59:34 +05:30
Shariq Ansari
8815433962 chore: Serbian (Latin) translations 2025-07-28 12:31:04 +05:30
Shariq Ansari
e4261820d4 chore: Serbian (Cyrillic) translations 2025-07-28 12:31:03 +05:30
Shariq Ansari
5bfcaf4809 fix: get latest exchange rate 2025-07-28 12:12:34 +05:30
Meer Uzair
7f4f6ff651
fix: Correct field name from 'agent' to 'user' in CRM Telephony Agent creation
- The `CRM Telephony Agent` doctype requires the field `user` (not `agent`) as per its schema (`reqd: 1` + `autoname: field:user`).
- This fixes the `ValidationError: User is required` by using the correct field name when creating a new agent.
2025-07-27 22:58:43 +05:30
frappe-pr-bot
7d9b7e92fb chore: update POT file 2025-07-27 09:38:13 +00:00
Shariq Ansari
63963414c1 chore: Serbian (Latin) translations 2025-07-26 12:04:14 +05:30
Shariq Ansari
f0ee245f75 chore: Bosnian translations 2025-07-26 12:04:13 +05:30
Shariq Ansari
a4710a8b85 chore: Indonesian translations 2025-07-26 12:04:12 +05:30
Shariq Ansari
201ad61e6d chore: Swedish translations 2025-07-26 12:04:10 +05:30
Shariq Ansari
bbb9eefc9a chore: Serbian (Cyrillic) translations 2025-07-26 12:04:08 +05:30
Shariq Ansari
d53b503805
Merge pull request #1070 from shariquerik/log-call-fix 2025-07-24 13:14:47 +05:30
Shariq Ansari
b1d90952c4 refactor: MultiActionButton dropdown 2025-07-24 13:10:58 +05:30
Shariq Ansari
742bd6e213 fix: update label for call log actions to "Log a Call" 2025-07-24 12:47:14 +05:30
Shariq Ansari
e0abb53d4c
Merge pull request #1068 from shariquerik/primary-mobile-no-fix 2025-07-24 12:32:26 +05:30
Shariq Ansari
8e08e6f415 fix: make mobile no primary on create of contact 2025-07-24 12:29:54 +05:30
Shariq Ansari
e883ea1346
Merge pull request #1066 from shariquerik/compatibility 2025-07-24 11:56:24 +05:30
Shariq Ansari
0327b37d2f docs: add compatibility section with supported Frappe and ERPNext versions in README 2025-07-24 11:54:01 +05:30
Shariq Ansari
699feead15
Merge pull request #1064 from frappe/l10n_develop3 2025-07-24 11:28:50 +05:30
Shariq Ansari
a08fabaed9 chore: Bosnian translations 2025-07-24 11:26:11 +05:30
Shariq Ansari
61e276df37 chore: Croatian translations 2025-07-24 11:26:09 +05:30
Shariq Ansari
076762e2f3
Merge pull request #1059 from shariquerik/refactor-1 2025-07-23 13:35:55 +05:30
Shariq Ansari
9761989ea4 fix: improve exchange rate fetching with retry logic and default return value 2025-07-23 13:33:41 +05:30
Shariq Ansari
5ae7698704
Merge pull request #1057 from frappe/l10n_develop3 2025-07-23 13:23:30 +05:30
Shariq Ansari
c84ac29332 refactor: rename update_close_date to update_closed_date and adjust related logic 2025-07-23 13:21:02 +05:30
Shariq Ansari
cf8a1ce8a3 refactor: cache assignees 2025-07-23 13:12:12 +05:30
Shariq Ansari
1507701981 chore: Serbian (Latin) translations 2025-07-23 10:58:29 +05:30
Shariq Ansari
b4796efed1 chore: Bosnian translations 2025-07-23 10:58:28 +05:30
Shariq Ansari
273f7d7f07 chore: Croatian translations 2025-07-23 10:58:26 +05:30
Shariq Ansari
8474630f4c chore: Thai translations 2025-07-23 10:58:25 +05:30
Shariq Ansari
907ec5fa3c chore: Persian translations 2025-07-23 10:58:24 +05:30
Shariq Ansari
43001bee9f chore: Indonesian translations 2025-07-23 10:58:22 +05:30
Shariq Ansari
5479a6b885 chore: Portuguese, Brazilian translations 2025-07-23 10:58:21 +05:30
Shariq Ansari
594f7922f9 chore: Vietnamese translations 2025-07-23 10:58:19 +05:30
Shariq Ansari
7aa9f768ea chore: Chinese Simplified translations 2025-07-23 10:58:18 +05:30
Shariq Ansari
b796a30e1f chore: Turkish translations 2025-07-23 10:58:16 +05:30
Shariq Ansari
5508eb013a chore: Swedish translations 2025-07-23 10:58:14 +05:30
Shariq Ansari
a3c7af7c8c chore: Serbian (Cyrillic) translations 2025-07-23 10:58:13 +05:30
Shariq Ansari
d689bf21d2 chore: Russian translations 2025-07-23 10:58:12 +05:30
Shariq Ansari
05ea067361 chore: Portuguese translations 2025-07-23 10:58:10 +05:30
Shariq Ansari
2af8710a6f chore: Polish translations 2025-07-23 10:58:08 +05:30
Shariq Ansari
6abc9f7f9f chore: Dutch translations 2025-07-23 10:58:07 +05:30
Shariq Ansari
7de21b7015 chore: Italian translations 2025-07-23 10:58:06 +05:30
Shariq Ansari
f9bae5d8ff chore: Hungarian translations 2025-07-23 10:58:04 +05:30
Shariq Ansari
29322577aa chore: German translations 2025-07-23 10:58:03 +05:30
Shariq Ansari
a4f96c7c5b chore: Czech translations 2025-07-23 10:58:01 +05:30
Shariq Ansari
b3fe85b8a0 chore: Arabic translations 2025-07-23 10:58:00 +05:30
Shariq Ansari
efc4213363 chore: Spanish translations 2025-07-23 10:57:59 +05:30
Shariq Ansari
b983648f35 chore: French translations 2025-07-23 10:57:57 +05:30
Shariq Ansari
e62eac91d1
Merge pull request #1054 from frappe/l10n_develop3 2025-07-22 10:53:04 +05:30
Shariq Ansari
e6f1bd50db chore: Esperanto translations 2025-07-22 10:52:15 +05:30
Shariq Ansari
c5458fb29c chore: Serbian (Latin) translations 2025-07-22 10:52:14 +05:30
Shariq Ansari
8e3daf482d chore: Bosnian translations 2025-07-22 10:52:13 +05:30
Shariq Ansari
03edd1a011 chore: Croatian translations 2025-07-22 10:52:11 +05:30
Shariq Ansari
246ed83a3d chore: Thai translations 2025-07-22 10:52:10 +05:30
Shariq Ansari
53440476c4 chore: Persian translations 2025-07-22 10:52:09 +05:30
Shariq Ansari
14f3fd3cd7 chore: Indonesian translations 2025-07-22 10:52:08 +05:30
Shariq Ansari
6c9d56808b chore: Portuguese, Brazilian translations 2025-07-22 10:52:06 +05:30
Shariq Ansari
08b3107d92 chore: Vietnamese translations 2025-07-22 10:52:05 +05:30
Shariq Ansari
dc8fa27ea1 chore: Chinese Simplified translations 2025-07-22 10:52:04 +05:30
Shariq Ansari
445d7050b7 chore: Turkish translations 2025-07-22 10:52:02 +05:30
Shariq Ansari
bfe0df4df3 chore: Swedish translations 2025-07-22 10:52:01 +05:30
Shariq Ansari
71bcca71cd chore: Serbian (Cyrillic) translations 2025-07-22 10:52:00 +05:30
Shariq Ansari
53864ac12c chore: Russian translations 2025-07-22 10:51:59 +05:30
Shariq Ansari
f438500a57 chore: Portuguese translations 2025-07-22 10:51:57 +05:30
Shariq Ansari
cef8aacf2f chore: Polish translations 2025-07-22 10:51:56 +05:30
Shariq Ansari
1ab414c4bf chore: Dutch translations 2025-07-22 10:51:54 +05:30
Shariq Ansari
fdffd7bf3a chore: Italian translations 2025-07-22 10:51:53 +05:30
Shariq Ansari
6fc52aba6d chore: Hungarian translations 2025-07-22 10:51:52 +05:30
Shariq Ansari
dfd4cc068a chore: German translations 2025-07-22 10:51:51 +05:30
Shariq Ansari
5ae21c8580 chore: Czech translations 2025-07-22 10:51:49 +05:30
Shariq Ansari
178a934fa3 chore: Arabic translations 2025-07-22 10:51:48 +05:30
Shariq Ansari
1ce508a4b1 chore: Spanish translations 2025-07-22 10:51:47 +05:30
Shariq Ansari
d0678ec7c2 chore: French translations 2025-07-22 10:51:46 +05:30
Shariq Ansari
8036ffeafc
Merge pull request #1053 from shariquerik/updated-crowdin-yml 2025-07-22 10:39:58 +05:30
Shariq Ansari
a5a37c6181 fix: updated crowdin.yml 2025-07-22 10:38:28 +05:30
Shariq Ansari
945cdc0ae3
Merge pull request #1050 from frappe/pot_develop_2025-07-20 2025-07-22 10:22:17 +05:30
frappe-pr-bot
8de2e89b68 chore: update POT file 2025-07-20 09:37:27 +00:00
Shariq Ansari
0f49470bf6
Merge pull request #1041 from shariquerik/dashboard-fix-1 2025-07-15 15:11:01 +05:30
Shariq Ansari
7826565ce7 fix: reset to default 2025-07-15 15:09:48 +05:30
Shariq Ansari
713571469b fix: only show edit button to system manager 2025-07-15 14:55:10 +05:30
Shariq Ansari
2f34fdd409 fix: set status correctly 2025-07-15 14:52:19 +05:30
Shariq Ansari
6fefa16ac3
Merge pull request #1033 from shariquerik/editable-dashboard 2025-07-15 13:28:50 +05:30
Shariq Ansari
b344f412c9 fix: forecasting chart is breaking if no data 2025-07-15 13:21:46 +05:30
Shariq Ansari
1e245e7719
Merge pull request #1038 from shariquerik/to-status-type-1 2025-07-15 12:39:07 +05:30
Shariq Ansari
28facd66c4 fix: to status type is not accessible 2025-07-15 12:37:44 +05:30
Shariq Ansari
c8f01f08ed fix: updated default manager dashboard 2025-07-15 12:33:54 +05:30
Shariq Ansari
a89525f77e
Merge pull request #1036 from shariquerik/to-status-type 2025-07-15 12:18:21 +05:30
Shariq Ansari
38a2fa87c3 fix: to status type is not accessible 2025-07-15 12:17:00 +05:30
Shariq Ansari
46e6ed2e6f fix: minor fix 2025-07-15 12:11:08 +05:30
Shariq Ansari
266952c404 fix: decreased rowHeight to 42 2025-07-15 12:07:19 +05:30
Shariq Ansari
b77e59589a build(deps): bump frappeui to 0.1.171 2025-07-15 12:06:41 +05:30
Shariq Ansari
1a5ae397dc
Merge pull request #1034 from shariquerik/exchange_rate_org 2025-07-15 11:51:21 +05:30
Shariq Ansari
7c4718ad02 fix: removed currency exchange and added exchange rate field 2025-07-15 11:41:02 +05:30
Shariq Ansari
d79341b6d9 fix: renamed blank card to spacer 2025-07-14 20:29:46 +05:30
Shariq Ansari
84738ba00c fix: allow force reset manager dashboard 2025-07-14 20:22:43 +05:30
Shariq Ansari
3b0a8d8e4b fix: updated default manager dashboard 2025-07-14 20:16:06 +05:30
Shariq Ansari
2584cca128 fix: minor fixes 2025-07-14 20:12:52 +05:30
Shariq Ansari
05b8cea206 fix: create default manager dashboard if not exists 2025-07-14 19:14:18 +05:30
Shariq Ansari
6e3d23a8e1 fix: add default Manager Dashboard on install 2025-07-14 18:28:51 +05:30
Shariq Ansari
2a38d0fb5f fix: disabel save button if not dirty, reset to old items if cancel 2025-07-14 18:15:04 +05:30
Shariq Ansari
97724c776b build(deps): bump frappeui to 0.1.170 2025-07-14 17:58:01 +05:30
Shariq Ansari
9b072058cc build(deps): bump frappeui to 0.1.170 2025-07-14 17:57:43 +05:30
Shariq Ansari
d2e65feaa6 fix: save edited dashboard 2025-07-14 17:41:00 +05:30
Shariq Ansari
37c2d3a2b0 feat: allow adding existing charts 2025-07-14 17:40:13 +05:30
Shariq Ansari
0909423fe9 fix: rename card to chart 2025-07-14 17:38:17 +05:30
Shariq Ansari
139bcb101c fix: added remove chart button 2025-07-14 17:37:36 +05:30
Shariq Ansari
0f06715d0c fix: use tooltip from data 2025-07-14 17:36:50 +05:30
Shariq Ansari
4a783fcba8 fix: show edit button to manager 2025-07-14 14:22:53 +05:30
Shariq Ansari
fd38f0ac98 fix: load dashboard layout from CRM Dashboard doctype and render 2025-07-14 14:16:10 +05:30
Shariq Ansari
ed2208fe75 fix: added crm dashboard doctype to store dashboard layout 2025-07-14 14:13:50 +05:30
Shariq Ansari
4320142132 fix: update status type patch was not working 2025-07-14 12:27:14 +05:30
Shariq Ansari
160649bf97 fix: use GridLayout from frappe-ui to display dashboard 2025-07-14 12:18:14 +05:30
Shariq Ansari
e7a2efd14a
Merge pull request #1029 from frappe/pot_develop_2025-07-13 2025-07-13 15:21:23 +05:30
Shariq Ansari
81614418d4
Merge pull request #1030 from shariquerik/deal-status-type 2025-07-13 15:15:19 +05:30
frappe-pr-bot
bb08f3d377 chore: update POT file 2025-07-13 09:38:02 +00:00
Shariq Ansari
5232da6ec3 fix: updated funnel query to get status change count 2025-07-13 13:07:31 +05:30
Shariq Ansari
e59547da30 fix: store deal status type in status log 2025-07-13 12:43:03 +05:30
Shariq Ansari
de85ccfc51 fix: added deals by ongoing & won stages bar 2025-07-13 12:35:16 +05:30
Shariq Ansari
f82019e510 fix: used closed_date instead of closed_on and set closed_date if status type is Won 2025-07-13 12:11:50 +05:30
Shariq Ansari
7fc26a5202 fix: used expected closed date & deal value for forecasting 2025-07-13 12:09:35 +05:30
Shariq Ansari
dcb1e47564 fix: added closed_date removed closed_on & added expected_deal_value & expected_closure_date field 2025-07-13 12:02:34 +05:30
Shariq Ansari
61259f3d2e fix: avg time to close a deal number card 2025-07-13 11:52:04 +05:30
Shariq Ansari
2dd2608c09 fix: added two more number cards 2025-07-13 11:35:14 +05:30
Shariq Ansari
81dc4e1138 fix: get ongoing deals and won deals based on closed_on date 2025-07-11 17:09:21 +05:30
Shariq Ansari
cb1f9f760c fix: use status.type instead of status in all query 2025-07-11 17:00:59 +05:30
Shariq Ansari
51530b7608 fix: show lost reason modal if status of type Lost is set 2025-07-11 16:35:47 +05:30
Shariq Ansari
4e6d4a1d77 patch: added patch to update deal status type 2025-07-11 16:16:40 +05:30
Shariq Ansari
efc5dd93e9 fix: added type in default deak status while installing 2025-07-11 16:07:52 +05:30
Shariq Ansari
210a9d8d06 fix: added type of deal status field 2025-07-11 16:05:57 +05:30
Shariq Ansari
9bd855ee2e
Merge pull request #1024 from shariquerik/save-lead 2025-07-10 18:33:18 +05:30
Shariq Ansari
970c215f40 fix: cannot save data fields in lead page 2025-07-10 18:27:01 +05:30
Shariq Ansari
7d157046ac
Merge pull request #1022 from shariquerik/dashboard-fix 2025-07-10 17:39:14 +05:30
Shariq Ansari
1ae7018f79 fix: apply user filter if sales user 2025-07-10 17:38:36 +05:30
Shariq Ansari
6802567291
Merge pull request #979 from nextchamp-saqib/dashboard 2025-07-10 17:21:58 +05:30
Shariq Ansari
cbc127e947 fix: better description 2025-07-10 17:20:25 +05:30
Shariq Ansari
d91d4765b5 fix: make autocomplete non clickable if disabled 2025-07-10 17:06:58 +05:30
Shariq Ansari
328959cc39 style: better spacing 2025-07-10 16:59:09 +05:30
Shariq Ansari
36320f61ab fix: remove mandatory from currency field in crm settings 2025-07-10 16:36:02 +05:30
Shariq Ansari
faeacb9a7d chore: minor fix 2025-07-10 16:27:00 +05:30
Shariq Ansari
5dcd416007 fix: added forecasting & currency setting in general settings 2025-07-10 16:15:27 +05:30
Shariq Ansari
33e4072430 fix: added disabled & placement prop in Link component 2025-07-10 16:14:32 +05:30
Shariq Ansari
c7fbd6f8f1 refactor: general settings 2025-07-10 15:27:55 +05:30
Shariq Ansari
a5d3694386 refactor: get exchange rate api 2025-07-10 15:23:59 +05:30
Shariq Ansari
b3075416e2 fix: show dashboard to manager only 2025-07-10 13:59:30 +05:30
Shariq Ansari
743d97d690
Merge pull request #1018 from zaqouttahir/fix-bug-edit-bulk 2025-07-10 12:29:07 +05:30
Shariq Ansari
2cb09dde4b fix: use exchange rate in deal to calculate the deal value 2025-07-09 18:22:52 +05:30
Shariq Ansari
d7ba5a5f62 fix: store current day exchange rate when currency is updated in deal & organization 2025-07-09 18:22:13 +05:30
Shariq Ansari
a00bba35f8 fix: remove crm currency exchange doctype 2025-07-09 18:20:49 +05:30
Shariq Ansari
71db65d21c fix: make currency read only once set 2025-07-09 17:49:44 +05:30
Shariq Ansari
37d820a67c chore: updated/added number card tooltip 2025-07-09 17:47:56 +05:30
Shariq Ansari
4f02f0a4d7 fix: convert to system currency and show deal value 2025-07-09 15:53:06 +05:30
Shariq Ansari
f4b81b3761 fix: added date field in currency exchange doctype 2025-07-09 14:48:49 +05:30
Shariq Ansari
0be737914a fix: store currency exchange in deal & organization 2025-07-09 14:38:56 +05:30
Shariq Ansari
1b0d966db0 fix: created currency exchange doctype 2025-07-09 14:37:16 +05:30
Shariq Ansari
27f87883f7 fix: fieldtype of value is not changing based on selected field's fieldtype 2025-07-08 19:16:58 +05:30
Shariq Ansari
f747e076ab fix: only show sales user filter to manager 2025-07-08 12:58:46 +05:30
Shariq Ansari
4b12918ba5 fix: added filters and translated titles 2025-07-08 12:29:20 +05:30
zaqouttahir
c104b1b8b4 fix: set fieldname to handle edit value modal 2025-07-07 13:13:04 +03:00
Shariq Ansari
9d4106cd81 chore: cleanup 2025-07-07 14:33:11 +05:30
Shariq Ansari
eddf8c9295 fix: show avg time number card based on closed_on date 2025-07-07 14:30:29 +05:30
Shariq Ansari
6b3e42a44e
Merge pull request #1015 from frappe/pot_develop_2025-07-06 2025-07-07 14:16:21 +05:30
Shariq Ansari
9b1d4832b6
Merge branch 'develop' into dashboard 2025-07-06 15:25:40 +05:30
Shariq Ansari
4d2f054e40 fix: added avg time to close number card 2025-07-06 15:24:43 +05:30
Shariq Ansari
6450b69ae7 fix: capture closed on datetime when deal marked as Won 2025-07-06 15:24:23 +05:30
frappe-pr-bot
223187c7ea chore: update POT file 2025-07-06 09:36:54 +00:00
Shariq Ansari
3b34f73cb3 fix: added more charts for dashboard 2025-07-06 14:44:12 +05:30
Shariq Ansari
40c5c92230
Merge pull request #1013 from shariquerik/primary-mobile-no 2025-07-05 14:33:10 +05:30
Shariq Ansari
6760798f18 fix: useDocument in organization page 2025-07-05 14:17:26 +05:30
Shariq Ansari
42ea1ad16e fix: useDocument in contact page 2025-07-05 14:09:37 +05:30
Shariq Ansari
96200aebe6 fix: update primary mobile_no & email in deal if contact is updated 2025-07-05 13:21:21 +05:30
Shariq Ansari
bcfe4b6a49 fix: made mobile_no, email & phone readonly since it captures primary contacts data 2025-07-05 12:30:40 +05:30
Shariq Ansari
cb92e5e68d
Merge branch 'develop' into dashboard 2025-07-03 14:28:28 +05:30
Shariq Ansari
1fa6b5bb51 fix: added first cut queries for some charts and number cards 2025-07-03 14:27:21 +05:30
Shariq Ansari
cafc4fb22f
Merge pull request #1009 from shariquerik/products-table-fix 2025-07-02 17:50:58 +05:30
Shariq Ansari
39eb5600d9 fix: grid field is not getting set 2025-07-02 17:45:46 +05:30
Shariq Ansari
0b97462dc9
Merge pull request #1007 from shariquerik/lost-reason-fix 2025-07-02 17:26:54 +05:30
Shariq Ansari
cab80edf60 fix: check reason.reason not reason 2025-07-02 17:15:59 +05:30
Shariq Ansari
6f3b58d1a5
Merge pull request #1005 from shariquerik/frappeui-update 2025-07-02 17:09:33 +05:30
Shariq Ansari
fc89c7b93c build(deps): bump frappeui to 0.1.166 2025-07-02 17:03:28 +05:30
Shariq Ansari
4a57c4eb84
Merge pull request #1003 from shariquerik/default-lost-reason 2025-07-02 16:59:23 +05:30
Shariq Ansari
96cbdea820 fix: add default lost reason on install 2025-07-02 16:57:26 +05:30
Shariq Ansari
a3a54aef94
Merge pull request #984 from shariquerik/lost-reasons 2025-07-02 16:03:30 +05:30
Shariq Ansari
c96e5ff6c5 fix: update default probability from deal status 2025-07-02 15:58:12 +05:30
Shariq Ansari
144470877d fix: allow creating lost reason from lost reason modal field 2025-07-02 15:42:25 +05:30
Shariq Ansari
391844512a fix: intercept data tab's before save and side panel's before field change to show lost reason modal 2025-07-02 15:28:21 +05:30
Shariq Ansari
d89c304b13 fix: show lost reason modal if status changed to lost 2025-07-02 14:11:11 +05:30
Shariq Ansari
881126c7f1 refactor: statusOptions code 2025-07-02 14:10:14 +05:30
Shariq Ansari
5bbec00803 fix: renamed other_lost_reason to lost_notes 2025-07-02 13:59:34 +05:30
Shariq Ansari
7730e46cfc fix: removed unused triggerOnChange 2025-07-02 13:58:51 +05:30
Shariq Ansari
97b2253e9d fix: made lost notes as text and non mandatory if lost reason is not Other 2025-07-02 11:10:53 +05:30
Shariq Ansari
1afb2a783b
Merge branch 'develop' into lost-reasons 2025-07-01 19:34:35 +05:30
Shariq Ansari
0fdbfa3ad4
Merge pull request #998 from shariquerik/prettydate-fix 2025-07-01 19:23:24 +05:30
Shariq Ansari
a7dc5e05b3 fix: show absolute day count not in decimels 2025-07-01 19:20:04 +05:30
Shariq Ansari
92d7280728 chore: resolved conflict 2025-07-01 16:53:40 +05:30
Shariq Ansari
5d01b88a1e feat: created lost reason doctype 2025-07-01 16:51:01 +05:30
Shariq Ansari
2b47e3f4c9
Merge pull request #994 from shariquerik/forecasting-fix-1 2025-07-01 16:49:48 +05:30
Shariq Ansari
485360f291 fix: show forcasted sales section in sidepanel if forecasting is enabled 2025-07-01 16:37:04 +05:30
Shariq Ansari
17fdbb05ce fix: add mandatory fields in convert to deal modal if not added 2025-07-01 16:05:08 +05:30
Shariq Ansari
adc22efcb1 fix: show error message on convert to deal modal 2025-07-01 16:04:40 +05:30
Shariq Ansari
4c70b1a06b fix: mandatory error 2025-07-01 15:06:02 +05:30
Shariq Ansari
4f58aa110a fix: made deal value mandatory if forecasting is enabled 2025-07-01 15:05:37 +05:30
Shariq Ansari
4d3fe722e8 fix: added default probability to Lost status 2025-07-01 13:16:40 +05:30
Shariq Ansari
6320e580ae refactor: moved convert to deal modal into separate component 2025-07-01 13:07:36 +05:30
Shariq Ansari
611f4cde70 fix: prettyDate is not accurate 2025-07-01 12:58:57 +05:30
Shariq Ansari
6d3268a61e fix: add default probabilities in deal status 2025-07-01 12:07:44 +05:30
Shariq Ansari
bf0a1ecebd
Merge pull request #991 from shariquerik/edit-call-log 2025-07-01 11:52:18 +05:30
Shariq Ansari
693c086930 fix: show edit call log button in call log details modal 2025-07-01 11:49:42 +05:30
Shariq Ansari
7c307a9134
Merge pull request #988 from shariquerik/call-log-on-before-create 2025-06-30 20:06:01 +05:30
Shariq Ansari
aae7e0e36c fix: pass reference doc to call log modal to get reference doc in on before create 2025-06-30 20:00:05 +05:30
Shariq Ansari
2014a3d6de
Merge pull request #985 from shariquerik/on-before-create 2025-06-30 19:41:35 +05:30
Shariq Ansari
2e5c1bc3b5 fix: added on before create hook in all modals 2025-06-30 19:16:24 +05:30
Shariq Ansari
ac13b7a3bd fix: added on before create hook in call log modal 2025-06-30 18:43:19 +05:30
Shariq Ansari
6b7bdf5afb feat: added on before create hook in document.js 2025-06-30 18:42:20 +05:30
Shariq Ansari
3eba628a8b
Merge branch 'develop' into dashboard 2025-06-30 12:57:13 +05:30
Shariq Ansari
9949478b36 fix: added breadcrumb and made header sticky 2025-06-30 12:56:47 +05:30
Shariq Ansari
ff657ec34c
Merge pull request #980 from frappe/pot_develop_2025-06-29 2025-06-30 12:42:55 +05:30
Shariq Ansari
da4698d431
Merge branch 'develop' into pot_develop_2025-06-29 2025-06-30 12:42:12 +05:30
Shariq Ansari
20d47ae323
Merge pull request #978 from shariquerik/dynamic-app-alias 2025-06-30 11:59:29 +05:30
Shariq Ansari
f4f799f636 feat: create dynamic alias to use components from frontend vue apps 2025-06-30 11:49:31 +05:30
frappe-pr-bot
cc411f036d chore: update POT file 2025-06-29 09:36:46 +00:00
Saqib Ansari
62d5c2a91f feat: initialize dashboard boilerplate 2025-06-28 10:57:58 +05:30
Shariq Ansari
8350c5ee36
Merge pull request #971 from shariquerik/email-template-settings 2025-06-26 17:54:04 +05:30
Shariq Ansari
65435cf2b5 fix: delete icon issue & more cleanup 2025-06-26 17:49:10 +05:30
Shariq Ansari
af4c64e633 build(deps): bump frappeui to 0.1.162 2025-06-26 17:11:37 +05:30
Shariq Ansari
41b913debe fix: cannot change role of user with Admin access 2025-06-26 17:07:26 +05:30
Shariq Ansari
a3b9368953 fix: give Sales Manager & Sales User role if System Manager access is given 2025-06-26 16:54:33 +05:30
Shariq Ansari
28ece820ed style: better spacing 2025-06-26 16:46:23 +05:30
Shariq Ansari
cca420b1a0 style: minor changes 2025-06-25 21:19:49 +05:30
Shariq Ansari
05803c79b4 fix: make email template row clickable 2025-06-25 17:02:41 +05:30
Shariq Ansari
5932ccafec fix: add new email template from email selector modal 2025-06-25 15:23:30 +05:30
Shariq Ansari
7cee017e20 fix: removed email template page and related components 2025-06-25 15:15:34 +05:30
Shariq Ansari
b15a8d9c8a fix: added email template icon 2025-06-25 15:03:32 +05:30
Shariq Ansari
7e6d5c3e54 fix: Duplicate email template 2025-06-24 19:46:27 +05:30
Shariq Ansari
dd3d297dab fix: Edit email template 2025-06-24 19:37:13 +05:30
Shariq Ansari
e4f728d809 fix: Create email template 2025-06-24 18:52:36 +05:30
Shariq Ansari
cd7bab9184 feat: Create/Edit & List page for email template & implemented delete from list 2025-06-24 18:52:00 +05:30
Shariq Ansari
ec6b1558b1 fix: only show search if users are more than 10 2025-06-24 15:36:54 +05:30
Shariq Ansari
1c3ee8b557 refactor: added email templates in settings modal 2025-06-24 15:35:48 +05:30
Shariq Ansari
1db7f69f89
Merge pull request #960 from shariquerik/users-fix-1 2025-06-24 12:12:20 +05:30
Shariq Ansari
3c1ce1fe27 fix: make header sticky 2025-06-24 12:11:39 +05:30
Shariq Ansari
2d05b6a282
Merge pull request #957 from shariquerik/users-fix 2025-06-24 11:45:46 +05:30
Shariq Ansari
b5ed9692df fix: user with System Manager role is admin 2025-06-24 11:44:13 +05:30
Shariq Ansari
9a326d791b
Merge pull request #953 from shariquerik/users 2025-06-23 21:00:32 +05:30
Shariq Ansari
7fbd240d97 fix: added search in users page 2025-06-23 20:55:35 +05:30
Shariq Ansari
58d4691354
Merge pull request #846 from pratikb64/delete-from-record-view 2025-06-23 13:50:19 +05:30
Pratik
594295b7c8 style: switch button position 2025-06-23 08:13:06 +00:00
Shariq Ansari
2c45673f54
Merge pull request #845 from shariquerik/agents 2025-06-23 13:22:56 +05:30
Shariq Ansari
7827afe606
Merge pull request #946 from kalungia/dev 2025-06-23 13:21:59 +05:30
Shariq Ansari
bc7498e02b
Merge branch 'develop' into dev 2025-06-23 13:20:01 +05:30
Shariq Ansari
e5dd85aefb
Merge pull request #948 from frappe/pot_develop_2025-06-22 2025-06-23 13:19:09 +05:30
Shariq Ansari
cf1fce3dc0 fix: renaming fix and removed CRM User code 2025-06-23 13:18:16 +05:30
Pratik
480cc07cd9 refactor: remove unnecessary functions & components 2025-06-23 07:35:15 +00:00
Shariq Ansari
84d4327e80 fix: use change password modal in place 2025-06-23 13:04:12 +05:30
Pratik
34102ef6ef refactor: change labels & function names 2025-06-23 05:15:40 +00:00
Pratik
ca985a0b76 Merge branch 'develop' of https://github.com/pratikb64/crm into delete-from-record-view 2025-06-23 04:26:50 +00:00
frappe-pr-bot
4b4a154261 chore: update POT file 2025-06-22 09:36:52 +00:00
Shariq Ansari
e957327877 fix: use crmUsers in all link field 2025-06-20 18:57:09 +05:30
Shariq Ansari
2fdea90ad4 fix: loading state in Users page 2025-06-20 18:55:42 +05:30
Shariq Ansari
ad1aee9c9e fix: add user is actually add role 2025-06-20 18:55:14 +05:30
Shariq Ansari
bd7451e86f fix: if role is set to sales user then remove modules and set FCRM 2025-06-20 17:56:14 +05:30
Shariq Ansari
0230360145 fix: use text-ink-gray-8 instead of 9 2025-06-20 17:47:07 +05:30
Shariq Ansari
eee1190f10 fix: dark mode fixes for email account setting 2025-06-20 17:42:10 +05:30
Shariq Ansari
96c8aae01e chore: fixed warning 2025-06-20 17:25:14 +05:30
Shariq Ansari
0ad65be961 fix: layout change 2025-06-20 17:20:12 +05:30
Shariq Ansari
0f8d484e28 fix: existingEmail is a list 2025-06-20 17:18:36 +05:30
Shariq Ansari
364c369199 fix: updated users page to update user directly and removed unnecessary code 2025-06-20 16:46:41 +05:30
Shariq Ansari
901bcb8460 fix: removed CRM User doctype and moved api's to user.py 2025-06-20 16:45:48 +05:30
Shariq Ansari
d06ac91052 fix: use multi select user input and show already exist error if user with email exist or invited 2025-06-20 16:43:48 +05:30
Shariq Ansari
f818a4c1d6 fix: created multi select user input 2025-06-20 16:42:45 +05:30
Shariq Ansari
85191e10c8 fix: use crmUsers in comment box 2025-06-20 16:41:08 +05:30
Shariq Ansari
001a3231e1 fix: get users and crm users 2025-06-20 15:50:47 +05:30
Shariq Ansari
d951dff5a9 fix: added tooltip with shortcut 2025-06-19 15:08:53 +05:30
Shariq Ansari
dc82f837aa fix: moved change password modal in global modals 2025-06-19 15:07:57 +05:30
Shariq Ansari
7f1db0b444 fix: change password validation messsage 2025-06-19 12:56:50 +05:30
Abraham Kalungi
a317950567 fix: prevent TypeError when concatenating first and last name in WhatsApp messages
The last name on CRM Leads can be empty. In cases where it is, an error occurs: can only concatenate str (not "NoneType") to str. This prevents retrieving messages until a last name is added.
2025-06-18 15:31:00 +02:00
Shariq Ansari
4c7269e357 fix: updated components.d.ts 2025-06-17 23:46:53 +05:30
Shariq Ansari
15fd763de8 fix: use validateIsImageFile from utils 2025-06-17 23:46:28 +05:30
Shariq Ansari
0c314674fc
Merge branch 'develop' into agents 2025-06-17 23:42:42 +05:30
Shariq Ansari
efd03141f0
chore: resolved conflict 2025-06-17 23:42:20 +05:30
Shariq Ansari
675bcb549d
Discard changes to frontend/src/components/Settings/ProfileImageEditor.vue 2025-06-17 23:40:58 +05:30
Shariq Ansari
56425254a9 fix: updated components.d.ts 2025-06-17 23:35:51 +05:30
Shariq Ansari
22856351fd fix: only show users to manager 2025-06-17 23:35:51 +05:30
Shariq Ansari
dd1229309f fix: updated components.d.ts 2025-06-17 23:35:51 +05:30
Shariq Ansari
fad7c5985c refactor: profile page 2025-06-17 23:34:45 +05:30
Shariq Ansari
3234102e55 fix: capture onboarding step event of setting up password 2025-06-17 23:34:45 +05:30
Shariq Ansari
fb2f105520 feat: update password modal 2025-06-17 23:34:45 +05:30
Shariq Ansari
03abe0b5cd fix: create crm user on accepting invite 2025-06-17 23:34:44 +05:30
Shariq Ansari
6873c6db4e feat: add existing users 2025-06-17 23:34:44 +05:30
Shariq Ansari
08bab927a2 fix: filter out existing emails 2025-06-17 23:34:44 +05:30
Shariq Ansari
d244567b30 fix: open invite user page from users add new agent dropdown option 2025-06-17 23:34:44 +05:30
Shariq Ansari
2b1b21d2e2 fix: show role options based on logged in user's role 2025-06-17 23:34:44 +05:30
Shariq Ansari
12213de478 fix: show role options based on logged in user's role 2025-06-17 23:34:44 +05:30
Shariq Ansari
2a2c832e0b fix: updated roles in Invite user page 2025-06-17 23:34:44 +05:30
Shariq Ansari
b534aae70b fix: renamed component names from Agent to User 2025-06-17 23:34:44 +05:30
Shariq Ansari
6d3e4406ae fix: renamed & added role with filter 2025-06-17 23:34:44 +05:30
Shariq Ansari
463d60b650 fix: renamed Agent to User in Settings 2025-06-17 23:34:44 +05:30
Shariq Ansari
e9812495e9 fix: update agent fields based on user 2025-06-17 23:34:44 +05:30
Shariq Ansari
0a836c78bb fix: activate/deactivate agent 2025-06-17 23:34:44 +05:30
Shariq Ansari
5c7f835e4c fix: renamed invite members to invite agent 2025-06-17 23:34:44 +05:30
Shariq Ansari
346849631e fix: show and allow changing role from agents settings page 2025-06-17 23:34:44 +05:30
Shariq Ansari
bf166bdaad fix: added get user role in users store 2025-06-17 23:34:44 +05:30
Shariq Ansari
123f183f68 fix: removed x button from settings modal 2025-06-17 23:34:44 +05:30
Shariq Ansari
bea1505c63 fix: added Agents page in settings 2025-06-17 23:34:44 +05:30
Shariq Ansari
fac5ed5579 feat: added crm agent doctype 2025-06-17 23:34:44 +05:30
Shariq Ansari
96fefbd8a3
Merge pull request #937 from shariquerik/forecasting-fix 2025-06-16 17:48:08 +05:30
Shariq Ansari
548018997e build(deps): bump frappeui to 0.1.156 2025-06-16 17:42:28 +05:30
Shariq Ansari
9f3477e1cd fix: updated button with icon 2025-06-16 17:42:01 +05:30
Shariq Ansari
baa03246e6 fix: fixed breaking button with icon and open email box 2025-06-16 17:06:06 +05:30
Shariq Ansari
a17b1cd0e2 fix: added mandatory field error toast 2025-06-16 16:32:15 +05:30
Shariq Ansari
49d82870c4 fix: hide calendar icon in side panel date fields 2025-06-16 13:41:11 +05:30
Shariq Ansari
6a72a4467a build(deps): bump frappeui to 0.1.154 2025-06-16 13:40:53 +05:30
Shariq Ansari
efbed6e0b6 fix: renamed Expected Closure Date to Close Date 2025-06-16 11:44:08 +05:30
Shariq Ansari
5270670b65
Merge pull request #933 from frappe/pot_develop_2025-06-15 2025-06-16 11:39:47 +05:30
frappe-pr-bot
b82f4ca02b chore: update POT file 2025-06-15 09:36:57 +00:00
Pratik Badhe
0f451c7e3a
Merge pull request #930 from pratikb64/git-error 2025-06-13 18:26:31 +05:30
Pratik
824dc8dcdd fix: git command error 2025-06-13 12:51:04 +00:00
Shariq Ansari
20405be86c
Merge pull request #920 from shariquerik/forecasting 2025-06-13 15:04:23 +05:30
Shariq Ansari
4edfa951dc fix: asterisk is not visible if label is big enough 2025-06-13 15:02:50 +05:30
Shariq Ansari
029c16d1d0 fix: circular import 2025-06-13 14:46:44 +05:30
Shariq Ansari
b3acff8cba fix: create standard forecasting script on install and added in patch 2025-06-13 14:42:09 +05:30
Shariq Ansari
a98b0e3a00 fix: set close date to now if status is Won 2025-06-13 14:20:50 +05:30
Shariq Ansari
b1cbcbd98d
Merge pull request #923 from shariquerik/lead-org-avatar-fix 2025-06-12 18:09:41 +05:30
Shariq Ansari
e079980598 fix: removed leads organization logo 2025-06-12 18:07:07 +05:30
Shariq Ansari
2e27c0459c fix: set close date reqd as 1 or 0 based on enabled_forecasting 2025-06-11 19:30:08 +05:30
Shariq Ansari
d87a237789 feat: added enable forecasting settings 2025-06-11 19:03:22 +05:30
Shariq Ansari
e9e0aa357b fix: trigger on change on status change 2025-06-11 14:20:11 +05:30
Shariq Ansari
9af300bba8 fix: added throwError global method 2025-06-11 14:19:16 +05:30
Shariq Ansari
7d79cbf5bd fix: update and reset value in triggerOnChange method 2025-06-11 14:18:25 +05:30
Shariq Ansari
fdca27bb81 fix: added probability field in deal status 2025-06-11 13:01:22 +05:30
Shariq Ansari
01f0213693 fix: added deal value field 2025-06-11 13:00:02 +05:30
Shariq Ansari
5d29a49120
Merge pull request #916 from mahsem/develop 2025-06-11 11:13:25 +05:30
mahsem
33e6b80d5a
fix: add context for Integrations 2025-06-09 22:11:22 +02:00
Shariq Ansari
100d931535
Merge pull request #913 from shariquerik/future-date-time-ago 2025-06-09 16:51:02 +05:30
Shariq Ansari
dba6dd1983 fix: future date is not captured in pretty date 2025-06-09 16:49:09 +05:30
Shariq Ansari
dc8898e1da
Merge pull request #907 from frappe/pot_develop_2025-06-08 2025-06-09 14:09:56 +05:30
frappe-pr-bot
ed79bf55eb chore: update POT file 2025-06-08 09:36:10 +00:00
Shariq Ansari
fb644f5fbe
Merge pull request #905 from NagariaHussain/fix/image-upload
refactor: DRY up validate image file
2025-06-08 12:49:03 +05:30
Hussain Nagaria
ab409dfd2c fix: yet another unused import due to merge conflict 2025-06-08 12:37:15 +05:30
Hussain Nagaria
42285dd911 fix: unused import due to merge conflict 2025-06-08 12:36:39 +05:30
Md Hussain Nagaria
db2a6c65b7
Merge branch 'develop' into fix/image-upload 2025-06-08 12:34:30 +05:30
Hussain Nagaria
c6ad10857a refactor: DRY up validate image file
* Also, allows more types of image files
2025-06-08 12:30:51 +05:30
Shariq Ansari
f128a55f97
Merge pull request #902 from shariquerik/onload-onsave 2025-06-06 21:15:42 +05:30
Shariq Ansari
2a817e5861 fix: only load assignee if docname is passed 2025-06-06 21:14:20 +05:30
Shariq Ansari
5e616f1a50 refactor: statusOptions code 2025-06-06 21:09:27 +05:30
Shariq Ansari
c6e9d71e1f fix: allow snake & camel case for on load, on save, convert to deal, on create lead 2025-06-06 21:01:20 +05:30
Shariq Ansari
f58d44bf9c fix: use document.doc in status dropdown 2025-06-06 20:49:58 +05:30
Shariq Ansari
f72ab39c93 fix: onLoad & onSave 2025-06-06 20:35:25 +05:30
Shariq Ansari
6c706e6162
Merge pull request #899 from shariquerik/minor-fix 2025-06-06 17:51:43 +05:30
Shariq Ansari
8f81d207b8 fix: activity is not loading 2025-06-06 17:50:48 +05:30
Shariq Ansari
b74c5f384d
Merge pull request #896 from shariquerik/custom-actions 2025-06-06 17:24:13 +05:30
Shariq Ansari
df412d51fe fix: add custom actions using class based script in mobile view 2025-06-06 17:19:27 +05:30
Shariq Ansari
8942bb7e48 feat: add custom statuses using class based script 2025-06-06 17:17:11 +05:30
Shariq Ansari
ca60679126 feat: add custom actions using class based script 2025-06-06 16:38:39 +05:30
Shariq Ansari
8db846ad5d fix: trigger onload method if controller is loaded 2025-06-06 16:37:55 +05:30
Shariq Ansari
bb6a90058b
Merge pull request #894 from shariquerik/refactor-assignees 2025-06-06 14:58:47 +05:30
Shariq Ansari
44df09fac2 fix: removed setuAssignees code 2025-06-06 14:06:02 +05:30
Shariq Ansari
e214ce8bfb refactor: render assignees from document.js
reload assignees if lead_owner/deal_owner is changed
2025-06-05 18:55:28 +05:30
Shariq Ansari
6d281922e4 fix: load assignees in document.js 2025-06-05 18:53:57 +05:30
Shariq Ansari
58f09331b0 fix: remove multiple assignees not working 2025-06-05 18:52:39 +05:30
Shariq Ansari
9780a6b63e
Merge pull request #891 from shariquerik/fixes-2 2025-06-05 16:14:38 +05:30
Shariq Ansari
71f764c224 refactor: set default values for new lead, deal and contact 2025-06-05 16:08:51 +05:30
Shariq Ansari
9362997246 refactor: organization modal code refactor 2025-06-05 16:08:13 +05:30
Shariq Ansari
28ea88f61e refactor: call log modal code refactor 2025-06-05 16:07:40 +05:30
Shariq Ansari
a25ff14dd4 fix: activity is not loading 2025-06-05 15:31:36 +05:30
Shariq Ansari
d86caee7af
Merge pull request #887 from shariquerik/new-doc-issue 2025-06-05 13:56:57 +05:30
Shariq Ansari
c4caabe722 fix: moved address modal to global modals and control it using modals.js 2025-06-04 19:15:12 +05:30
Shariq Ansari
8dcb77634b fix: moved quick entry modal related logic to modals.js & GlobalModals for all pages 2025-06-04 19:14:15 +05:30
Shariq Ansari
c4feed116d fix: handle new document for lead/deal/contact/organization 2025-06-04 19:00:57 +05:30
Shariq Ansari
e220767179 fix: global modals not working in mobile view 2025-06-04 18:44:51 +05:30
Shariq Ansari
571126c36d fix: moved modal related code to modal.js 2025-06-04 18:34:19 +05:30
Shariq Ansari
832323f25e fix: handle new document for call log 2025-06-04 12:50:19 +05:30
Shariq Ansari
3b73432d8c fix: handle controllers for new document 2025-06-04 12:49:14 +05:30
Shariq Ansari
3aa341370b fix: await scripts.list.promise 2025-06-04 12:48:20 +05:30
Ankush Menat
895da1a812
fix: remove invasive settings (#884) 2025-06-02 19:03:54 +05:30
Shariq Ansari
d34ee6fe48
Merge pull request #875 from Ocheretovich/patch-2 2025-06-02 12:37:56 +05:30
Shariq Ansari
7298fe378c
Merge pull request #878 from frappe/pot_develop_2025-06-01 2025-06-02 12:37:01 +05:30
Shariq Ansari
2da0b48c29
Merge pull request #874 from Ocheretovich/patch-1 2025-06-02 12:36:01 +05:30
frappe-pr-bot
165509f5a0 chore: update POT file 2025-06-01 09:36:30 +00:00
Ocheretovich
c9b9dbb092
Update README.md 2025-05-29 13:06:52 +03:00
Ocheretovich
0cc1d5da8f
Update README.md 2025-05-29 13:03:21 +03:00
Pratik
c70dced268 refactor: internationalization & code clean up 2025-05-29 06:02:35 +00:00
Pratik
df698387dc Merge remote-tracking branch 'origin/develop' into delete-from-record-view 2025-05-29 05:54:17 +00:00
Pratik
716dc056d6 refactor: move delete button 2025-05-29 05:52:36 +00:00
Pratik Badhe
cf91f3f72a
Merge pull request #872 from pratikb64/export-filter-fix 2025-05-28 17:18:19 +05:30
Pratik
51b87d0ac6 fix: export with filter 2025-05-28 11:09:22 +00:00
Shariq Ansari
c83d7adddd
Merge pull request #868 from shariquerik/prettydate 2025-05-28 13:58:41 +05:30
Shariq Ansari
549665bc61 fix: use prettydate method instead of useTimeAgo 2025-05-28 13:50:01 +05:30
Pratik
7f5f43f0c2 style: fix dark mode styles 2025-05-27 05:22:30 +00:00
Pratik
af41469d58 feat: add list view & handle bulk delete, unlink 2025-05-26 15:24:24 +00:00
Shariq Ansari
43e1309bd8
Merge pull request #865 from shariquerik/form-script-fix 2025-05-26 17:56:12 +05:30
Shariq Ansari
91f7cf05fc fix: handle script load while setting up script 2025-05-26 17:50:24 +05:30
Shariq Ansari
875431a620 fix: moved setupHelperMethods from setupFormController to evaluateFormClass 2025-05-26 16:57:02 +05:30
Shariq Ansari
db0c0d98bc fix: pass getDoc function instead of document.doc to keep the reactivity 2025-05-26 16:46:24 +05:30
Shariq Ansari
5406f4a11b
Merge pull request #863 from shariquerik/convert-to-deal-script 2025-05-26 15:40:01 +05:30
Shariq Ansari
bfdd3273fe feat: intercept convert to deal via form script 2025-05-26 14:30:15 +05:30
Shariq Ansari
8798103e7e
Merge pull request #859 from frappe/pot_develop_2025-05-25 2025-05-26 12:08:46 +05:30
Shariq Ansari
203b5ab1ac
Merge branch 'develop' into pot_develop_2025-05-25 2025-05-26 12:04:31 +05:30
Shariq Ansari
ed1b26207b
Merge pull request #858 from shariquerik/create-call-log-script 2025-05-26 11:58:49 +05:30
frappe-pr-bot
e0166a08e2 chore: update POT file 2025-05-25 09:35:42 +00:00
Shariq Ansari
8af4e9b5e8 feat: intercept create lead from call log via form script 2025-05-23 21:49:12 +05:30
Shariq Ansari
900c1d3570
Merge pull request #856 from shariquerik/table-multiselect-fix 2025-05-23 20:39:42 +05:30
Shariq Ansari
b95a17a4e0 fix: set default value as empty array 2025-05-23 20:26:43 +05:30
Shariq Ansari
0f0b012a44
Merge pull request #849 from shariquerik/communication-date 2025-05-22 18:53:20 +05:30
Shariq Ansari
b291f82e4d fix: show communication date instead of creation 2025-05-22 18:13:07 +05:30
Pratik Badhe
86b7222916
Merge pull request #847 from pratikb64/filter-selected-filters 2025-05-22 16:41:57 +05:30
Pratik
7a12b80dd2 fix: hide selected filters from filter list 2025-05-22 10:50:39 +00:00
Pratik
4a836a58ee feat: handle bulk delete 2025-05-22 07:15:22 +00:00
Pratik
b47fc5b93b feat: handle linked docs while deleting 2025-05-21 14:20:48 +00:00
Shariq Ansari
f3b9103a51
Merge pull request #843 from shariquerik/about 2025-05-21 18:00:12 +05:30
Shariq Ansari
dc3ccdddd4 fix: added about link in standard_dropdown_items in hook.py 2025-05-21 17:56:54 +05:30
Shariq Ansari
807eb4a7d9 fix: removed doc & telegram link from user dropdown 2025-05-21 17:51:26 +05:30
Shariq Ansari
a24283eb5e feat: added action to open about modal in user dropdown 2025-05-21 17:50:53 +05:30
Shariq Ansari
fd7116b2e1 feat: show about details in about modal 2025-05-21 17:49:09 +05:30
Shariq Ansari
2e1289df28
Merge pull request #841 from shariquerik/update-toast 2025-05-20 14:37:38 +05:30
Shariq Ansari
6064ca5a4f fix: use toast.create api instead of createToast 2025-05-20 14:35:02 +05:30
Shariq Ansari
3db1b3c0f3 chore: update frappe-ui 2025-05-20 14:21:47 +05:30
Shariq Ansari
06ffa203ef
Merge pull request #838 from shariquerik/invite-member-fix-5 2025-05-20 14:09:38 +05:30
Shariq Ansari
dd1db8f782 fix: added update your password step in onboarding 2025-05-20 14:07:26 +05:30
Shariq Ansari
fe8e309399 feat: added password control 2025-05-20 14:07:15 +05:30
Shariq Ansari
e7a20374c7 fix: set default value as 0 in int field 2025-05-20 14:05:58 +05:30
Shariq Ansari
4cfa0f512b fix: do not show contacts in dropdown in invite member page 2025-05-20 14:05:47 +05:30
Shariq Ansari
64b4f6b759 fix: only set FCRM module if user is Sales User 2025-05-20 14:05:37 +05:30
Shariq Ansari
2d421e6052 fix: allow read permission for form script 2025-05-20 14:05:14 +05:30
Shariq Ansari
cd8dd683fa
Merge pull request #837 from frappe/revert-836-invite-member-fix-3 2025-05-20 13:54:44 +05:30
Shariq Ansari
a2bdc7ab93
Revert "fix: Invite Member Page" 2025-05-20 13:53:35 +05:30
Shariq Ansari
d4132c2411
Merge pull request #836 from shariquerik/invite-member-fix-3 2025-05-20 13:47:44 +05:30
Shariq Ansari
4c6e273268 fix: added update your password step in onboarding 2025-05-20 13:26:02 +05:30
Shariq Ansari
043f174e05 fix: updated components.d.ts 2025-05-20 13:24:42 +05:30
Shariq Ansari
26e9fac1ed feat: added password control 2025-05-20 13:23:26 +05:30
Shariq Ansari
88f33db249 fix: set default value as 0 in int field 2025-05-20 13:17:52 +05:30
Shariq Ansari
55a67bbc0c fix: do not show contacts in dropdown in invite member page 2025-05-20 13:17:20 +05:30
Shariq Ansari
08f042589d fix: only set FCRM module if user is Sales User 2025-05-20 13:16:58 +05:30
Shariq Ansari
52f540a014 fix: allow read permission for form script 2025-05-20 13:15:15 +05:30
Shariq Ansari
e85ef93480
Merge pull request #835 from frappe/revert-833-invite-member-fix-2 2025-05-20 13:09:32 +05:30
Shariq Ansari
a757f80263
Revert "fix: Invite Member Page" 2025-05-20 13:04:02 +05:30
Shariq Ansari
b9b8ff0e10
Merge pull request #833 from shariquerik/invite-member-fix-2 2025-05-20 12:47:27 +05:30
Shariq Ansari
e0aad074ec fix: added update your password step in onboarding 2025-05-19 21:58:03 +05:30
Shariq Ansari
ad88b4e046 feat: added password control 2025-05-19 21:02:10 +05:30
Shariq Ansari
5156814e7a fix: set default value as 0 in int field 2025-05-19 20:41:06 +05:30
Shariq Ansari
f988d16215 fix: use toast.create api instead of createToast 2025-05-19 20:40:11 +05:30
Shariq Ansari
f5a3fccad3 fix: do not show contacts in dropdown in invite member page 2025-05-19 19:05:37 +05:30
Shariq Ansari
e3f0079578 fix: only set FCRM module if user is Sales User 2025-05-19 18:02:30 +05:30
Shariq Ansari
b831ea3c47 fix: allow read permission for form script 2025-05-19 17:58:17 +05:30
Shariq Ansari
a88545b8b9
Merge pull request #831 from shariquerik/onboarding-fixes 2025-05-19 16:51:44 +05:30
Shariq Ansari
44523a0392 fix: store firstLead & firstDeal per user 2025-05-19 16:44:42 +05:30
Shariq Ansari
dbc207a9a6
Merge branch 'develop' into onboarding-fixes 2025-05-19 16:23:35 +05:30
Shariq Ansari
e68d861ee5
Merge pull request #828 from shariquerik/esm-toast 2025-05-19 16:21:16 +05:30
Shariq Ansari
7851bbadfa
Merge branch 'develop' into esm-toast 2025-05-19 16:13:55 +05:30
Shariq Ansari
9223d00af3
Merge pull request #826 from frappe/pot_develop_2025-05-18 2025-05-19 16:12:59 +05:30
Shariq Ansari
740c21532a fix: update Vue compiler options for custom lucide elements 2025-05-19 16:11:19 +05:30
Shariq Ansari
9fdd8bbc17 fix: add @tiptap/extension-paragraph dependency version 2.12.0 2025-05-19 15:56:43 +05:30
Shariq Ansari
0978fa58a2 fix: wrap layout and dialogs in FrappeUIProvider 2025-05-19 15:55:54 +05:30
Shariq Ansari
1395a12d32 build(deps): bump frappeui to 0.1.145 2025-05-19 15:55:37 +05:30
Shariq Ansari
9aab0e7417 fix: update package.json and config files to use ES module syntax 2025-05-19 15:55:07 +05:30
Shariq Ansari
ddc5810c71 fix: change heading to paragraph in invitation email template 2025-05-19 13:40:26 +05:30
Shariq Ansari
21c349e1d7 fix: added dependsOn value on dependent step to gray out 2025-05-19 13:39:06 +05:30
Shariq Ansari
a7784c2985 fix: get filtered steps based on condition 2025-05-19 13:06:38 +05:30
frappe-pr-bot
0cc69d90f0 chore: update POT file 2025-05-18 09:35:38 +00:00
Shariq Ansari
f125737d30
Merge pull request #821 from shariquerik/call-issue 2025-05-15 11:25:13 +05:30
Shariq Ansari
18aef2376a fix: cannot make call 2025-05-15 11:24:27 +05:30
Shariq Ansari
c8287ff107
Merge pull request #818 from shariquerik/contact-not-loading-1 2025-05-15 01:18:00 +05:30
Shariq Ansari
baf344a697 fix: remove updateField event from various components 2025-05-15 01:15:25 +05:30
Shariq Ansari
8c94049e3c fix: contact/organization page not loading 2025-05-15 01:01:48 +05:30
Shariq Ansari
646c76c3cb
Merge pull request #814 from shariquerik/product-details 2025-05-14 23:42:46 +05:30
Shariq Ansari
adbb9f5765 fix: pass doctype argument to get_product_details_script in create_product_details_script 2025-05-14 20:13:34 +05:30
Shariq Ansari
d3a6cc968f fix: add patch to create default scripts 2025-05-14 20:00:48 +05:30
Shariq Ansari
d6ff40cc6a chore: formatting fix 2025-05-14 19:58:39 +05:30
Shariq Ansari
fdd6c46b5f fix: create product detail script on install 2025-05-14 19:57:51 +05:30
Shariq Ansari
26c892c2a0 fix: added products table in crm lead 2025-05-14 19:57:09 +05:30
Shariq Ansari
3516e1ff44 fix: update field visibility logic and disable inputs based on read-only status 2025-05-14 19:08:29 +05:30
Shariq Ansari
0047077074 feat: enhance FormattedInput component with description slot and useAttrs for better attribute handling 2025-05-14 19:01:43 +05:30
Shariq Ansari
8459fac184 fix: set discount amount and net amount fields to read-only 2025-05-14 17:16:32 +05:30
Shariq Ansari
afe828f012 fix: update mandatory field indicator color and replace FormControl with FormattedInput for various field types 2025-05-14 16:55:19 +05:30
Shariq Ansari
60ed0a2043 fix: cache controller on document level not on doctype level 2025-05-14 15:21:47 +05:30
Shariq Ansari
2c9bc07dec fix: added default percent & int to 0 2025-05-14 15:21:07 +05:30
Shariq Ansari
91ba11b565 feat: added callback to update link field value after creating new 2025-05-14 14:04:25 +05:30
Shariq Ansari
8f79427720 fix: select text on focus 2025-05-14 13:34:21 +05:30
Shariq Ansari
32f3aaf38f fix: show formatted percent, currency & float only when not focused 2025-05-14 13:19:54 +05:30
Shariq Ansari
76aaf7f37d feat: added global create document modal for link field 2025-05-12 19:27:41 +05:30
Shariq Ansari
7d37c606cc fix: added columns for product code field 2025-05-12 17:51:36 +05:30
Shariq Ansari
6bce89f277 fix: right aligned number fields 2025-05-12 17:47:49 +05:30
Shariq Ansari
5420fcfe29 fix: render correct currency format 2025-05-12 17:45:05 +05:30
Shariq Ansari
8507c20481 fix: do not show qty and other fields 2025-05-12 17:20:28 +05:30
Shariq Ansari
914dd8bf93 fix: handle this.doc.getRow effectively 2025-05-12 16:37:33 +05:30
Shariq Ansari
960ebdc727 fix: handle field change for float, percent & currency 2025-05-12 16:34:48 +05:30
Shariq Ansari
74ef956638 fix: removed total quantity field 2025-05-12 16:32:09 +05:30
Shariq Ansari
a6323f42af fix: added logic to update amount, net amount, total and net total 2025-05-12 11:58:07 +05:30
Shariq Ansari
bc1c20c91f fix: added crm products table in crm deal and total field 2025-05-12 11:58:07 +05:30
Shariq Ansari
43297373ed fix: added crm product doctype 2025-05-12 11:58:07 +05:30
Shariq Ansari
5228755f7f fix: show formatted percent, currency & float in grid 2025-05-12 11:58:07 +05:30
Shariq Ansari
7ded0a0742 fix: show formatted percent, currency if read only 2025-05-12 11:53:03 +05:30
Shariq Ansari
d74ff9ab62
Merge pull request #811 from shariquerik/required-field-modal-fix 2025-05-12 11:30:32 +05:30
Shariq Ansari
6ef27106df
Merge branch 'develop' into required-field-modal-fix 2025-05-12 11:19:37 +05:30
Shariq Ansari
35a27101c1 fix: error if section is removed and saved 2025-05-12 11:18:47 +05:30
Shariq Ansari
6fbe75c8ad
Merge pull request #803 from frappe/pot_develop_2025-05-04 2025-05-09 20:30:58 +05:30
Shariq Ansari
89fd754efc
Merge pull request #806 from shariquerik/form-script-refactor 2025-05-09 20:24:49 +05:30
Shariq Ansari
576763fe5b fix: enhance error and warning messages with localization support 2025-05-09 18:00:46 +05:30
Shariq Ansari
c67ec08e1a fix: update toast messages for document update success and error handling 2025-05-09 17:53:28 +05:30
Shariq Ansari
6f49573f2f fix: add loading state check to prevent rendering issues in SidePanelLayout 2025-05-09 17:50:43 +05:30
Shariq Ansari
12c3290f19 fix: streamline trigger functions to use a unified handler for controller actions 2025-05-09 17:38:10 +05:30
Shariq Ansari
53c0706a3a feat: implement runSequentially utility for sequential function execution 2025-05-09 17:06:41 +05:30
Shariq Ansari
556386e446 fix: cache controllers and use Promise.all for concurrent execution 2025-05-09 15:22:40 +05:30
Shariq Ansari
07b2d9f792 fix: loop through controllers with multiple instances of multiple scripts and run trigger methods 2025-05-08 18:32:56 +05:30
Shariq Ansari
a2081da296 fix: provide array of instances in controllers if multiple script exist 2025-05-08 18:32:13 +05:30
Shariq Ansari
dde7db9489 fix: remove deprecated setupForm warning and error handling 2025-05-07 19:07:54 +05:30
Shariq Ansari
f947f55fc6 fix: do not show non value fields in dropdown 2025-05-07 19:05:10 +05:30
Shariq Ansari
7bbac6c703 fix: use dayjs for date field default value 2025-05-07 18:26:23 +05:30
Shariq Ansari
420ecb6147 fix: update all fields default value 2025-05-07 18:18:14 +05:30
Shariq Ansari
dcb2787498 feat: handle default value in grid 2025-05-07 18:12:22 +05:30
Shariq Ansari
336083a00f feat: added trigger function on row add & remove 2025-05-07 18:06:38 +05:30
Shariq Ansari
727d0a9acd fix: add doctype, idx, parent, parenttype & parentfield in new grid row 2025-05-07 16:59:22 +05:30
Shariq Ansari
29894ffcca fix: handle commented class declations 2025-05-07 16:38:01 +05:30
Shariq Ansari
e804fa39ba fix: exclude Float & Currency from read only formcontrol 2025-05-07 14:01:39 +05:30
Shariq Ansari
f866284240 fix: allow empty actions 2025-05-07 13:59:55 +05:30
Shariq Ansari
9e3124d29e fix: added triggerOnRefresh & getActions method 2025-05-07 12:15:49 +05:30
Shariq Ansari
d7e0eb09b3 fix: getRow should be available in parent & child instances 2025-05-07 12:14:47 +05:30
Shariq Ansari
5fcd447bc8 fix: added this.meta 2025-05-07 12:13:56 +05:30
Shariq Ansari
6f04b85663 fix: added this.doc.trigger & this.doc.getRow with row.trigger 2025-05-06 13:03:56 +05:30
Shariq Ansari
47262761fe fix: handle section.contacts also 2025-05-06 12:59:31 +05:30
frappe-pr-bot
b46e7a2185 chore: update POT file 2025-05-04 09:35:32 +00:00
Shariq Ansari
2d484c1ad2 fix: handle onchange of grid row field in modal 2025-05-03 15:35:10 +05:30
Shariq Ansari
275fa90a4d fix: added trigger method to call methods from same or different class instance 2025-05-02 18:25:51 +05:30
Shariq Ansari
f8956c70bf fix: handle onchange of grid row field 2025-05-02 16:56:57 +05:30
Shariq Ansari
39fa9c78f8 fix: parse multiple class in form script 2025-05-02 15:56:47 +05:30
Shariq Ansari
d96a29543e fix: added deprecation warning if using old formScript syntax 2025-05-02 08:03:42 +05:30
Shariq Ansari
d2d4abe91f fix: avoid none values 2025-05-02 07:28:40 +05:30
Shariq Ansari
5f567cf138 fix: added change emit in Table bulti select 2025-05-02 06:51:52 +05:30
Shariq Ansari
7bf7d94127 fix: added fieldChange method in almost all fieldtypes 2025-05-01 18:25:25 +05:30
Shariq Ansari
5b8d0d2aeb fix: check if script exist 2025-05-01 18:07:40 +05:30
Shariq Ansari
d37e585205 fix: trigger on change in Field & SidePanelLayout for Select field 2025-05-01 18:03:04 +05:30
Shariq Ansari
a30503ca5f fix: use document to load doc data in sidepanel layout 2025-05-01 18:01:53 +05:30
Shariq Ansari
e65899e384 fix: use document to load doc data in DataFields 2025-05-01 17:57:17 +05:30
Shariq Ansari
16a3f3d66c fix: created triggerOnChange method 2025-05-01 17:56:18 +05:30
Shariq Ansari
1e2f325c55 fix: setup form script in document.js 2025-05-01 17:55:54 +05:30
Shariq Ansari
ccd240f4e8 fix: created document composable to get any doctype record 2025-05-01 17:54:13 +05:30
Shariq Ansari
7b34c5eb66 fix: load script and setup class instances 2025-05-01 17:52:11 +05:30
Shariq Ansari
6da3761e76 fix: check if setupForm exist 2025-05-01 17:27:00 +05:30
Shariq Ansari
b03abdd2eb fix: get scripts api 2025-05-01 17:22:26 +05:30
Shariq Ansari
6ea4e985ef
Merge pull request #787 from frappe/pot_develop_2025-04-27 2025-04-28 12:28:29 +05:30
frappe-pr-bot
699d6cb08c chore: update POT file 2025-04-27 09:35:22 +00:00
Pratik Badhe
ac70deaf19
Merge pull request #781 from pratikb64/call-log-fix
fix: international call log issue
2025-04-23 16:12:59 +05:30
Pratik
4907db44eb fix: international call log issue 2025-04-23 15:54:54 +05:30
Pratik Badhe
81154d1f50
Merge pull request #776 from pratikb64/email-acc-localization
chore: add localization support for email account settings
2025-04-22 15:39:14 +05:30
Pratik
5eb46f6b6c chore: add localization support for email account settings 2025-04-22 15:33:28 +05:30
Shariq Ansari
001a6617f5
Merge pull request #771 from shariquerik/contact-not-loading 2025-04-22 13:11:42 +05:30
Shariq Ansari
c009373a43 fix: do not show error page while loading 2025-04-22 12:59:24 +05:30
Shariq Ansari
cef20e37c2 fix: contact page not loading 2025-04-22 12:57:20 +05:30
Shariq Ansari
20d16c6a32
Merge pull request #759 from frappe/pot_develop_2025-04-20 2025-04-21 14:36:29 +05:30
Shariq Ansari
2fc3daee70
Merge branch 'develop' into pot_develop_2025-04-20 2025-04-21 14:30:57 +05:30
Shariq Ansari
a7955ba9c5
Merge pull request #761 from shariquerik/data-tab-dirty-fix 2025-04-21 11:53:36 +05:30
Shariq Ansari
84e773eab9 fix: do not show error page while loading 2025-04-21 11:46:42 +05:30
Shariq Ansari
da4d3032be fix: mark data tab form dirty by watching field updates 2025-04-21 11:46:19 +05:30
frappe-pr-bot
d89e71ac2f chore: update POT file 2025-04-20 09:35:21 +00:00
Pratik Badhe
de806ee6d9
Merge pull request #753 from pratikb64/email-account-dark-mode
fix: dark mode email account css
2025-04-16 18:10:27 +05:30
Pratik
9c45877999 fix: dark mode email account css 2025-04-16 18:00:28 +05:30
Shariq Ansari
2059ecdb40
Merge pull request #726 from pratikb64/fix-export-logic 2025-04-14 11:19:59 +05:30
Shariq Ansari
52d66b5de4
Merge branch 'develop' into fix-export-logic 2025-04-14 11:15:25 +05:30
Shariq Ansari
fb9b026ad6 fix: restrict app in apps page if no access to FCRM module 2025-04-14 11:05:43 +05:30
Shariq Ansari
8f1b6f6b67
Merge pull request #742 from shariquerik/restrict-app-if-no-module-access-2 2025-04-14 10:36:12 +05:30
Shariq Ansari
0bd448a399 revert: restrict app in apps page if no access to FCRM module 2025-04-14 10:35:28 +05:30
Shariq Ansari
2b395a05ea
Merge pull request #734 from frappe/pot_develop_2025-04-13 2025-04-13 20:14:27 +05:30
Shariq Ansari
dce17de000
Merge pull request #735 from shariquerik/restrict-app-if-no-module-access-1 2025-04-13 20:07:47 +05:30
Shariq Ansari
3881179f72 fix: restrict app in apps page if no access to FCRM module 2025-04-13 19:59:23 +05:30
frappe-pr-bot
da0a502756 chore: update POT file 2025-04-13 09:36:51 +00:00
Pratik
cbf00e29ac refactor: make function names clearer 2025-04-11 18:09:15 +05:30
Pratik
a466766c5c refactor: remove unnecessary watchers 2025-04-08 18:22:44 +05:30
Shariq Ansari
a4781509c4
Merge branch 'develop' into fix-export-logic 2025-04-08 16:37:26 +05:30
Shariq Ansari
8a9361d822 revert: module validation 2025-04-08 16:01:23 +05:30
Shariq Ansari
e2522a492a
Merge pull request #728 from shariquerik/restrict-doc-access
fix: added ErrorPage if user does not have access to doc
2025-04-08 15:41:10 +05:30
Shariq Ansari
bab551c511
Merge branch 'develop' into restrict-doc-access 2025-04-08 15:39:37 +05:30
Shariq Ansari
c63bb16704 ci: added backport to main-hotfix ci 2025-04-08 15:36:32 +05:30
Shariq Ansari
fa56dc4791 fix: show error page if there is no access 2025-04-08 15:28:54 +05:30
Shariq Ansari
e92ee3b730 fix: check read access before loading data 2025-04-08 15:28:19 +05:30
Shariq Ansari
bb794f4887 fix: added ErrorPage component 2025-04-08 15:27:50 +05:30
Pratik
a227389e3e fix: export logic 2025-04-08 15:07:27 +05:30
Shariq Ansari
d9f0b067ca
Merge pull request #722 from shariquerik/added-mergify
ci: added mergify.yml for backport
2025-04-07 21:17:59 +05:30
Shariq Ansari
c0b708462a ci: added mergify.yml for backport 2025-04-07 18:00:59 +05:30
Shariq Ansari
adb0dfff47
Merge pull request #721 from shariquerik/restrict-app-if-no-module-access
fix: restrict app in apps page if no access to FCRM module
2025-04-07 17:37:02 +05:30
Shariq Ansari
6139cb5cb9 fix: restrict app in apps page if no access to FCRM module 2025-04-07 17:31:17 +05:30
Shariq Ansari
61d7924c54
Merge pull request #701 from frappe/pot_develop_2025-03-30
chore: update POT file
2025-04-07 16:51:16 +05:30
Shariq Ansari
899b09ac40
Merge branch 'develop' into pot_develop_2025-03-30 2025-04-07 16:51:07 +05:30
Shariq Ansari
debc9fc1cb
Merge pull request #716 from shariquerik/make-create-call
fix: Create & Make call
2025-04-07 16:49:38 +05:30
Shariq Ansari
5c76adedf3
Merge pull request #712 from shariquerik/dynamic-link
feat: Dynamic Link field support
2025-04-07 16:49:30 +05:30
Shariq Ansari
1ebb26e4c2
Merge pull request #708 from frappe/pot_develop_2025-04-06
chore: update POT file
2025-04-07 16:44:59 +05:30
Shariq Ansari
67378c1f52
Merge pull request #719 from pratikb64/default-assigned-to
fix: default "assigned to" in deals and leads list view
2025-04-07 16:44:32 +05:30
Pratik
469a22ef5f fix: default "assigned to" in deals and leads list view 2025-04-07 16:37:19 +05:30
Shariq Ansari
fdceb51fdc fix: added multi action button to make and create call 2025-04-07 15:34:46 +05:30
Shariq Ansari
97a132e05f fix: show call tab always 2025-04-07 15:32:34 +05:30
Shariq Ansari
26fabddcbe fix: handle feather icon in multi action button 2025-04-07 15:32:09 +05:30
Shariq Ansari
40370067b2 fix: dynamic variant 2025-04-07 14:13:55 +05:30
Shariq Ansari
f0bf6962e7 fix: do not show dropdown if only one option 2025-04-07 14:07:41 +05:30
Shariq Ansari
3b432a0209 fix: added multi action button 2025-04-07 13:58:58 +05:30
Shariq Ansari
c7a03922a0 feat: Dynamic Link field support 2025-04-07 13:16:52 +05:30
frappe-pr-bot
e70b4c091e chore: update POT file 2025-04-06 09:35:28 +00:00
Pratik Badhe
7e38d5e405
Merge pull request #707 from pratikb64/kanban-filter-fix
fix: kanban filter
2025-04-04 17:14:46 +05:30
Pratik
f810e82b45 fix: kanban filter 2025-04-04 17:07:54 +05:30
Pratik Badhe
dff9f93a6b
Merge pull request #704 from pratikb64/make-fields-mandatory
fix: add mandatory fields
2025-04-04 10:26:29 +05:30
Shariq Ansari
c4109ad6ac build(deps): bump frappeui to 0.1.123 2025-04-04 10:09:18 +05:30
Pratik
7a6efb900e fix: add mandatory fields 2025-04-01 17:26:46 +05:30
frappe-pr-bot
e080e47a35 chore: update POT file 2025-03-30 09:35:00 +00:00
Pratik Badhe
82599f91d8
Merge pull request #698 from pratikb64/email-settings-fix
fix: ui alignment
2025-03-28 15:34:36 +05:30
Pratik
8fa156f625 fix: ui alignment 2025-03-28 15:33:40 +05:30
Pratik Badhe
55112cefa9
Merge pull request #697 from pratikb64/email-setting-fix
fix: broken images
2025-03-27 17:38:28 +05:30
Pratik
152c7c8a91 fix: broken images 2025-03-27 17:37:39 +05:30
Pratik Badhe
aa1c0da80e
Merge pull request #696 from pratikb64/add-email-setting
feat: add email account
2025-03-27 15:33:20 +05:30
Pratik
87174f207d feat: add email account 2025-03-27 15:32:37 +05:30
Shariq Ansari
400f879d29 fix: only allow invite by email for Sales Manager & Sales User role 2025-03-26 14:44:40 +05:30
169 changed files with 23163 additions and 58692 deletions

1
.gitignore vendored
View File

@ -7,5 +7,6 @@ dev-dist
tags
node_modules
crm/public/frontend
frontend/yarn.lock
crm/www/crm.html
build

45
.mergify.yml Normal file
View File

@ -0,0 +1,45 @@
pull_request_rules:
- name: Auto-close PRs on stable branch
conditions:
- and:
- and:
- author!=shariquerik
- author!=frappe-pr-bot
- author!=mergify[bot]
- or:
- base=main
actions:
close:
comment:
message: |
@{{author}}, thanks for the contribution, but we do not accept pull requests on a stable branch. Please raise PR on develop branch.
- name: backport to develop
conditions:
- label="backport develop"
actions:
backport:
branches:
- develop
assignees:
- "{{ author }}"
- name: backport to main-hotfix
conditions:
- label="backport main-hotfix"
actions:
backport:
branches:
- main-hotfix
assignees:
- "{{ author }}"
- name: backport to main
conditions:
- label="backport main"
actions:
backport:
branches:
- main
assignees:
- "{{ author }}"

View File

@ -1,4 +1,4 @@
__version__ = "1.53.1"
__version__ = "2.0.0-dev"
__title__ = "Frappe CRM"

View File

@ -1,9 +1,9 @@
import frappe
from bs4 import BeautifulSoup
from frappe.config import get_modules_from_all_apps_for_user
from frappe.core.api.file import get_max_file_size
from frappe.translate import get_all_translations
from frappe.utils import cstr, split_emails, validate_email_address
from frappe.utils.modules import get_modules_from_all_apps_for_user
from frappe.utils.telemetry import POSTHOG_HOST_FIELD, POSTHOG_PROJECT_FIELD
@ -106,6 +106,7 @@ def invite_by_email(emails: str, role: str):
if not emails:
return
email_string = validate_email_address(emails, throw=False)
email_list = split_emails(email_string)
if not email_list:

View File

@ -1,32 +0,0 @@
import frappe
@frappe.whitelist()
def get_assignment_rules_list():
assignment_rules = []
for docname in frappe.get_all(
"Assignment Rule", filters={"document_type": ["in", ["CRM Lead", "CRM Deal"]]}
):
doc = frappe.get_value(
"Assignment Rule",
docname,
fieldname=[
"name",
"description",
"disabled",
"priority",
],
as_dict=True,
)
users_exists = bool(frappe.db.exists("Assignment Rule User", {"parent": docname.name}))
assignment_rules.append({**doc, "users_exists": users_exists})
return assignment_rules
@frappe.whitelist()
def duplicate_assignment_rule(docname, new_name):
doc = frappe.get_doc("Assignment Rule", docname)
doc.name = new_name
doc.assignment_rule_name = new_name
doc.insert()
return doc

View File

@ -420,16 +420,23 @@ def get_data(
rows.append(field)
for kc in kanban_columns:
column_filters = {column_field: kc.get("name")}
# Start with base filters
column_filters = []
# Convert and add the main filters first
if filters:
base_filters = convert_filter_to_tuple(doctype, filters)
column_filters.extend(base_filters)
# Add the column-specific filter
if column_field and kc.get("name"):
column_filters.append([doctype, column_field, "=", kc.get("name")])
order = kc.get("order")
if (column_field in filters and filters.get(column_field) != kc.get("name")) or kc.get("delete"):
if kc.get("delete"):
column_data = []
else:
column_filters.update(filters.copy())
page_length = 20
if kc.get("page_length"):
page_length = kc.get("page_length")
page_length = kc.get("page_length", 20)
if order:
column_data = get_records_based_on_order(
@ -439,26 +446,20 @@ def get_data(
column_data = frappe.get_list(
doctype,
fields=rows,
filters=convert_filter_to_tuple(doctype, column_filters),
filters=column_filters,
order_by=order_by,
page_length=page_length,
)
new_filters = filters.copy()
new_filters.update({column_field: kc.get("name")})
all_count = frappe.get_list(
doctype,
filters=convert_filter_to_tuple(doctype, new_filters),
filters=column_filters,
fields="count(*) as total_count",
)[0].total_count
kc["all_count"] = all_count
kc["count"] = len(column_data)
for d in column_data:
getCounts(d, doctype)
if order:
column_data = sorted(
column_data,
@ -662,7 +663,7 @@ def get_fields_meta(doctype, restricted_fieldtypes=None, as_array=False, only_re
@frappe.whitelist()
def remove_assignments(doctype, name, assignees, ignore_permissions=False):
assignees = frappe.parse_json(assignees)
assignees = json.loads(assignees)
if not assignees:
return
@ -750,11 +751,7 @@ def getCounts(d, doctype):
@frappe.whitelist()
def get_linked_docs_of_document(doctype, docname):
try:
doc = frappe.get_doc(doctype, docname)
except frappe.DoesNotExistError:
return []
linked_docs = get_linked_docs(doc)
dynamic_linked_docs = get_dynamic_linked_docs(doc)
@ -763,14 +760,7 @@ def get_linked_docs_of_document(doctype, docname):
docs_data = []
for doc in linked_docs:
if not doc.get("reference_doctype") or not doc.get("reference_docname"):
continue
try:
data = frappe.get_doc(doc["reference_doctype"], doc["reference_docname"])
except (frappe.DoesNotExistError, frappe.ValidationError):
continue
title = data.get("title")
if data.doctype == "CRM Call Log":
title = f"Call from {data.get('from')} to {data.get('to')}"
@ -778,9 +768,6 @@ def get_linked_docs_of_document(doctype, docname):
if data.doctype == "CRM Deal":
title = data.get("organization")
if data.doctype == "CRM Notification":
title = data.get("message")
docs_data.append(
{
"doc": data.doctype,
@ -793,41 +780,17 @@ def get_linked_docs_of_document(doctype, docname):
def remove_doc_link(doctype, docname):
if not doctype or not docname:
return
try:
linked_doc_data = frappe.get_doc(doctype, docname)
if doctype == "CRM Notification":
delete_notification_type = {
"notification_type_doctype": "",
"notification_type_doc": "",
}
delete_references = {
"reference_doctype": "",
"reference_name": "",
}
if linked_doc_data.get("notification_type_doctype") == linked_doc_data.get("reference_doctype"):
delete_references.update(delete_notification_type)
linked_doc_data.update(delete_references)
else:
linked_doc_data.update(
{
"reference_doctype": "",
"reference_docname": "",
"reference_doctype": None,
"reference_docname": None,
}
)
linked_doc_data.save(ignore_permissions=True)
except (frappe.DoesNotExistError, frappe.ValidationError):
pass
def remove_contact_link(doctype, docname):
if not doctype or not docname:
return
try:
linked_doc_data = frappe.get_doc(doctype, docname)
linked_doc_data.update(
{
@ -836,8 +799,6 @@ def remove_contact_link(doctype, docname):
}
)
linked_doc_data.save(ignore_permissions=True)
except (frappe.DoesNotExistError, frappe.ValidationError):
pass
@frappe.whitelist()
@ -846,19 +807,13 @@ def remove_linked_doc_reference(items, remove_contact=None, delete=False):
items = frappe.parse_json(items)
for item in items:
if not item.get("doctype") or not item.get("docname"):
continue
try:
if remove_contact:
remove_contact_link(item["doctype"], item["docname"])
else:
remove_doc_link(item["doctype"], item["docname"])
if delete:
frappe.delete_doc(item["doctype"], item["docname"])
except (frappe.DoesNotExistError, frappe.ValidationError):
# Skip if document doesn't exist or has validation errors
continue
return "success"
@ -867,27 +822,10 @@ def remove_linked_doc_reference(items, remove_contact=None, delete=False):
def delete_bulk_docs(doctype, items, delete_linked=False):
from frappe.desk.reportview import delete_bulk
if not doctype:
frappe.throw("Doctype is required")
if not items:
frappe.throw("Items are required")
items = frappe.parse_json(items)
if not isinstance(items, list):
frappe.throw("Items must be a list")
for doc in items:
try:
if not frappe.db.exists(doctype, doc):
frappe.log_error(f"Document {doctype} {doc} does not exist", "Bulk Delete Error")
continue
linked_docs = get_linked_docs_of_document(doctype, doc)
for linked_doc in linked_docs:
if not linked_doc.get("reference_doctype") or not linked_doc.get("reference_docname"):
continue
remove_linked_doc_reference(
[
{
@ -898,10 +836,6 @@ def delete_bulk_docs(doctype, items, delete_linked=False):
remove_contact=doctype == "Contact",
delete=delete_linked,
)
except Exception as e:
frappe.log_error(
f"Error processing linked docs for {doctype} {doc}: {str(e)}", "Bulk Delete Error"
)
if len(items) > 10:
frappe.enqueue("frappe.desk.reportview.delete_bulk", doctype=doctype, items=items)

View File

@ -1,19 +1,28 @@
import frappe
from frappe import _
from crm.fcrm.doctype.crm_notification.crm_notification import notify_user
def after_insert(doc, method):
if doc.reference_type in ["CRM Lead", "CRM Deal"] and doc.reference_name and doc.allocated_to:
if (
doc.reference_type in ["CRM Lead", "CRM Deal"]
and doc.reference_name
and doc.allocated_to
):
fieldname = "lead_owner" if doc.reference_type == "CRM Lead" else "deal_owner"
owner = frappe.db.get_value(doc.reference_type, doc.reference_name, fieldname)
if not owner:
lead_owner = frappe.db.get_value(
doc.reference_type, doc.reference_name, fieldname
)
if not lead_owner:
frappe.db.set_value(
doc.reference_type, doc.reference_name, fieldname, doc.allocated_to, update_modified=False
doc.reference_type, doc.reference_name, fieldname, doc.allocated_to
)
if doc.reference_type in ["CRM Lead", "CRM Deal", "CRM Task"] and doc.reference_name and doc.allocated_to:
if (
doc.reference_type in ["CRM Lead", "CRM Deal", "CRM Task"]
and doc.reference_name
and doc.allocated_to
):
notify_assigned_user(doc)
@ -38,7 +47,9 @@ def notify_assigned_user(doc, is_cancelled=False):
doc.reference_type, doc.reference_name, owner
)
if is_cancelled
else _("{0} assigned a {1} {2} to you").format(owner, doc.reference_type, doc.reference_name)
else _("{0} assigned a {1} {2} to you").format(
owner, doc.reference_type, doc.reference_name
)
)
redirect_to_doctype, redirect_to_name = get_redirect_to_doc(doc)

View File

@ -10,13 +10,6 @@ from crm.fcrm.doctype.crm_notification.crm_notification import notify_user
def validate(doc, method):
if doc.type == "Incoming" and doc.get("from"):
name, doctype = get_lead_or_deal_from_number(doc.get("from"))
if name != None:
doc.reference_doctype = doctype
doc.reference_name = name
if doc.type == "Outgoing" and doc.get("to"):
name, doctype = get_lead_or_deal_from_number(doc.get("to"))
if name != None:
doc.reference_doctype = doctype
doc.reference_name = name
@ -36,7 +29,7 @@ def on_update(doc, method):
def notify_agent(doc):
if doc.type == "Incoming":
doctype = doc.reference_doctype
if doctype and doctype.startswith("CRM "):
if doctype.startswith("CRM "):
doctype = doctype[4:].lower()
notification_text = f"""
<div class="mb-2 leading-5 text-ink-gray-5">

View File

@ -26,9 +26,8 @@ def create_default_manager_dashboard(force=False):
doc.title = "Manager Dashboard"
doc.layout = default_manager_dashboard_layout()
doc.insert(ignore_permissions=True)
else:
elif force:
doc = frappe.get_doc("CRM Dashboard", "Manager Dashboard")
if force:
doc.layout = default_manager_dashboard_layout()
doc.save(ignore_permissions=True)
return doc.layout

View File

@ -129,13 +129,15 @@
"fieldname": "email",
"fieldtype": "Data",
"label": "Primary Email",
"options": "Email"
"options": "Email",
"read_only": 1
},
{
"fieldname": "mobile_no",
"fieldtype": "Data",
"label": "Primary Mobile No",
"options": "Phone"
"options": "Phone",
"read_only": 1
},
{
"default": "Qualification",
@ -249,7 +251,8 @@
"fieldname": "phone",
"fieldtype": "Data",
"label": "Primary Phone",
"options": "Phone"
"options": "Phone",
"read_only": 1
},
{
"fieldname": "log_tab",
@ -432,7 +435,7 @@
"grid_page_length": 50,
"index_web_pages_for_search": 1,
"links": [],
"modified": "2025-08-26 12:12:56.324245",
"modified": "2025-07-13 11:54:20.608489",
"modified_by": "Administrator",
"module": "FCRM",
"name": "CRM Deal",

View File

@ -25,7 +25,7 @@ class CRMDeal(Document):
add_status_change_log(self)
if frappe.db.get_value("CRM Deal Status", self.status, "type") == "Won":
self.closed_date = frappe.utils.nowdate()
self.validate_forecasting_fields()
self.validate_forcasting_fields()
self.validate_lost_reason()
self.update_exchange_rate()
@ -151,21 +151,9 @@ class CRMDeal(Document):
if not self.probability or self.probability == 0:
self.probability = frappe.db.get_value("CRM Deal Status", self.status, "probability") or 0
def update_expected_deal_value(self):
"""
Update the expected deal value based on the net total or total.
"""
if (
frappe.db.get_single_value("FCRM Settings", "auto_update_expected_deal_value")
and (self.net_total or self.total)
and self.expected_deal_value
):
self.expected_deal_value = self.net_total or self.total
def validate_forecasting_fields(self):
def validate_forcasting_fields(self):
self.update_closed_date()
self.update_default_probability()
self.update_expected_deal_value()
if frappe.db.get_single_value("FCRM Settings", "enable_forecasting"):
if not self.expected_deal_value or self.expected_deal_value == 0:
frappe.throw(_("Expected Deal Value is required."), frappe.MandatoryError)

View File

@ -63,7 +63,8 @@
"fieldname": "twiml_sid",
"fieldtype": "Data",
"label": "TwiML SID",
"permlevel": 1
"permlevel": 1,
"read_only": 1
},
{
"fieldname": "section_break_ssqj",
@ -104,7 +105,7 @@
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
"modified": "2025-08-19 13:36:19.823197",
"modified": "2025-01-15 19:35:13.406254",
"modified_by": "Administrator",
"module": "FCRM",
"name": "CRM Twilio Settings",
@ -151,7 +152,6 @@
"write": 1
}
],
"row_format": "Dynamic",
"sort_field": "modified",
"sort_order": "DESC",
"states": [],

View File

@ -128,35 +128,14 @@ def get_quotation_url(crm_deal, organization):
address = address.get("name") if address else None
if not erpnext_crm_settings.is_erpnext_in_different_site:
base_url = f"{get_url_to_list('Quotation')}/new"
params = {
"quotation_to": "CRM Deal",
"crm_deal": crm_deal,
"party_name": crm_deal,
"company": erpnext_crm_settings.erpnext_company,
"contact_person": contact,
"customer_address": address
}
quotation_url = get_url_to_list("Quotation")
return f"{quotation_url}/new?quotation_to=CRM Deal&crm_deal={crm_deal}&party_name={crm_deal}&company={erpnext_crm_settings.erpnext_company}&contact_person={contact}&customer_address={address}"
else:
site_url = erpnext_crm_settings.get("erpnext_site_url")
base_url = f"{site_url}/app/quotation/new"
quotation_url = f"{site_url}/app/quotation"
prospect = create_prospect_in_remote_site(crm_deal, erpnext_crm_settings)
params = {
"quotation_to": "Prospect",
"crm_deal": crm_deal,
"party_name": prospect,
"company": erpnext_crm_settings.erpnext_company,
"contact_person": contact,
"customer_address": address
}
# Filter out None values and build query string
query_string = "&".join(
f"{key}={value}" for key, value in params.items()
if value is not None
)
return f"{base_url}?{query_string}"
return f"{quotation_url}/new?quotation_to=Prospect&crm_deal={crm_deal}&party_name={prospect}&company={erpnext_crm_settings.erpnext_company}&contact_person={contact}&customer_address={address}"
def create_prospect_in_remote_site(crm_deal, erpnext_crm_settings):

View File

@ -8,7 +8,6 @@
"defaults_tab",
"restore_defaults",
"enable_forecasting",
"auto_update_expected_deal_value",
"currency_tab",
"currency",
"exchange_rate_provider_section",
@ -106,19 +105,12 @@
{
"fieldname": "column_break_vqck",
"fieldtype": "Column Break"
},
{
"default": "1",
"description": "Automatically update \"Expected Deal Value\" based on the total value of associated products in a deal",
"fieldname": "auto_update_expected_deal_value",
"fieldtype": "Check",
"label": "Auto Update Expected Deal Value"
}
],
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
"modified": "2025-09-16 17:33:26.406549",
"modified": "2025-07-29 11:26:50.420614",
"modified_by": "Administrator",
"module": "FCRM",
"name": "FCRM Settings",

View File

@ -25,8 +25,6 @@ def after_install(force=False):
add_standard_dropdown_items()
add_default_scripts()
create_default_manager_dashboard(force)
create_assignment_rule_custom_fields()
add_assignment_rule_property_setters()
frappe.db.commit()
@ -194,7 +192,7 @@ def add_default_fields_layout(force=False):
},
"CRM Deal-Data Fields": {
"doctype": "CRM Deal",
"layout": '[{"name":"first_tab","sections":[{"label":"Details","name":"details_section","opened":true,"columns":[{"name":"column_z9XL","fields":["organization","annual_revenue","next_step"]},{"name":"column_gM4w","fields":["website","closed_date","deal_owner"]},{"name":"column_gWmE","fields":["territory","probability"]}]},{"label":"Products","name":"section_jHhQ","opened":true,"columns":[{"name":"column_xiNF","fields":["products"]}],"editingLabel":false,"hideLabel":true},{"label":"New Section","name":"section_WNOQ","opened":true,"columns":[{"name":"column_ziBW","fields":["total"]},{"label":"","name":"column_wuwA","fields":["net_total"]}],"hideBorder":true,"hideLabel":true}]}]',
"layout": '[{"label": "Details", "name": "details_section", "opened": true, "columns": [{"name": "column_z9XL", "fields": ["organization", "annual_revenue", "next_step"]}, {"name": "column_gM4w", "fields": ["website", "close_date", "deal_owner"]}, {"name": "column_gWmE", "fields": ["territory", "probability"]}]}]',
},
}
@ -423,80 +421,3 @@ def add_default_scripts():
for doctype in ["CRM Lead", "CRM Deal"]:
create_product_details_script(doctype)
create_forecasting_script()
def add_assignment_rule_property_setters():
"""Add a property setter to the Assignment Rule DocType for assign_condition and unassign_condition."""
default_fields = {
"doctype": "Property Setter",
"doctype_or_field": "DocField",
"doc_type": "Assignment Rule",
"property_type": "Data",
"is_system_generated": 1,
}
if not frappe.db.exists("Property Setter", {"name": "Assignment Rule-assign_condition-depends_on"}):
frappe.get_doc(
{
**default_fields,
"name": "Assignment Rule-assign_condition-depends_on",
"field_name": "assign_condition",
"property": "depends_on",
"value": "eval: !doc.assign_condition_json",
}
).insert()
else:
frappe.db.set_value(
"Property Setter",
{"name": "Assignment Rule-assign_condition-depends_on"},
"value",
"eval: !doc.assign_condition_json",
)
if not frappe.db.exists("Property Setter", {"name": "Assignment Rule-unassign_condition-depends_on"}):
frappe.get_doc(
{
**default_fields,
"name": "Assignment Rule-unassign_condition-depends_on",
"field_name": "unassign_condition",
"property": "depends_on",
"value": "eval: !doc.unassign_condition_json",
}
).insert()
else:
frappe.db.set_value(
"Property Setter",
{"name": "Assignment Rule-unassign_condition-depends_on"},
"value",
"eval: !doc.unassign_condition_json",
)
def create_assignment_rule_custom_fields():
if not frappe.get_meta("Assignment Rule").has_field("assign_condition_json"):
click.secho("* Installing Custom Fields in Assignment Rule")
create_custom_fields(
{
"Assignment Rule": [
{
"description": "Autogenerated field by CRM App",
"fieldname": "assign_condition_json",
"fieldtype": "Code",
"label": "Assign Condition JSON",
"insert_after": "assign_condition",
"depends_on": "eval: doc.assign_condition_json",
},
{
"description": "Autogenerated field by CRM App",
"fieldname": "unassign_condition_json",
"fieldtype": "Code",
"label": "Unassign Condition JSON",
"insert_after": "unassign_condition",
"depends_on": "eval: doc.unassign_condition_json",
},
],
}
)
frappe.clear_cache(doctype="Assignment Rule")

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -15,5 +15,3 @@ crm.patches.v1_0.move_twilio_agent_to_telephony_agent
crm.patches.v1_0.create_default_scripts # 13-06-2025
crm.patches.v1_0.update_deal_status_probabilities
crm.patches.v1_0.update_deal_status_type
crm.patches.v1_0.create_default_lost_reasons
crm.patches.v1_0.add_fields_in_assignment_rule

View File

@ -1,9 +0,0 @@
from crm.install import (
add_assignment_rule_property_setters,
create_assignment_rule_custom_fields,
)
def execute():
create_assignment_rule_custom_fields()
add_assignment_rule_property_setters()

View File

@ -1,5 +0,0 @@
from crm.install import add_default_lost_reasons
def execute():
add_default_lost_reasons()

@ -1 +1 @@
Subproject commit c9a0fc937cc897864857271b3708a0c675379015
Subproject commit b295b54aaa3a2a22f455df819d87433dc6b9ff6a

1
frontend/.gitignore vendored
View File

@ -3,4 +3,3 @@ node_modules
dist
dist-ssr
*.local
components.d.ts

View File

@ -1,10 +0,0 @@
/* eslint-disable */
/* prettier-ignore */
// @ts-nocheck
// noinspection JSUnusedGlobalSymbols
// Generated by unplugin-auto-import
// biome-ignore lint: disable
export {}
declare global {
}

View File

@ -25,7 +25,6 @@ declare module 'vue' {
AscendingIcon: typeof import('./src/components/Icons/AscendingIcon.vue')['default']
AssignmentModal: typeof import('./src/components/Modals/AssignmentModal.vue')['default']
AssignTo: typeof import('./src/components/AssignTo.vue')['default']
AssignToBody: typeof import('./src/components/AssignToBody.vue')['default']
AttachmentArea: typeof import('./src/components/Activities/AttachmentArea.vue')['default']
AttachmentIcon: typeof import('./src/components/Icons/AttachmentIcon.vue')['default']
AttachmentItem: typeof import('./src/components/AttachmentItem.vue')['default']
@ -33,7 +32,7 @@ declare module 'vue' {
Autocomplete: typeof import('./src/components/frappe-ui/Autocomplete.vue')['default']
AvatarIcon: typeof import('./src/components/Icons/AvatarIcon.vue')['default']
BrandLogo: typeof import('./src/components/BrandLogo.vue')['default']
BrandSettings: typeof import('./src/components/Settings/BrandSettings.vue')['default']
BrandSettings: typeof import('./src/components/Settings/General/BrandSettings.vue')['default']
BulkDeleteLinkedDocModal: typeof import('./src/components/BulkDeleteLinkedDocModal.vue')['default']
CalendarIcon: typeof import('./src/components/Icons/CalendarIcon.vue')['default']
CallArea: typeof import('./src/components/Activities/CallArea.vue')['default']
@ -63,7 +62,7 @@ declare module 'vue' {
CountUpTimer: typeof import('./src/components/CountUpTimer.vue')['default']
CreateDocumentModal: typeof import('./src/components/Modals/CreateDocumentModal.vue')['default']
CRMLogo: typeof import('./src/components/Icons/CRMLogo.vue')['default']
CurrencySettings: typeof import('./src/components/Settings/CurrencySettings.vue')['default']
CurrencySettings: typeof import('./src/components/Settings/General/CurrencySettings.vue')['default']
CustomActions: typeof import('./src/components/CustomActions.vue')['default']
DashboardGrid: typeof import('./src/components/Dashboard/DashboardGrid.vue')['default']
DashboardIcon: typeof import('./src/components/Icons/DashboardIcon.vue')['default']
@ -85,6 +84,7 @@ declare module 'vue' {
DragIcon: typeof import('./src/components/Icons/DragIcon.vue')['default']
DragVerticalIcon: typeof import('./src/components/Icons/DragVerticalIcon.vue')['default']
Dropdown: typeof import('./src/components/frappe-ui/Dropdown.vue')['default']
DropdownItem: typeof import('./src/components/DropdownItem.vue')['default']
DuplicateIcon: typeof import('./src/components/Icons/DuplicateIcon.vue')['default']
DurationIcon: typeof import('./src/components/Icons/DurationIcon.vue')['default']
EditEmailTemplate: typeof import('./src/components/Settings/EmailTemplate/EditEmailTemplate.vue')['default']
@ -127,10 +127,11 @@ declare module 'vue' {
FileVideoIcon: typeof import('./src/components/Icons/FileVideoIcon.vue')['default']
Filter: typeof import('./src/components/Filter.vue')['default']
FilterIcon: typeof import('./src/components/Icons/FilterIcon.vue')['default']
ForecastingSettings: typeof import('./src/components/Settings/ForecastingSettings.vue')['default']
FormattedInput: typeof import('./src/components/Controls/FormattedInput.vue')['default']
FrappeCloudIcon: typeof import('./src/components/Icons/FrappeCloudIcon.vue')['default']
GenderIcon: typeof import('./src/components/Icons/GenderIcon.vue')['default']
GeneralSettings: typeof import('./src/components/Settings/General/GeneralSettings.vue')['default']
GeneralSettingsPage: typeof import('./src/components/Settings/General/GeneralSettingsPage.vue')['default']
GlobalModals: typeof import('./src/components/Modals/GlobalModals.vue')['default']
GoogleIcon: typeof import('./src/components/Icons/GoogleIcon.vue')['default']
Grid: typeof import('./src/components/Controls/Grid.vue')['default']
@ -141,7 +142,7 @@ declare module 'vue' {
GroupByIcon: typeof import('./src/components/Icons/GroupByIcon.vue')['default']
HeartIcon: typeof import('./src/components/Icons/HeartIcon.vue')['default']
HelpIcon: typeof import('./src/components/Icons/HelpIcon.vue')['default']
HomeActions: typeof import('./src/components/Settings/HomeActions.vue')['default']
HomeActions: typeof import('./src/components/Settings/General/HomeActions.vue')['default']
Icon: typeof import('./src/components/Icon.vue')['default']
IconPicker: typeof import('./src/components/IconPicker.vue')['default']
ImageUploader: typeof import('./src/components/Controls/ImageUploader.vue')['default']
@ -168,6 +169,9 @@ declare module 'vue' {
LoadingIndicator: typeof import('./src/components/Icons/LoadingIndicator.vue')['default']
LostReasonModal: typeof import('./src/components/Modals/LostReasonModal.vue')['default']
LucideCalendar: typeof import('~icons/lucide/calendar')['default']
LucideChevronRight: typeof import('~icons/lucide/chevron-right')['default']
LucidePenLine: typeof import('~icons/lucide/pen-line')['default']
LucideRefreshCcw: typeof import('~icons/lucide/refresh-ccw')['default']
MarkAsDoneIcon: typeof import('./src/components/Icons/MarkAsDoneIcon.vue')['default']
MaximizeIcon: typeof import('./src/components/Icons/MaximizeIcon.vue')['default']
MenuIcon: typeof import('./src/components/Icons/MenuIcon.vue')['default']
@ -182,6 +186,7 @@ declare module 'vue' {
MultiSelectEmailInput: typeof import('./src/components/Controls/MultiSelectEmailInput.vue')['default']
MultiSelectUserInput: typeof import('./src/components/Controls/MultiSelectUserInput.vue')['default']
MuteIcon: typeof import('./src/components/Icons/MuteIcon.vue')['default']
NestedPopover: typeof import('./src/components/NestedPopover.vue')['default']
NewEmailTemplate: typeof import('./src/components/Settings/EmailTemplate/NewEmailTemplate.vue')['default']
NoteArea: typeof import('./src/components/Activities/NoteArea.vue')['default']
NoteIcon: typeof import('./src/components/Icons/NoteIcon.vue')['default']
@ -200,8 +205,6 @@ declare module 'vue' {
PlaybackSpeedOption: typeof import('./src/components/Activities/PlaybackSpeedOption.vue')['default']
PlayIcon: typeof import('./src/components/Icons/PlayIcon.vue')['default']
Popover: typeof import('./src/components/frappe-ui/Popover.vue')['default']
PrimaryDropdown: typeof import('./src/components/PrimaryDropdown.vue')['default']
PrimaryDropdownItem: typeof import('./src/components/PrimaryDropdownItem.vue')['default']
ProfileSettings: typeof import('./src/components/Settings/ProfileSettings.vue')['default']
QuickEntryModal: typeof import('./src/components/Modals/QuickEntryModal.vue')['default']
QuickFilterField: typeof import('./src/components/QuickFilterField.vue')['default']
@ -228,7 +231,6 @@ declare module 'vue' {
SmileIcon: typeof import('./src/components/Icons/SmileIcon.vue')['default']
SortBy: typeof import('./src/components/SortBy.vue')['default']
SortIcon: typeof import('./src/components/Icons/SortIcon.vue')['default']
SparkleIcon: typeof import('./src/components/Icons/SparkleIcon.vue')['default']
SquareAsterisk: typeof import('./src/components/Icons/SquareAsterisk.vue')['default']
StepsIcon: typeof import('./src/components/Icons/StepsIcon.vue')['default']
SuccessIcon: typeof import('./src/components/Icons/SuccessIcon.vue')['default']

View File

@ -13,7 +13,7 @@
"@tiptap/extension-paragraph": "^2.12.0",
"@twilio/voice-sdk": "^2.10.2",
"@vueuse/integrations": "^10.3.0",
"frappe-ui": "^0.1.201",
"frappe-ui": "^0.1.171",
"gemoji": "^8.1.0",
"lodash": "^4.17.21",
"mime": "^4.0.1",

View File

@ -50,13 +50,11 @@
class="activity grid grid-cols-[30px_minmax(auto,_1fr)] gap-2 px-3 sm:gap-4 sm:px-10"
>
<div
class="z-0 relative flex justify-center before:absolute before:left-[50%] before:-z-[1] before:top-0 before:border-l before:border-outline-gray-modals"
:class="
i != activities.length - 1 ? 'before:h-full' : 'before:h-4'
"
class="relative flex justify-center after:absolute after:left-[50%] after:top-0 after:-z-10 after:border-l after:border-outline-gray-modals"
:class="i != activities.length - 1 ? 'after:h-full' : 'after:h-4'"
>
<div
class="flex h-8 w-7 items-center justify-center bg-surface-white"
class="z-10 flex h-8 w-7 items-center justify-center bg-surface-white"
>
<CommentIcon class="text-ink-gray-8" />
</div>
@ -74,13 +72,11 @@
class="activity grid grid-cols-[30px_minmax(auto,_1fr)] gap-4 px-3 sm:px-10"
>
<div
class="z-0 relative flex justify-center before:absolute before:left-[50%] before:-z-[1] before:top-0 before:border-l before:border-outline-gray-modals"
:class="
i != activities.length - 1 ? 'before:h-full' : 'before:h-4'
"
class="relative flex justify-center after:absolute after:left-[50%] after:top-0 after:-z-10 after:border-l after:border-outline-gray-modals"
:class="i != activities.length - 1 ? 'after:h-full' : 'after:h-4'"
>
<div
class="flex h-8 w-7 items-center justify-center bg-surface-white text-ink-gray-8"
class="z-10 flex h-8 w-7 items-center justify-center bg-surface-white text-ink-gray-8"
>
<MissedCallIcon
v-if="call.status == 'No Answer'"
@ -120,11 +116,11 @@
>
<div
v-if="['Activity', 'Emails'].includes(title)"
class="z-0 relative flex justify-center before:absolute before:left-[50%] before:-z-[1] before:top-0 before:border-l before:border-outline-gray-modals"
class="relative flex justify-center before:absolute before:left-[50%] before:top-0 before:-z-10 before:border-l before:border-outline-gray-modals"
:class="[i != activities.length - 1 ? 'before:h-full' : 'before:h-4']"
>
<div
class="flex h-7 w-7 items-center justify-center bg-surface-white"
class="z-10 flex h-7 w-7 items-center justify-center bg-surface-white"
:class="{
'mt-2.5': ['communication'].includes(activity.activity_type),
'bg-surface-white': ['added', 'removed', 'changed'].includes(
@ -238,9 +234,12 @@
<Button
class="!size-4"
variant="ghost"
:icon="SelectIcon"
@click="activity.show_others = !activity.show_others"
/>
>
<template #icon>
<SelectIcon />
</template>
</Button>
</div>
<div
v-else
@ -556,7 +555,6 @@ const all_activities = createResource({
transform: ([versions, calls, notes, tasks, attachments]) => {
return { versions, calls, notes, tasks, attachments }
},
onSuccess: () => nextTick(() => scroll()),
})
const showWhatsappTemplates = ref(false)

View File

@ -9,17 +9,23 @@
<Button
v-if="title == 'Emails'"
variant="solid"
:label="__('New Email')"
iconLeft="plus"
@click="emailBox.show = true"
/>
>
<template #prefix>
<FeatherIcon name="plus" class="h-4 w-4" />
</template>
<span>{{ __('New Email') }}</span>
</Button>
<Button
v-else-if="title == 'Comments'"
variant="solid"
:label="__('New Comment')"
iconLeft="plus"
@click="emailBox.showComment = true"
/>
>
<template #prefix>
<FeatherIcon name="plus" class="h-4 w-4" />
</template>
<span>{{ __('New Comment') }}</span>
</Button>
<MultiActionButton
v-else-if="title == 'Calls'"
variant="solid"
@ -28,46 +34,60 @@
<Button
v-else-if="title == 'Notes'"
variant="solid"
:label="__('New Note')"
iconLeft="plus"
@click="modalRef.showNote()"
/>
>
<template #prefix>
<FeatherIcon name="plus" class="h-4 w-4" />
</template>
<span>{{ __('New Note') }}</span>
</Button>
<Button
v-else-if="title == 'Tasks'"
variant="solid"
:label="__('New Task')"
iconLeft="plus"
@click="modalRef.showTask()"
/>
>
<template #prefix>
<FeatherIcon name="plus" class="h-4 w-4" />
</template>
<span>{{ __('New Task') }}</span>
</Button>
<Button
v-else-if="title == 'Attachments'"
variant="solid"
:label="__('Upload Attachment')"
iconLeft="plus"
@click="showFilesUploader = true"
/>
>
<template #prefix>
<FeatherIcon name="plus" class="h-4 w-4" />
</template>
<span>{{ __('Upload Attachment') }}</span>
</Button>
<div class="flex gap-2 shrink-0" v-else-if="title == 'WhatsApp'">
<Button
:label="__('Send Template')"
@click="showWhatsappTemplates = true"
/>
<Button
variant="solid"
:label="__('New Message')"
iconLeft="plus"
@click="whatsappBox.show()"
/>
<Button variant="solid" @click="whatsappBox.show()">
<template #prefix>
<FeatherIcon name="plus" class="h-4 w-4" />
</template>
<span>{{ __('New Message') }}</span>
</Button>
</div>
<Dropdown v-else :options="defaultActions" @click.stop>
<template v-slot="{ open }">
<Button
variant="solid"
class="flex items-center gap-1"
:label="__('New')"
iconLeft="plus"
:iconRight="open ? 'chevron-up' : 'chevron-down'"
<Button variant="solid" class="flex items-center gap-1">
<template #prefix>
<FeatherIcon name="plus" class="h-4 w-4" />
</template>
<span>{{ __('New') }}</span>
<template #suffix>
<FeatherIcon
:name="open ? 'chevron-up' : 'chevron-down'"
class="h-4 w-4"
/>
</template>
</Button>
</template>
</Dropdown>
</div>
</template>

View File

@ -38,10 +38,13 @@
</div>
</Tooltip>
<div class="flex gap-1">
<Button
:tooltip="
<Tooltip
:text="
attachment.is_private ? __('Make public') : __('Make private')
"
>
<div>
<Button
class="!size-5"
@click.stop="
togglePrivate(attachment.name, attachment.is_private)
@ -54,16 +57,24 @@
/>
</template>
</Button>
</div>
</Tooltip>
<Tooltip :text="__('Delete attachment')">
<div>
<Button
:tooltip="__('Delete attachment')"
class="!size-5"
@click.stop="() => deleteAttachment(attachment.name)"
>
<template #icon>
<FeatherIcon name="trash-2" class="size-3 text-ink-gray-7" />
<FeatherIcon
name="trash-2"
class="size-3 text-ink-gray-7"
/>
</template>
</Button>
</div>
</Tooltip>
</div>
</div>
</div>
<div

View File

@ -1,12 +1,12 @@
<template>
<div class="w-full text-sm text-ink-gray-5">
<div class="flex items-center gap-2">
<Button
variant="ghost"
class="text-ink-gray-5"
:icon="isPaused ? PlayIcon : PauseIcon"
@click="playPause"
/>
<Button variant="ghost" @click="playPause">
<template #icon>
<PlayIcon v-if="isPaused" class="size-4 text-ink-gray-5" />
<PauseIcon v-else class="size-4 text-ink-gray-5" />
</template>
</Button>
<div class="flex gap-2 items-center justify-between flex-1">
<input
class="w-full slider !h-[0.5] bg-surface-gray-3 [&::-webkit-slider-thumb]:shadow [&::-webkit-slider-thumb:hover]:outline [&::-webkit-slider-thumb:hover]:outline-[0.5px]"
@ -61,11 +61,11 @@
</Button>
</div>
<Dropdown :options="options">
<Button
icon="more-horizontal"
variant="ghost"
@click="showPlaybackSpeed = false"
/>
<Button variant="ghost" @click="showPlaybackSpeed = false">
<template #icon>
<FeatherIcon class="size-4" name="more-horizontal" />
</template>
</Button>
</Dropdown>
</div>
</div>

View File

@ -1,5 +1,5 @@
<template>
<div>
<div @click="showCallLogDetailModal = true" class="cursor-pointer">
<div class="mb-1 flex items-center justify-stretch gap-2 py-1 text-base">
<div class="inline-flex items-center flex-wrap gap-1 text-ink-gray-5">
<Avatar
@ -25,8 +25,7 @@
</div>
</div>
<div
@click="showCallLogDetailModal = true"
class="flex flex-col gap-2 border cursor-pointer border-outline-gray-modals rounded-md bg-surface-cards px-3 py-2.5 text-ink-gray-9"
class="flex flex-col gap-2 border border-outline-gray-modals rounded-md bg-surface-cards px-3 py-2.5 text-ink-gray-9"
>
<div class="flex items-center justify-between">
<div class="inline-flex gap-2 items-center text-base font-medium">

View File

@ -14,10 +14,12 @@
<div class="flex gap-1">
<Button
v-if="isManager() && !isMobileView"
:tooltip="__('Edit fields layout')"
:icon="EditIcon"
@click="showDataFieldsModal = true"
/>
>
<template #icon>
<EditIcon />
</template>
</Button>
<Button
label="Save"
:disabled="!document.isDirty"

View File

@ -2,9 +2,7 @@
<div
class="cursor-pointer flex flex-col rounded-md shadow bg-surface-cards px-3 py-1.5 text-base transition-all duration-300 ease-in-out"
>
<div
class="-mb-0.5 flex items-center justify-between gap-2 truncate text-ink-gray-9"
>
<div class="-mb-0.5 flex items-center justify-between gap-2 truncate text-ink-gray-9">
<div class="flex items-center gap-2 truncate">
<span>{{ activity.data.sender_full_name }}</span>
<span class="sm:flex hidden text-sm text-ink-gray-5">
@ -30,20 +28,32 @@
</div>
</Tooltip>
<div class="flex gap-0.5">
<Tooltip :text="__('Reply')">
<div>
<Button
:tooltip="__('Reply')"
variant="ghost"
class="text-ink-gray-7"
:icon="ReplyIcon"
@click="reply(activity.data)"
/>
>
<template #icon>
<ReplyIcon />
</template>
</Button>
</div>
</Tooltip>
<Tooltip :text="__('Reply All')">
<div>
<Button
:tooltip="__('Reply All')"
variant="ghost"
:icon="ReplyAllIcon"
class="text-ink-gray-7"
@click="reply(activity.data, true)"
/>
>
<template #icon>
<ReplyAllIcon />
</template>
</Button>
</div>
</Tooltip>
</div>
</div>
</div>

View File

@ -41,13 +41,13 @@
:options="taskStatusOptions(modalRef.updateTaskStatus, task)"
@click.stop
>
<Button
:tooltip="__('Change status')"
variant="ghosted"
class="hover:bg-surface-gray-4"
>
<Tooltip :text="__('Change Status')">
<div>
<Button variant="ghosted" class="hover:bg-surface-gray-4">
<TaskStatusIcon :status="task.status" />
</Button>
</div>
</Tooltip>
</Dropdown>
<Dropdown
:options="[

View File

@ -1,99 +1,31 @@
<template>
<Popover placement="bottom-end">
<template #target="{ togglePopover }">
<div class="flex items-center" @click="togglePopover">
<component
v-if="assignees?.length"
:is="assignees?.length == 1 ? 'Button' : 'div'"
>
<MultipleAvatar :avatars="assignees" />
<MultipleAvatar :avatars="assignees" @click="showAssignmentModal = true" />
</component>
<Button v-else :label="__('Assign to')" />
</div>
</template>
<template #body="{ isOpen }">
<AssignToBody
v-show="isOpen"
v-model="assignees"
:docname="docname"
<Button v-else @click="showAssignmentModal = true">
{{ __('Assign to') }}
</Button>
<AssignmentModal
v-if="showAssignmentModal"
v-model="showAssignmentModal"
v-model:assignees="assignees"
:doctype="doctype"
:open="isOpen"
:onUpdate="ownerField && saveAssignees"
:doc="data"
/>
</template>
</Popover>
</template>
<script setup>
import MultipleAvatar from '@/components/MultipleAvatar.vue'
import AssignToBody from '@/components/AssignToBody.vue'
import { useDocument } from '@/data/document'
import { toast, Popover } from 'frappe-ui'
import { computed } from 'vue'
import AssignmentModal from '@/components/Modals/AssignmentModal.vue'
import { ref } from 'vue'
const props = defineProps({
data: Object,
doctype: String,
docname: String,
})
const { document } = useDocument(props.doctype, props.docname)
const showAssignmentModal = ref(false)
const assignees = defineModel()
const ownerField = computed(() => {
if (props.doctype === 'CRM Lead') {
return 'lead_owner'
} else if (props.doctype === 'CRM Deal') {
return 'deal_owner'
} else {
return null
}
})
async function saveAssignees(
addedAssignees,
removedAssignees,
addAssignees,
removeAssignees,
) {
removedAssignees.length && (await removeAssignees.submit(removedAssignees))
addedAssignees.length && (await addAssignees.submit(addedAssignees))
const nextAssignee = assignees.value.find(
(a) => a.name !== document.doc[ownerField.value],
)
let owner = ownerField.value.replace('_', ' ')
if (
document.doc[ownerField.value] &&
removedAssignees.includes(document.doc[ownerField.value])
) {
document.doc[ownerField.value] = nextAssignee ? nextAssignee.name : ''
document.save.submit()
if (nextAssignee) {
toast.info(
__(
'Since you removed {0} from the assignee, the {0} has been changed to the next available assignee {1}.',
[owner, nextAssignee.label || nextAssignee.name],
),
)
} else {
toast.info(
__(
'Since you removed {0} from the assignee, the {0} has also been removed.',
[owner],
),
)
}
} else if (!document.doc[ownerField.value] && nextAssignee) {
document.doc[ownerField.value] = nextAssignee ? nextAssignee.name : ''
toast.info(
__('Since you added a new assignee, the {0} has been set to {1}.', [
owner,
nextAssignee.label || nextAssignee.name,
]),
)
}
}
</script>

View File

@ -1,211 +0,0 @@
<template>
<div
class="flex flex-col gap-2 my-2 w-[470px] rounded-lg bg-surface-modal shadow-2xl ring-1 ring-black p-3 ring-opacity-5 focus:outline-none"
>
<div class="text-base text-ink-gray-5">{{ __('Assign to') }}</div>
<Link
class="form-control"
value=""
doctype="User"
@change="(option) => addValue(option) && ($refs.input.value = '')"
:placeholder="__('John Doe')"
:filters="{
name: ['in', users.data.crmUsers?.map((user) => user.name)],
}"
:hideMe="true"
>
<template #target="{ togglePopover }">
<div
class="w-full min-h-12 flex flex-wrap items-center gap-1.5 p-1.5 pb-5 rounded-lg bg-surface-gray-2 cursor-text"
@click.stop="togglePopover"
>
<Tooltip
:text="assignee.name"
v-for="assignee in assignees"
:key="assignee.name"
@click.stop
>
<div
class="flex items-center text-sm p-0.5 text-ink-gray-6 border border-outline-gray-1 bg-surface-modal rounded-full cursor-pointer"
@click.stop
>
<UserAvatar :user="assignee.name" size="sm" />
<div class="ml-1">{{ getUser(assignee.name).full_name }}</div>
<Button
variant="ghost"
class="rounded-full !size-4 m-1"
@click.stop="removeValue(assignee.name)"
>
<template #icon>
<FeatherIcon name="x" class="h-3 w-3 text-ink-gray-6" />
</template>
</Button>
</div>
</Tooltip>
</div>
</template>
<template #item-prefix="{ option }">
<UserAvatar class="mr-2" :user="option.value" size="sm" />
</template>
<template #item-label="{ option }">
<Tooltip :text="option.value">
<div class="cursor-pointer text-ink-gray-9">
{{ getUser(option.value).full_name }}
</div>
</Tooltip>
</template>
</Link>
<div class="flex items-center justify-between gap-2">
<div
class="text-base text-ink-gray-5 cursor-pointer select-none"
@click="assignToMe = !assignToMe"
>
{{ __('Assign to me') }}
</div>
<Switch v-model="assignToMe" @click.stop />
</div>
</div>
</template>
<script setup>
import UserAvatar from '@/components/UserAvatar.vue'
import Link from '@/components/Controls/Link.vue'
import { usersStore } from '@/stores/users'
import { capture } from '@/telemetry'
import { Tooltip, Switch, createResource } from 'frappe-ui'
import { ref, watch } from 'vue'
const props = defineProps({
doctype: {
type: String,
default: '',
},
docname: {
type: Object,
default: null,
},
open: {
type: Boolean,
default: false,
},
onUpdate: {
type: Function,
default: null,
},
})
const emit = defineEmits(['reload'])
const assignees = defineModel()
const oldAssignees = ref([])
const assignToMe = ref(false)
const error = ref('')
const { users, getUser } = usersStore()
const removeValue = (value) => {
if (value === getUser('').name) {
assignToMe.value = false
}
assignees.value = assignees.value.filter(
(assignee) => assignee.name !== value,
)
}
const addValue = (value) => {
if (value === getUser('').name) {
assignToMe.value = true
}
error.value = ''
let obj = {
name: value,
image: getUser(value).user_image,
label: getUser(value).full_name,
}
if (!assignees.value.find((assignee) => assignee.name === value)) {
assignees.value.push(obj)
}
}
watch(assignToMe, (val) => {
let user = getUser('')
if (val) {
addValue(user.name)
} else {
removeValue(user.name)
}
})
watch(
() => props.open,
(val) => {
if (val) {
oldAssignees.value = [...(assignees.value || [])]
assignToMe.value = assignees.value.some(
(assignee) => assignee.name === getUser('').name,
)
} else {
updateAssignees()
}
},
{ immediate: true },
)
async function updateAssignees() {
if (JSON.stringify(oldAssignees.value) === JSON.stringify(assignees.value))
return
const removedAssignees = oldAssignees.value
.filter(
(assignee) => !assignees.value.find((a) => a.name === assignee.name),
)
.map((assignee) => assignee.name)
const addedAssignees = assignees.value
.filter(
(assignee) => !oldAssignees.value.find((a) => a.name === assignee.name),
)
.map((assignee) => assignee.name)
if (props.onUpdate) {
props.onUpdate(
addedAssignees,
removedAssignees,
addAssignees,
removeAssignees,
)
} else {
if (removedAssignees.length) {
await removeAssignees.submit(removedAssignees)
}
if (addedAssignees.length) {
addAssignees.submit(addedAssignees)
}
}
}
const addAssignees = createResource({
url: 'frappe.desk.form.assign_to.add',
makeParams: (addedAssignees) => ({
doctype: props.doctype,
name: props.docname,
assign_to: addedAssignees,
}),
onSuccess: () => {
capture('assign_to', { doctype: props.doctype })
},
})
const removeAssignees = createResource({
url: 'crm.api.doc.remove_assignments',
makeParams: (removedAssignees) => ({
doctype: props.doctype,
name: props.docname,
assignees: removedAssignees,
}),
})
</script>

View File

@ -5,9 +5,11 @@
:label="label"
theme="gray"
variant="outline"
:iconLeft="getIcon()"
@click="toggleDialog()"
>
<template #prefix>
<component :is="getIcon()" class="h-4 w-4" />
</template>
<template #suffix>
<slot name="suffix" />
</template>

View File

@ -13,7 +13,7 @@
</div>
</div>
<div>
<div class="text-ink-gray-5 text-base">
<div class="text-ink-gray-5">
{{
__('Are you sure you want to delete {0} items?', [
props.items?.length,
@ -53,7 +53,7 @@
</div>
</div>
<div>
<div class="text-ink-gray-5 text-base">
<div class="text-ink-gray-5">
{{
confirmDeleteInfo.delete
? __(

View File

@ -1,7 +1,7 @@
<template>
<Popover placement="bottom-end">
<template #target="{ togglePopover }">
<Button :label="__('Columns')" @click="togglePopover">
<NestedPopover>
<template #target>
<Button :label="__('Columns')">
<template v-if="hideLabel">
<ColumnsIcon class="h-4" />
</template>
@ -65,28 +65,37 @@
<Button
class="w-full !justify-start !text-ink-gray-5"
variant="ghost"
@click="togglePopover()"
:label="__('Add Column')"
iconLeft="plus"
@click="togglePopover"
/>
>
<template #prefix>
<FeatherIcon name="plus" class="h-4" />
</template>
</Button>
</template>
</Autocomplete>
<Button
v-if="columnsUpdated"
class="w-full !justify-start !text-ink-gray-5"
variant="ghost"
:label="__('Reset Changes')"
:iconLeft="ReloadIcon"
@click="reset(close)"
/>
:label="__('Reset Changes')"
>
<template #prefix>
<ReloadIcon class="h-4" />
</template>
</Button>
<Button
v-if="!is_default"
class="w-full !justify-start !text-ink-gray-5"
variant="ghost"
:label="__('Reset to Default')"
:iconLeft="ReloadIcon"
@click="resetToDefault(close)"
/>
:label="__('Reset to Default')"
>
<template #prefix>
<ReloadIcon class="h-4" />
</template>
</Button>
</div>
</div>
<div v-else>
@ -135,7 +144,7 @@
</div>
</div>
</template>
</Popover>
</NestedPopover>
</template>
<script setup>
@ -143,9 +152,9 @@ import ColumnsIcon from '@/components/Icons/ColumnsIcon.vue'
import EditIcon from '@/components/Icons/EditIcon.vue'
import DragIcon from '@/components/Icons/DragIcon.vue'
import ReloadIcon from '@/components/Icons/ReloadIcon.vue'
import NestedPopover from '@/components/NestedPopover.vue'
import Autocomplete from '@/components/frappe-ui/Autocomplete.vue'
import { isTouchScreenDevice } from '@/utils'
import { Popover } from 'frappe-ui'
import Draggable from 'vuedraggable'
import { computed, ref } from 'vue'
import { watchOnce } from '@vueuse/core'
@ -210,7 +219,6 @@ const fields = computed(() => {
})
function addColumn(c) {
if (!c) return
let align = ['Float', 'Int', 'Percent', 'Currency'].includes(c.type)
? 'right'
: 'left'

View File

@ -45,12 +45,11 @@
v-slot="{ togglePopover }"
@update:modelValue="() => appendEmoji()"
>
<Button
:tooltip="__('Insert Emoji')"
:icon="SmileIcon"
variant="ghost"
@click="togglePopover()"
/>
<Button variant="ghost" @click="togglePopover()">
<template #icon>
<SmileIcon class="h-4" />
</template>
</Button>
</IconPicker>
<FileUploader
:upload-args="{
@ -62,11 +61,14 @@
>
<template #default="{ openFileSelector }">
<Button
:tooltip="__('Attach a file')"
theme="gray"
variant="ghost"
:icon="AttachmentIcon"
@click="openFileSelector()"
/>
>
<template #icon>
<AttachmentIcon class="h-4" />
</template>
</Button>
</template>
</FileUploader>
</div>

View File

@ -8,18 +8,24 @@
showEmailBox ? '!bg-surface-gray-4 hover:!bg-surface-gray-3' : '',
]"
:label="__('Reply')"
:iconLeft="Email2Icon"
@click="toggleEmailBox()"
/>
>
<template #prefix>
<Email2Icon class="h-4" />
</template>
</Button>
<Button
variant="ghost"
:label="__('Comment')"
:class="[
showCommentBox ? '!bg-surface-gray-4 hover:!bg-surface-gray-3' : '',
]"
:iconLeft="CommentIcon"
@click="toggleCommentBox()"
/>
>
<template #prefix>
<CommentIcon class="h-4" />
</template>
</Button>
</div>
</div>
<div

View File

@ -1,454 +0,0 @@
<template>
<div
class="flex gap-2"
:class="[
{
'items-center': !props.isGroup,
},
]"
>
<div
class="flex gap-2 w-full"
:class="[
{
'items-center justify-between': !props.isGroup,
},
]"
>
<div :class="'text-end text-base text-gray-600'">
<div v-if="props.itemIndex == 0" class="min-w-[66px] text-start">
{{ __('Where') }}
</div>
<div v-else class="min-w-[66px] flex items-start">
<Button
variant="subtle"
class="w-max"
@click="toggleConjunction"
icon-right="refresh-cw"
:disabled="props.itemIndex > 2"
:label="conjunction"
/>
</div>
</div>
<div v-if="!props.isGroup" class="flex items-center gap-2 w-full">
<div id="fieldname" class="w-full">
<Autocomplete
:options="filterableFields.data"
v-model="props.condition[0]"
:placeholder="__('Field')"
@update:modelValue="updateField"
/>
</div>
<div id="operator">
<FormControl
v-if="!props.condition[0]"
disabled
type="text"
:placeholder="__('operator')"
class="w-[100px]"
/>
<FormControl
v-else
:disabled="!props.condition[0]"
type="select"
v-model="props.condition[1]"
@change="updateOperator"
:options="getOperators()"
class="w-max min-w-[100px]"
/>
</div>
<div id="value" class="w-full">
<FormControl
v-if="!props.condition[0]"
disabled
type="text"
:placeholder="__('condition')"
class="w-full"
/>
<component
v-else
:is="getValueControl()"
v-model="props.condition[2]"
@change="updateValue"
:placeholder="__('condition')"
/>
</div>
</div>
<CFConditions
v-if="props.isGroup && !(props.level == 2 || props.level == 4)"
:conditions="props.condition"
:isChild="true"
:level="props.level"
:disableAddCondition="props.disableAddCondition"
/>
<Button
variant="outline"
v-if="props.isGroup && (props.level == 2 || props.level == 4)"
@click="show = true"
:label="__('Open nested conditions')"
/>
</div>
<div :class="'w-max'">
<Dropdown placement="right" :options="dropdownOptions">
<Button variant="ghost" icon="more-horizontal" />
</Dropdown>
</div>
</div>
<Dialog
v-model="show"
:options="{ size: '3xl', title: __('Nested conditions') }"
>
<template #body-content>
<CFConditions
:conditions="props.condition"
:isChild="true"
:level="props.level"
:disableAddCondition="props.disableAddCondition"
/>
</template>
</Dialog>
</template>
<script setup>
import {
Autocomplete,
Button,
DatePicker,
DateRangePicker,
DateTimePicker,
Dialog,
Dropdown,
FormControl,
Rating,
} from 'frappe-ui'
import { computed, defineEmits, h, ref } from 'vue'
import GroupIcon from '~icons/lucide/group'
import UnGroupIcon from '~icons/lucide/ungroup'
import CFConditions from './CFConditions.vue'
import { filterableFields } from './filterableFields'
import Link from '@/components/Controls/Link.vue'
const show = ref(false)
const emit = defineEmits([
'remove',
'unGroupConditions',
'toggleConjunction',
'turnIntoGroup',
])
const props = defineProps({
condition: {
type: Array,
required: true,
},
isChild: {
type: Boolean,
default: false,
},
itemIndex: {
type: Number,
},
level: {
type: Number,
default: 0,
},
isGroup: {
type: Boolean,
default: false,
},
conjunction: {
type: String,
},
disableAddCondition: {
type: Boolean,
default: false,
},
})
const dropdownOptions = computed(() => {
const options = []
if (!props.isGroup && props.level < 4) {
options.push({
label: __('Turn into a group'),
icon: () => h(GroupIcon),
onClick: () => {
emit('turnIntoGroup')
},
})
}
if (props.isGroup) {
options.push({
label: __('Ungroup conditions'),
icon: () => h(UnGroupIcon),
onClick: () => {
emit('unGroupConditions')
},
})
}
options.push({
label: __('Remove'),
icon: 'trash-2',
variant: 'red',
onClick: () => emit('remove'),
condition: () => !props.isGroup,
})
options.push({
label: __('Remove group'),
icon: 'trash-2',
variant: 'red',
onClick: () => emit('remove'),
condition: () => props.isGroup,
})
return options
})
const typeCheck = ['Check']
const typeLink = ['Link', 'Dynamic Link']
const typeNumber = ['Float', 'Int', 'Currency', 'Percent']
const typeSelect = ['Select']
const typeString = ['Data', 'Long Text', 'Small Text', 'Text Editor', 'Text']
const typeDate = ['Date', 'Datetime']
const typeRating = ['Rating']
function toggleConjunction() {
emit('toggleConjunction', props.conjunction)
}
const updateField = (field) => {
props.condition[0] = field?.fieldname
resetConditionValue()
}
const resetConditionValue = () => {
props.condition[2] = ''
}
function getValueControl() {
const [field, operator] = props.condition
if (!field) return null
const fieldData = filterableFields.data?.find((f) => f.fieldname == field)
if (!fieldData) return null
const { fieldtype, options } = fieldData
if (operator == 'is') {
return h(FormControl, {
type: 'select',
options: [
{
label: 'Set',
value: 'set',
},
{
label: 'Not Set',
value: 'not set',
},
],
})
} else if (['like', 'not like', 'in', 'not in'].includes(operator)) {
return h(FormControl, { type: 'text' })
} else if (typeSelect.includes(fieldtype) || typeCheck.includes(fieldtype)) {
const _options =
fieldtype == 'Check' ? ['Yes', 'No'] : getSelectOptions(options)
return h(FormControl, {
type: 'select',
options: _options.map((o) => ({
label: o,
value: o,
})),
})
} else if (typeLink.includes(fieldtype)) {
if (fieldtype == 'Dynamic Link') {
return h(FormControl, { type: 'text' })
}
return h(Link, {
class: 'form-control',
doctype: options,
value: props.condition[2],
})
} else if (typeNumber.includes(fieldtype)) {
return h(FormControl, { type: 'number' })
} else if (typeDate.includes(fieldtype) && operator == 'between') {
return h(DateRangePicker, { value: props.condition[2], iconLeft: '' })
} else if (typeDate.includes(fieldtype)) {
return h(fieldtype == 'Date' ? DatePicker : DateTimePicker, {
value: props.condition[2],
iconLeft: '',
})
} else if (typeRating.includes(fieldtype)) {
return h(Rating, {
modelValue: props.condition[2] || 0,
class: 'truncate',
'update:modelValue': (v) => updateValue(v),
})
} else {
return h(FormControl, { type: 'text' })
}
}
function updateValue(value) {
value = value.target ? value.target.value : value
if (props.condition[1] === 'between') {
props.condition[2] = [value.split(',')[0], value.split(',')[1]]
} else {
props.condition[2] = value + ''
}
}
function getSelectOptions(options) {
return options.split('\n')
}
function updateOperator(event) {
let oldOperatorValue = event.target._value
let newOperatorValue = event.target.value
props.condition[1] = event.target.value
if (!isSameTypeOperator(oldOperatorValue, newOperatorValue)) {
props.condition[2] = getDefaultValue(props.condition[0])
}
resetConditionValue()
}
function getOperators() {
let options = []
const field = props.condition[0]
if (!field) return options
const fieldData = filterableFields.data?.find((f) => f.fieldname == field)
if (!fieldData) return options
const { fieldtype, fieldname } = fieldData
if (typeString.includes(fieldtype)) {
options.push(
...[
{ label: 'Equals', value: '==' },
{ label: 'Not Equals', value: '!=' },
{ label: 'Like', value: 'like' },
{ label: 'Not Like', value: 'not like' },
{ label: 'In', value: 'in' },
{ label: 'Not In', value: 'not in' },
{ label: 'Is', value: 'is' },
],
)
}
if (fieldname === '_assign') {
options = [
{ label: 'Like', value: 'like' },
{ label: 'Not Like', value: 'not like' },
{ label: 'Is', value: 'is' },
]
}
if (typeNumber.includes(fieldtype)) {
options.push(
...[
{ label: 'Equals', value: '==' },
{ label: 'Not Equals', value: '!=' },
{ label: 'Like', value: 'like' },
{ label: 'Not Like', value: 'not like' },
{ label: 'In', value: 'in' },
{ label: 'Not In', value: 'not in' },
{ label: 'Is', value: 'is' },
{ label: '<', value: '<' },
{ label: '>', value: '>' },
{ label: '<=', value: '<=' },
{ label: '>=', value: '>=' },
],
)
}
if (typeSelect.includes(fieldtype)) {
options.push(
...[
{ label: 'Equals', value: '==' },
{ label: 'Not Equals', value: '!=' },
{ label: 'In', value: 'in' },
{ label: 'Not In', value: 'not in' },
{ label: 'Is', value: 'is' },
],
)
}
if (typeLink.includes(fieldtype)) {
options.push(
...[
{ label: 'Equals', value: '==' },
{ label: 'Not Equals', value: '!=' },
{ label: 'Like', value: 'like' },
{ label: 'Not Like', value: 'not like' },
{ label: 'In', value: 'in' },
{ label: 'Not In', value: 'not in' },
{ label: 'Is', value: 'is' },
],
)
}
if (typeCheck.includes(fieldtype)) {
options.push(...[{ label: 'Equals', value: '==' }])
}
if (['Duration'].includes(fieldtype)) {
options.push(
...[
{ label: 'Like', value: 'like' },
{ label: 'Not Like', value: 'not like' },
{ label: 'In', value: 'in' },
{ label: 'Not In', value: 'not in' },
{ label: 'Is', value: 'is' },
],
)
}
if (typeDate.includes(fieldtype)) {
options.push(
...[
{ label: 'Equals', value: '==' },
{ label: 'Not Equals', value: '!=' },
{ label: 'Is', value: 'is' },
{ label: '>', value: '>' },
{ label: '<', value: '<' },
{ label: '>=', value: '>=' },
{ label: '<=', value: '<=' },
{ label: 'Between', value: 'between' },
],
)
}
if (typeRating.includes(fieldtype)) {
options.push(
...[
{ label: 'Equals', value: '==' },
{ label: 'Not Equals', value: '!=' },
{ label: 'Is', value: 'is' },
{ label: '>', value: '>' },
{ label: '<', value: '<' },
{ label: '>=', value: '>=' },
{ label: '<=', value: '<=' },
],
)
}
const op = options.find((o) => o.value == props.condition[1])
props.condition[1] = op?.value || options[0].value
return options
}
function getDefaultValue(field) {
if (typeSelect.includes(field.fieldtype)) {
return getSelectOptions(field.options)[0]
}
if (typeCheck.includes(field.fieldtype)) {
return 'Yes'
}
if (typeDate.includes(field.fieldtype)) {
return null
}
if (typeRating.includes(field.fieldtype)) {
return 0
}
return ''
}
function isSameTypeOperator(oldOperator, newOperator) {
let textOperators = ['==', '!=', 'in', 'not in', '>', '<', '>=', '<=']
if (
textOperators.includes(oldOperator) &&
textOperators.includes(newOperator)
)
return true
return false
}
</script>

View File

@ -1,142 +0,0 @@
<template>
<div class="rounded-lg border border-outline-gray-2 p-3 flex flex-col gap-4 w-full">
<template v-for="(condition, i) in props.conditions" :key="condition.field">
<CFCondition
v-if="Array.isArray(condition)"
:condition="condition"
:isChild="props.isChild"
:itemIndex="i"
@remove="removeCondition(condition)"
@unGroupConditions="unGroupConditions(condition)"
:level="props.level + 1"
@toggleConjunction="toggleConjunction"
:isGroup="isGroupCondition(condition[0])"
:conjunction="getConjunction()"
@turnIntoGroup="turnIntoGroup(condition)"
:disableAddCondition="props.disableAddCondition"
/>
</template>
<div v-if="props.isChild" class="flex">
<Dropdown v-slot="{ open }" :options="dropdownOptions">
<Button
:disabled="props.disableAddCondition"
:label="__('Add condition')"
icon-left="plus"
:icon-right="open ? 'chevron-up' : 'chevron-down'"
/>
</Dropdown>
</div>
</div>
</template>
<script setup>
import { Button, Dropdown } from 'frappe-ui'
import { computed, watch } from 'vue'
import CFCondition from './CFCondition.vue'
import { filterableFields } from './filterableFields'
const props = defineProps({
conditions: {
type: Array,
required: true,
},
isChild: {
type: Boolean,
default: false,
},
level: {
type: Number,
default: 0,
},
disableAddCondition: {
type: Boolean,
default: false,
},
doctype: {
type: String,
required: true,
},
})
const getConjunction = () => {
let conjunction = 'and'
props.conditions.forEach((condition) => {
if (typeof condition == 'string') {
conjunction = condition
}
})
return conjunction
}
const turnIntoGroup = (condition) => {
props.conditions.splice(props.conditions.indexOf(condition), 1, [condition])
}
const isGroupCondition = (condition) => {
return Array.isArray(condition)
}
const dropdownOptions = computed(() => {
const options = [
{
label: __('Add condition'),
onClick: () => {
const conjunction = getConjunction()
props.conditions.push(conjunction, ['', '', ''])
},
},
]
if (props.level < 3) {
options.push({
label: __('Add condition group'),
onClick: () => {
const conjunction = getConjunction()
props.conditions.push(conjunction, [[]])
},
})
}
return options
})
function removeCondition(condition) {
const conditionIndex = props.conditions.indexOf(condition)
if (conditionIndex == 0) {
props.conditions.splice(conditionIndex, 2)
} else {
props.conditions.splice(conditionIndex - 1, 2)
}
}
function unGroupConditions(condition) {
const conjunction = getConjunction()
const newConditions = condition.map((c) => {
if (typeof c == 'string') {
return conjunction
}
return c
})
const index = props.conditions.indexOf(condition)
if (index !== -1) {
props.conditions.splice(index, 1, ...newConditions)
}
}
function toggleConjunction(conjunction) {
for (let i = 0; i < props.conditions.length; i++) {
if (typeof props.conditions[i] == 'string') {
props.conditions[i] = conjunction == 'and' ? 'or' : 'and'
}
}
}
watch(
() => props.doctype,
(doctype) => {
filterableFields.submit({
doctype,
})
},
{ immediate: true },
)
</script>

View File

@ -1,17 +0,0 @@
import { createResource } from 'frappe-ui'
export const filterableFields = createResource({
url: 'crm.api.doc.get_filterable_fields',
transform: (data) => {
data = data
.filter((field) => !field.fieldname.startsWith('_'))
.map((field) => {
return {
label: field.label,
value: field.fieldname,
...field,
}
})
return data
},
})

View File

@ -52,14 +52,16 @@
>
</div>
</div>
<div class="flex items-center justify-center w-12">
<div class="w-12">
<Button
:tooltip="__('Edit grid fields')"
class="rounded !bg-surface-gray-2 border-0 !text-ink-gray-5"
class="flex w-full items-center justify-center rounded !bg-surface-gray-2 border-0"
variant="outline"
icon="settings"
@click="showGridFieldsEditorModal = true"
/>
>
<template #icon>
<FeatherIcon name="settings" class="size-4 text-ink-gray-7" />
</template>
</Button>
</div>
</div>
<!-- Rows -->
@ -70,7 +72,6 @@
:delay="isTouchScreenDevice() ? 200 : 0"
group="rows"
item-key="name"
@end="reorder"
>
<template #item="{ element: row, index }">
<div
@ -276,14 +277,16 @@
/>
</div>
</div>
<div class="edit-row flex items-center justify-center w-12">
<div class="edit-row w-12">
<Button
:tooltip="__('Edit row')"
class="rounded border-0 !text-ink-gray-7"
class="flex w-full items-center justify-center rounded border-0"
variant="outline"
:icon="EditIcon"
@click="showRowList[index] = true"
/>
>
<template #icon>
<EditIcon class="text-ink-gray-7" />
</template>
</Button>
</div>
<GridRowModal
v-if="showRowList[index]"
@ -347,6 +350,7 @@ import { usersStore } from '@/stores/users'
import { getMeta } from '@/stores/meta'
import { createDocument } from '@/composables/document'
import {
FeatherIcon,
FormControl,
Checkbox,
DateTimePicker,
@ -516,13 +520,6 @@ const deleteRows = () => {
selectedRows.clear()
}
const reorder = () => {
rows.value.forEach((row, index) => {
row.idx = index + 1
})
}
function fieldChange(value, field, row) {
triggerOnChange(field.fieldname, value, row)
}

View File

@ -54,10 +54,13 @@
<template #target="{ togglePopover }">
<Button
class="w-full mt-2"
:label="__('Add Field')"
iconLeft="plus"
@click="togglePopover()"
/>
:label="__('Add Field')"
>
<template #prefix>
<FeatherIcon name="plus" class="h-4" />
</template>
</Button>
</template>
<template #item-label="{ option }">
<div class="flex flex-col gap-1 text-ink-gray-9">
@ -72,7 +75,7 @@
</div>
</template>
<template #actions>
<div class="flex items-center gap-2 justify-end">
<div class="flex flex-col gap-2">
<Button
v-if="dirty"
class="w-full"

View File

@ -11,18 +11,19 @@
<div class="flex items-center gap-1">
<Button
v-if="isManager()"
:tooltip="__('Edit fields layout')"
variant="ghost"
class="w-7"
:icon="EditIcon"
@click="openGridRowFieldsModal"
/>
<Button
icon="x"
variant="ghost"
class="w-7"
@click="show = false"
/>
>
<template #icon>
<EditIcon />
</template>
</Button>
<Button variant="ghost" class="w-7" @click="show = false">
<template #icon>
<FeatherIcon name="x" class="size-4" />
</template>
</Button>
</div>
</div>
<div>

View File

@ -1,6 +1,7 @@
<template>
<FileUploader
:file-types="image_type"
class="text-base"
@success="
(file) => {
$emit('upload', file.file_url)
@ -9,28 +10,21 @@
>
<template v-slot="{ progress, uploading, openFileSelector }">
<div class="flex items-end space-x-1">
<Button
@click="openFileSelector"
:iconLeft="uploading ? 'cloud-upload' : ImageUpIcon"
:label="
<Button @click="openFileSelector">
{{
uploading
? __('Uploading {0}%', [progress])
? `Uploading ${progress}%`
: image_url
? __('Change')
: __('Upload')
"
/>
<Button
v-if="image_url"
:label="__('Remove')"
@click="$emit('remove')"
/>
? 'Change'
: 'Upload'
}}
</Button>
<Button v-if="image_url" @click="$emit('remove')">Remove</Button>
</div>
</template>
</FileUploader>
</template>
<script setup>
import ImageUpIcon from '~icons/lucide/image-up'
import { FileUploader, Button } from 'frappe-ui'
const prop = defineProps({
@ -39,6 +33,10 @@ const prop = defineProps({
type: String,
default: 'image/*',
},
label: {
type: String,
default: '',
},
})
const emit = defineEmits(['upload', 'remove'])
</script>

View File

@ -48,18 +48,24 @@
variant="ghost"
class="w-full !justify-start"
:label="__('Create New')"
iconLeft="plus"
@click="() => attrs.onCreate(value, close)"
/>
>
<template #prefix>
<FeatherIcon name="plus" class="h-4" />
</template>
</Button>
</div>
<div>
<Button
variant="ghost"
class="w-full !justify-start"
:label="__('Clear')"
iconLeft="x"
@click="() => clearValue(close)"
/>
>
<template #prefix>
<FeatherIcon name="x" class="h-4" />
</template>
</Button>
</div>
</template>
</Autocomplete>

View File

@ -18,10 +18,14 @@
:key="g.label"
>
<Dropdown :options="g.action" v-slot="{ open }">
<Button
:label="g.label"
:iconRight="open ? 'chevron-up' : 'chevron-down'"
<Button :label="g.label">
<template #suffix>
<FeatherIcon
:name="open ? 'chevron-up' : 'chevron-down'"
class="h-4"
/>
</template>
</Button>
</Dropdown>
</div>
</template>

View File

@ -2,7 +2,7 @@
<Dialog v-model="show" :options="{ size: 'xl' }">
<template #body v-if="!confirmDeleteInfo.show">
<div class="bg-surface-modal px-4 pb-6 pt-5 sm:px-6">
<div class="mb-6 flex items-center justify-between">
<div class="mb-4 flex items-center justify-between">
<div>
<h3 class="text-2xl leading-6 text-ink-gray-9 font-semibold">
{{
@ -32,12 +32,11 @@
{
label: 'Document',
key: 'title',
width: '19rem',
},
{
label: 'Master',
key: 'reference_doctype',
width: '12rem',
width: '30%',
},
]"
@selectionsChanged="

View File

@ -19,36 +19,53 @@
v-if="editMode"
variant="ghost"
:label="__('Save')"
size="sm"
class="opacity-0 hover:bg-surface-gray-4 group-hover:opacity-100"
@click="saveOption"
/>
<Tooltip text="Set As Primary" v-if="!isNew && !option.selected">
<div>
<Button
v-if="!isNew && !option.selected"
:tooltip="__('Set As Primary')"
variant="ghost"
:icon="SuccessIcon"
size="sm"
class="opacity-0 hover:bg-surface-gray-4 group-hover:opacity-100"
@click="option.onClick"
/>
>
<template #icon>
<SuccessIcon />
</template>
</Button>
</div>
</Tooltip>
<Tooltip v-if="!editMode" text="Edit">
<div>
<Button
v-if="!editMode"
:tooltip="__('Edit')"
variant="ghost"
:icon="EditIcon"
size="sm"
class="opacity-0 hover:bg-surface-gray-4 group-hover:opacity-100"
@click="toggleEditMode"
/>
>
<template #icon>
<EditIcon />
</template>
</Button>
</div>
</Tooltip>
<Tooltip text="Delete">
<div>
<Button
:tooltip="__('Delete')"
variant="ghost"
icon="x"
size="sm"
class="opacity-0 hover:bg-surface-gray-4 group-hover:opacity-100"
@click="() => option.onDelete(option, isNew)"
/>
</div>
</Tooltip>
</div>
</div>
<div v-if="option.selected">
<FeatherIcon name="check" class="text-ink-gray-5 h-4 w-6" />
<FeatherIcon name="check" class="text-ink-gray-5 h-4 w-6" size="sm" />
</div>
</div>
</template>
@ -56,7 +73,7 @@
<script setup>
import SuccessIcon from '@/components/Icons/SuccessIcon.vue'
import EditIcon from '@/components/Icons/EditIcon.vue'
import { TextInput } from 'frappe-ui'
import { TextInput, Tooltip } from 'frappe-ui'
import { nextTick, ref, onMounted } from 'vue'
const props = defineProps({

View File

@ -123,12 +123,11 @@
v-slot="{ togglePopover }"
@update:modelValue="() => appendEmoji()"
>
<Button
:tooltip="__('Insert Emoji')"
:icon="SmileIcon"
variant="ghost"
@click="togglePopover()"
/>
<Button variant="ghost" @click="togglePopover()">
<template #icon>
<SmileIcon class="h-4" />
</template>
</Button>
</IconPicker>
<FileUploader
:upload-args="{
@ -139,20 +138,21 @@
@success="(f) => attachments.push(f)"
>
<template #default="{ openFileSelector }">
<Button
:tooltip="__('Attach a file')"
:icon="AttachmentIcon"
variant="ghost"
@click="openFileSelector()"
/>
<Button variant="ghost" @click="openFileSelector()">
<template #icon>
<AttachmentIcon class="h-4" />
</template>
</Button>
</template>
</FileUploader>
<Button
:tooltip="__('Insert Email Template')"
variant="ghost"
:icon="EmailTemplateIcon"
@click="showEmailTemplateSelectorModal = true"
/>
>
<template #icon>
<EmailTemplateIcon class="h-4" />
</template>
</Button>
</div>
<div class="mt-2 flex items-center justify-end space-x-2 sm:mt-0">
<Button v-bind="discardButtonProps || {}" :label="__('Discard')" />

View File

@ -89,9 +89,12 @@
v-if="data[field.fieldname] && field.edit"
class="shrink-0"
:label="__('Edit')"
:iconLeft="EditIcon"
@click="field.edit(data[field.fieldname])"
/>
>
<template #prefix>
<EditIcon class="h-4 w-4" />
</template>
</Button>
</div>
<TableMultiselectInput

View File

@ -169,10 +169,13 @@
<Button
class="w-full !h-8 !bg-surface-modal"
variant="outline"
:label="__('Add Field')"
iconLeft="plus"
@click="togglePopover()"
/>
:label="__('Add Field')"
>
<template #prefix>
<FeatherIcon name="plus" class="h-4" />
</template>
</Button>
</div>
</template>
<template #item-label="{ option }">
@ -195,7 +198,6 @@
class="w-full h-8"
variant="subtle"
:label="__('Add Section')"
iconLeft="plus"
@click="
tabs[tabIndex].sections.push({
label: __('New Section'),
@ -204,7 +206,11 @@
columns: [{ name: 'column_' + getRandom(), fields: [] }],
})
"
/>
>
<template #prefix>
<FeatherIcon name="plus" class="h-4" />
</template>
</Button>
</div>
</div>
</div>

View File

@ -29,7 +29,6 @@
filesUploaderArea?.showWebLink || filesUploaderArea?.showCamera
"
:label="isMobileView ? __('Back') : __('Back to file upload')"
iconLeft="arrow-left"
@click="
() => {
filesUploaderArea.showWebLink = false
@ -38,7 +37,11 @@
filesUploaderArea.cameraImage = null
}
"
/>
>
<template #prefix>
<FeatherIcon name="arrow-left" class="size-4" />
</template>
</Button>
<Button
v-if="
filesUploaderArea?.showCamera && !filesUploaderArea?.cameraImage

View File

@ -1,13 +1,12 @@
<template>
<Popover placement="bottom-end">
<template #target="{ togglePopover, close }">
<NestedPopover>
<template #target>
<div class="flex items-center">
<Button
:label="__('Filter')"
:class="filters?.size ? 'rounded-r-none' : ''"
:iconLeft="FilterIcon"
@click="togglePopover"
>
<template #prefix><FilterIcon class="h-4" /></template>
<template v-if="filters?.size" #suffix>
<div
class="flex h-5 w-5 items-center justify-center rounded-[5px] bg-surface-white pt-px text-xs font-medium text-ink-gray-8 shadow-sm"
@ -16,14 +15,16 @@
</div>
</template>
</Button>
<Tooltip v-if="filters?.size" :text="__('Clear all Filter')">
<div>
<Button
v-if="filters?.size"
:tooltip="__('Clear all Filter')"
class="rounded-l-none border-l"
icon="x"
@click.stop="clearfilter(close)"
@click.stop="clearfilter(false)"
/>
</div>
</Tooltip>
</div>
</template>
<template #body="{ close }">
<div
@ -133,10 +134,13 @@
<Button
class="!text-ink-gray-5"
variant="ghost"
:label="__('Add Filter')"
iconLeft="plus"
@click="togglePopover()"
/>
:label="__('Add Filter')"
>
<template #prefix>
<FeatherIcon name="plus" class="h-4" />
</template>
</Button>
</template>
</Autocomplete>
<Button
@ -150,16 +154,17 @@
</div>
</div>
</template>
</Popover>
</NestedPopover>
</template>
<script setup>
import NestedPopover from '@/components/NestedPopover.vue'
import FilterIcon from '@/components/Icons/FilterIcon.vue'
import Link from '@/components/Controls/Link.vue'
import Autocomplete from '@/components/frappe-ui/Autocomplete.vue'
import {
FormControl,
createResource,
Popover,
Tooltip,
DatePicker,
DateTimePicker,
DateRangePicker,
@ -480,7 +485,7 @@ function removeFilter(index) {
function clearfilter(close) {
filters.value.clear()
apply()
close()
close && close()
}
function updateValue(value, filter) {

View File

@ -7,11 +7,19 @@
? groupByValue?.label
: __('Group By: ') + groupByValue?.label
"
:iconLeft="DetailsIcon"
:iconRight="isOpen ? 'chevron-up' : 'chevron-down'"
@click="togglePopover()"
>
<template #prefix>
<DetailsIcon />
</template>
<template #suffix>
<FeatherIcon
:name="isOpen ? 'chevron-up' : 'chevron-down'"
class="h-4"
/>
</template>
</Button>
</template>
</Autocomplete>
</template>
<script setup>

View File

@ -69,7 +69,7 @@
</Popover>
</template>
<script setup>
import { Popover } from 'frappe-ui'
import Popover from '@/components/frappe-ui/Popover.vue'
import { gemoji } from 'gemoji'
import { ref, computed } from 'vue'

View File

@ -1,19 +0,0 @@
<template>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-settings2-icon lucide-settings-2"
>
<path d="M14 17H5" />
<path d="M19 7h-9" />
<circle cx="17" cy="17" r="3" />
<circle cx="7" cy="7" r="3" />
</svg>
</template>

View File

@ -1,16 +0,0 @@
<template>
<svg
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M3.37543 1.93494L4.21632 2.21494C4.35232 2.26027 4.44388 2.38738 4.44388 2.53138C4.44388 2.67538 4.35143 2.80249 4.21543 2.84783L3.37454 3.12783L3.09365 3.9696C3.04921 4.10472 2.92121 4.19716 2.7781 4.19716C2.63499 4.19716 2.50787 4.1056 2.46254 3.9696L2.18165 3.12783L1.34076 2.84783C1.20476 2.80249 1.11232 2.67538 1.11232 2.53138C1.11232 2.38738 1.20476 2.26027 1.34076 2.21494L2.18165 1.93494L2.46254 1.09316C2.55321 0.82116 3.00387 0.82116 3.09454 1.09316L3.37543 1.93494ZM8.44852 1.33394C8.3643 1.16325 8.19046 1.05518 8.00012 1.05518C7.80978 1.05518 7.63595 1.16325 7.55173 1.33394L5.67697 5.13368L1.48388 5.74214C1.29552 5.76947 1.13901 5.90137 1.08017 6.08238C1.02133 6.26339 1.07036 6.46211 1.20665 6.59497L4.24065 9.55281L3.52421 13.7284C3.49203 13.916 3.56913 14.1056 3.7231 14.2174C3.87706 14.3293 4.08119 14.3441 4.24966 14.2555L8.11188 12.2253C8.35631 12.0968 8.4503 11.7945 8.32181 11.5501C8.19333 11.3057 7.89102 11.2117 7.64659 11.3402L4.68114 12.899L5.2707 9.46284C5.29853 9.30065 5.24477 9.13514 5.12693 9.02027L2.63025 6.58626L6.08082 6.08555C6.24373 6.06191 6.38457 5.95959 6.45741 5.81196L8.00012 2.6852L9.54284 5.81196C9.61568 5.95959 9.75652 6.06191 9.91943 6.08555L13.37 6.58625L11.6235 8.2887C11.4258 8.48146 11.4218 8.79802 11.6145 8.99575C11.8073 9.19349 12.1239 9.19752 12.3216 9.00476L14.7936 6.59498C14.9299 6.46212 14.9789 6.2634 14.9201 6.08239C14.8612 5.90138 14.7047 5.76947 14.5164 5.74214L10.3233 5.13368L8.44852 1.33394ZM13.4744 11.9911L12.3517 11.6168L11.9775 10.4942C11.8557 10.1315 11.2557 10.1315 11.1339 10.4942L10.7597 11.6168L9.63702 11.9911C9.45569 12.0515 9.33302 12.2213 9.33302 12.4124C9.33302 12.6035 9.45569 12.7733 9.63702 12.8337L10.7597 13.2079L11.1339 14.3306C11.1944 14.5119 11.365 14.6346 11.5561 14.6346C11.7472 14.6346 11.917 14.5119 11.9784 14.3306L12.3526 13.2079L13.4752 12.8337C13.6566 12.7733 13.7792 12.6035 13.7792 12.4124C13.7792 12.2213 13.6566 12.0515 13.4752 11.9911H13.4744ZM13.3333 2.88883C13.3333 3.25702 13.0349 3.5555 12.6667 3.5555C12.2985 3.5555 12 3.25702 12 2.88883C12 2.52064 12.2985 2.22217 12.6667 2.22217C13.0349 2.22217 13.3333 2.52064 13.3333 2.88883Z"
fill="currentColor"
/>
</svg>
</template>

View File

@ -3,8 +3,11 @@
:label="__('Kanban Settings')"
@click="showDialog = true"
v-bind="$attrs"
:iconLeft="KanbanIcon"
/>
>
<template #prefix>
<KanbanIcon class="h-4" />
</template>
</Button>
<Dialog v-model="showDialog" :options="{ title: __('Kanban Settings') }">
<template #body-content>
<div>
@ -20,8 +23,8 @@
<template #target="{ togglePopover }">
<Button
class="w-full !justify-start"
:label="columnField.label"
@click="togglePopover()"
:label="columnField.label"
/>
</template>
</Autocomplete>
@ -77,10 +80,13 @@
<template #target="{ togglePopover }">
<Button
class="w-full mt-2"
:label="__('Add Field')"
iconLeft="plus"
@click="togglePopover()"
/>
:label="__('Add Field')"
>
<template #prefix>
<FeatherIcon name="plus" class="h-4" />
</template>
</Button>
</template>
<template #item-label="{ option }">
<div class="flex flex-col gap-1 text-ink-gray-9">

View File

@ -15,18 +15,17 @@
>
<div class="flex gap-2 items-center group justify-between">
<div class="flex items-center text-base">
<Popover>
<template #target="{ togglePopover }">
<NestedPopover>
<template #target>
<Button
variant="ghost"
size="sm"
class="hover:!bg-surface-gray-2"
@click="togglePopover"
>
<IndicatorIcon :class="parseColor(column.column.color)" />
</Button>
</template>
<template #body>
<template #body="{ close }">
<div
class="flex flex-col gap-3 px-3 py-2.5 min-w-40 rounded-lg bg-surface-modal shadow-2xl ring-1 ring-black ring-opacity-5 focus:outline-none"
>
@ -49,7 +48,7 @@
</div>
</div>
</template>
</Popover>
</NestedPopover>
<div class="text-ink-gray-9">{{ column.column.name }}</div>
</div>
<div class="flex">
@ -154,10 +153,13 @@
<template #target="{ togglePopover }">
<Button
class="w-full mt-2.5 mb-1 mr-5"
:label="__('Add Column')"
iconLeft="plus"
@click="togglePopover()"
/>
:label="__('Add Column')"
>
<template #prefix>
<FeatherIcon name="plus" class="h-4" />
</template>
</Button>
</template>
</Autocomplete>
</div>
@ -165,10 +167,11 @@
</template>
<script setup>
import Autocomplete from '@/components/frappe-ui/Autocomplete.vue'
import NestedPopover from '@/components/NestedPopover.vue'
import IndicatorIcon from '@/components/Icons/IndicatorIcon.vue'
import { isTouchScreenDevice, colors, parseColor } from '@/utils'
import Draggable from 'vuedraggable'
import { Dropdown, Popover } from 'frappe-ui'
import { Dropdown } from 'frappe-ui'
import { computed } from 'vue'
const props = defineProps({

View File

@ -3,8 +3,8 @@
class="relative flex h-full flex-col justify-between transition-all duration-300 ease-in-out"
:class="isSidebarCollapsed ? 'w-12' : 'w-[220px]'"
>
<div class="p-2">
<UserDropdown :isCollapsed="isSidebarCollapsed" />
<div>
<UserDropdown class="p-2" :isCollapsed="isSidebarCollapsed" />
</div>
<div class="flex-1 overflow-y-auto">
<div class="mb-3 flex flex-col">
@ -197,11 +197,13 @@ const isSidebarCollapsed = useStorage('isSidebarCollapsed', false)
const isFCSite = ref(window.is_fc_site)
const isDemoSite = ref(window.is_demo_site)
const links = [
const allViews = computed(() => {
const links = [
{
label: 'Dashboard',
icon: LucideLayoutDashboard,
to: 'Dashboard',
condition: () => isManager(),
},
{
label: 'Leads',
@ -238,9 +240,8 @@ const links = [
icon: PhoneIcon,
to: 'Call Logs',
},
]
]
const allViews = computed(() => {
let _views = [
{
name: 'All Views',

View File

@ -106,8 +106,6 @@ function convertToDeal(selections, unselectAll) {
}
function deleteValues(selections, unselectAll) {
unselectAllAction.value = unselectAll
const selectedDocs = Array.from(selections)
if (selectedDocs.length == 1) {
showDeleteDocModal.value = {
@ -219,12 +217,6 @@ function bulkActions(selections, unselectAll) {
}
function reload(unselectAll) {
showDeleteDocModal.value = {
showLinkedDocsModal: false,
showDeleteModal: false,
docname: null,
}
unselectAllAction.value?.()
unselectAll?.()
list.value?.reload()

View File

@ -26,14 +26,13 @@
<ListRowItem
:item="item"
@click="listViewRef.toggleRow(row['reference_docname'])"
class="!w-full"
>
<template #default="{ label }">
<div
v-if="column.key === 'title'"
class="truncate text-base flex gap-2 w-full"
class="truncate text-base flex gap-2"
>
<span class="max-w-[90%] truncate">
<span>
{{ label }}
</span>
<FeatherIcon
@ -103,7 +102,6 @@ const listViewRef = ref(null)
const viewLinkedDoc = (doc) => {
let page = ''
let id = ''
let openDesk = false
switch (doc.reference_doctype) {
case 'CRM Lead':
page = 'leads'
@ -125,11 +123,6 @@ const viewLinkedDoc = (doc) => {
page = 'organizations'
id = doc.reference_docname
break
case 'CRM Notification':
page = 'crm-notification'
id = doc.reference_docname
openDesk = true
break
case 'FCRM Note':
page = 'notes'
id = `view?open=${doc.reference_docname}`
@ -137,11 +130,7 @@ const viewLinkedDoc = (doc) => {
default:
break
}
let base = '/crm'
if (openDesk) {
base = '/app'
}
window.open(`${base}/${page}/${id}`)
window.open(`/crm/${page}/${id}`)
}
const getDoctypeName = (doctype) => {

View File

@ -11,18 +11,19 @@
<div class="flex items-center gap-1">
<Button
v-if="isManager() && !isMobileView"
:tooltip="__('Edit fields layout')"
variant="ghost"
:icon="EditIcon"
class="w-7"
@click="openQuickEntryModal"
/>
<Button
icon="x"
variant="ghost"
class="w-7"
@click="show = false"
/>
>
<template #icon>
<EditIcon />
</template>
</Button>
<Button variant="ghost" class="w-7" @click="show = false">
<template #icon>
<FeatherIcon name="x" class="size-4" />
</template>
</Button>
</div>
</div>
<div v-if="tabs.data && _address.doc">

View File

@ -1,8 +1,30 @@
<template>
<Dialog
v-model="show"
:options="{ title: __('Assign To'), size: 'xl' }"
@close="() => (assignees = [...oldAssignees])"
:options="{
title: __('Assign To'),
size: 'xl',
actions: [
{
label: __('Cancel'),
variant: 'subtle',
onClick: () => {
assignees = [...oldAssignees]
show = false
},
},
{
label: __('Update'),
variant: 'solid',
onClick: () => updateAssignees(),
},
],
}"
@close="
() => {
assignees = [...oldAssignees]
}
"
>
<template #body-content>
<Link
@ -11,43 +33,9 @@
doctype="User"
@change="(option) => addValue(option) && ($refs.input.value = '')"
:placeholder="__('John Doe')"
:filters="{
name: ['in', users.data.crmUsers?.map((user) => user.name)],
}"
:filters="{ name: ['in', users.data.crmUsers?.map((user) => user.name)] }"
:hideMe="true"
>
<template #target="{ togglePopover }">
<div
class="w-full min-h-12 flex flex-wrap items-center gap-1.5 p-1.5 pb-5 rounded-lg bg-surface-gray-2 cursor-text"
@click.stop="togglePopover"
>
<Tooltip
:text="assignee.name"
v-for="assignee in assignees"
:key="assignee.name"
@click.stop
>
<div>
<div
class="flex items-center text-sm text-ink-gray-6 border border-outline-gray-1 bg-surface-white rounded-full hover:bg-surface-white !p-0.5"
@click.stop
>
<UserAvatar :user="assignee.name" size="sm" />
<div class="ml-1">{{ getUser(assignee.name).full_name }}</div>
<Button
variant="ghost"
class="rounded-full !size-4 m-1"
@click.stop="removeValue(assignee.name)"
>
<template #icon>
<FeatherIcon name="x" class="h-3 w-3 text-ink-gray-6" />
</template>
</Button>
</div>
</div>
</Tooltip>
</div>
</template>
<template #item-prefix="{ option }">
<UserAvatar class="mr-2" :user="option.value" size="sm" />
</template>
@ -59,28 +47,30 @@
</Tooltip>
</template>
</Link>
<div class="mt-3 flex flex-wrap items-center gap-2">
<Tooltip
:text="assignee.name"
v-for="assignee in assignees"
:key="assignee.name"
>
<div>
<Button :label="getUser(assignee.name).full_name" theme="gray">
<template #prefix>
<UserAvatar :user="assignee.name" size="sm" />
</template>
<template #actions>
<div class="flex justify-between items-center gap-2">
<div><ErrorMessage :message="__(error)" /></div>
<div class="flex items-center justify-end gap-2">
<Button
variant="subtle"
:label="__('Cancel')"
@click="
() => {
assignees = [...oldAssignees]
show = false
}
"
/>
<Button
variant="solid"
:label="__('Update')"
@click="updateAssignees()"
<template #suffix>
<FeatherIcon
v-if="assignee.name !== owner"
class="h-3.5"
name="x"
@click.stop="removeValue(assignee.name)"
/>
</template>
</Button>
</div>
</Tooltip>
</div>
<ErrorMessage class="mt-2" v-if="error" :message="__(error)" />
</template>
</Dialog>
</template>
@ -142,7 +132,7 @@ const addValue = (value) => {
}
}
async function updateAssignees() {
function updateAssignees() {
const removedAssignees = oldAssignees.value
.filter(
(assignee) => !assignees.value.find((a) => a.name === assignee.name),
@ -156,7 +146,7 @@ async function updateAssignees() {
.map((assignee) => assignee.name)
if (removedAssignees.length) {
await call('crm.api.doc.remove_assignments', {
call('crm.api.doc.remove_assignments', {
doctype: props.doctype,
name: props.doc.name,
assignees: JSON.stringify(removedAssignees),

View File

@ -36,17 +36,18 @@
<Button
v-if="!isMobileView"
variant="ghost"
:tooltip="__('Edit call log')"
:icon="EditIcon"
class="w-7"
@click="openCallLogModal"
/>
<Button
icon="x"
variant="ghost"
class="w-7"
@click="show = false"
/>
>
<template #icon>
<EditIcon />
</template>
</Button>
<Button variant="ghost" class="w-7" @click="show = false">
<template #icon>
<FeatherIcon name="x" class="size-4" />
</template>
</Button>
</div>
</div>
<div class="flex flex-col gap-3.5">

View File

@ -13,17 +13,18 @@
<Button
v-if="isManager() && !isMobileView"
variant="ghost"
:tooltip="__('Edit fields layout')"
:icon="EditIcon"
class="w-7"
@click="openQuickEntryModal"
/>
<Button
variant="ghost"
class="w-7"
@click="show = false"
icon="x"
/>
>
<template #icon>
<EditIcon />
</template>
</Button>
<Button variant="ghost" class="w-7" @click="show = false">
<template #icon>
<FeatherIcon name="x" class="size-4" />
</template>
</Button>
</div>
</div>
<div v-if="tabs.data">
@ -36,7 +37,7 @@
</div>
</div>
<div class="px-4 pt-4 pb-7 sm:px-6">
<div class="flex justify-end gap-2">
<div class="space-y-2">
<Button
class="w-full"
v-for="action in dialogOptions.actions"
@ -60,7 +61,7 @@ import { showQuickEntryModal, quickEntryProps } from '@/composables/modals'
import { getRandom } from '@/utils'
import { capture } from '@/telemetry'
import { useDocument } from '@/data/document'
import { createResource, ErrorMessage, Badge } from 'frappe-ui'
import { FeatherIcon, createResource, ErrorMessage, Badge } from 'frappe-ui'
import { ref, nextTick, computed, onMounted } from 'vue'
const props = defineProps({

View File

@ -13,16 +13,17 @@
v-if="isManager() && !isMobileView"
variant="ghost"
class="w-7"
:tooltip="__('Edit fields layout')"
:icon="EditIcon"
@click="openQuickEntryModal"
/>
<Button
variant="ghost"
class="w-7"
@click="show = false"
icon="x"
/>
>
<template #icon>
<EditIcon />
</template>
</Button>
<Button variant="ghost" class="w-7" @click="show = false">
<template #icon>
<FeatherIcon name="x" class="size-4" />
</template>
</Button>
</div>
</div>
<FieldLayout
@ -89,16 +90,12 @@ const { document: _contact, triggerOnBeforeCreate } = useDocument('Contact')
async function createContact() {
if (_contact.doc.email_id) {
_contact.doc.email_ids = [
{ email_id: _contact.doc.email_id, is_primary: 1 },
]
_contact.doc.email_ids = [{ email_id: _contact.doc.email_id, is_primary: 1 }]
delete _contact.doc.email_id
}
if (_contact.doc.mobile_no) {
_contact.doc.phone_nos = [
{ phone: _contact.doc.mobile_no, is_primary_mobile_no: 1 },
]
_contact.doc.phone_nos = [{ phone: _contact.doc.mobile_no, is_primary_mobile_no: 1 }]
delete _contact.doc.mobile_no
}

View File

@ -1,5 +1,17 @@
<template>
<Dialog v-model="show" :options="{ size: 'xl' }">
<Dialog
v-model="show"
:options="{
size: 'xl',
actions: [
{
label: __('Convert'),
variant: 'solid',
onClick: convertToDeal,
},
],
}"
>
<template #body-header>
<div class="mb-6 flex items-center justify-between">
<div>
@ -11,10 +23,12 @@
<Button
v-if="isManager() && !isMobileView"
variant="ghost"
:tooltip="__('Edit deal\'s mandatory fields layout')"
:icon="EditIcon"
@click="openQuickEntryModal"
/>
>
<template #icon>
<EditIcon class="h-4 w-4" />
</template>
</Button>
<Button icon="x" variant="ghost" @click="show = false" />
</div>
</div>
@ -78,11 +92,6 @@
/>
<ErrorMessage class="mt-4" :message="error" />
</template>
<template #actions>
<div class="flex justify-end">
<Button :label="__('Convert')" variant="solid" @click="convertToDeal" />
</div>
</template>
</Dialog>
</template>
<script setup>

View File

@ -13,16 +13,17 @@
v-if="isManager() && !isMobileView"
variant="ghost"
class="w-7"
:tooltip="__('Edit fields layout')"
:icon="EditIcon"
@click="openQuickEntryModal"
/>
<Button
variant="ghost"
class="w-7"
icon="x"
@click="show = false"
/>
>
<template #icon>
<EditIcon />
</template>
</Button>
<Button variant="ghost" class="w-7" @click="show = false">
<template #icon>
<FeatherIcon name="x" class="size-4" />
</template>
</Button>
</div>
</div>
<div v-if="tabs.data">

View File

@ -13,16 +13,17 @@
v-if="isManager() && !isMobileView"
variant="ghost"
class="w-7"
:tooltip="__('Edit fields layout')"
:icon="EditIcon"
@click="openQuickEntryModal"
/>
<Button
variant="ghost"
class="w-7"
icon="x"
@click="show = false"
/>
>
<template #icon>
<EditIcon />
</template>
</Button>
<Button variant="ghost" class="w-7" @click="show = false">
<template #icon>
<FeatherIcon name="x" class="size-4" />
</template>
</Button>
</div>
</div>
<div>

View File

@ -13,16 +13,17 @@
v-if="isManager() && !isMobileView"
variant="ghost"
class="w-7"
:tooltip="__('Edit fields layout')"
:icon="EditIcon"
@click="openQuickEntryModal"
/>
<Button
variant="ghost"
class="w-7"
@click="show = false"
icon="x"
/>
>
<template #icon>
<EditIcon />
</template>
</Button>
<Button variant="ghost" class="w-7" @click="show = false">
<template #icon>
<FeatherIcon name="x" class="size-4" />
</template>
</Button>
</div>
</div>
<div>

View File

@ -1,60 +1,45 @@
<template>
<Dialog v-model="show" :options="{ size: 'xl' }">
<Dialog v-model="show" :options="{
size: 'xl',
actions: [
{
label: editMode ? __('Update') : __('Create'),
variant: 'solid',
onClick: () => updateNote(),
},
],
}">
<template #body-title>
<div class="flex items-center gap-3">
<h3 class="text-2xl font-semibold leading-6 text-ink-gray-9">
{{ editMode ? __('Edit Note') : __('Create Note') }}
</h3>
<Button
v-if="_note?.reference_docname"
size="sm"
:label="
_note.reference_doctype == 'CRM Deal'
<Button v-if="_note?.reference_docname" size="sm" :label="_note.reference_doctype == 'CRM Deal'
? __('Open Deal')
: __('Open Lead')
"
:iconRight="ArrowUpRightIcon"
@click="redirect()"
/>
" @click="redirect()">
<template #suffix>
<ArrowUpRightIcon class="w-4 h-4" />
</template>
</Button>
</div>
</template>
<template #body-content>
<div class="flex flex-col gap-4">
<div>
<FormControl
ref="title"
:label="__('Title')"
v-model="_note.title"
:placeholder="__('Call with John Doe')"
required
/>
<FormControl ref="title" :label="__('Title')" v-model="_note.title" :placeholder="__('Call with John Doe')"
required />
</div>
<div>
<div class="mb-1.5 text-xs text-ink-gray-5">{{ __('Content') }}</div>
<TextEditor
variant="outline"
ref="content"
<TextEditor variant="outline" ref="content"
editor-class="!prose-sm overflow-auto min-h-[180px] max-h-80 py-1.5 px-2 rounded border border-[--surface-gray-2] bg-surface-gray-2 placeholder-ink-gray-4 hover:border-outline-gray-modals hover:bg-surface-gray-3 hover:shadow-sm focus:bg-surface-white focus:border-outline-gray-4 focus:shadow-sm focus:ring-0 focus-visible:ring-2 focus-visible:ring-outline-gray-3 text-ink-gray-8 transition-colors"
:bubbleMenu="true"
:content="_note.content"
@change="(val) => (_note.content = val)"
:placeholder="
__('Took a call with John Doe and discussed the new project.')
"
/>
:bubbleMenu="true" :content="_note.content" @change="(val) => (_note.content = val)" :placeholder="__('Took a call with John Doe and discussed the new project.')
" />
</div>
<ErrorMessage class="mt-4" v-if="error" :message="__(error)" />
</div>
</template>
<template #actions>
<div class="flex justify-end">
<Button
:label="editMode ? __('Update') : __('Create')"
variant="solid"
@click="updateNote"
/>
</div>
</template>
</Dialog>
</template>
@ -107,9 +92,7 @@ async function updateNote() {
emit('after', d)
}
} else {
let d = await call(
'frappe.client.insert',
{
let d = await call('frappe.client.insert', {
doc: {
doctype: 'FCRM Note',
title: _note.value.title,
@ -117,15 +100,13 @@ async function updateNote() {
reference_doctype: props.doctype,
reference_docname: props.doc || '',
},
},
{
}, {
onError: (err) => {
if (err.error.exc_type == 'MandatoryError') {
error.value = 'Title is mandatory'
error.value = "Title is mandatory"
}
},
},
)
}
})
if (d.name) {
updateOnboardingStep('create_first_note')
capture('note_created')

Some files were not shown because too many files have changed in this diff Show More