|
86417
|
2962
|
21
|
2026-05-28T14:08:42.721163+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-28/1779 /Users/lukas/.screenpipe/data/data/2026-05-28/1779977322721_m1.jpg...
|
iTerm2
|
NULL
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Activity MonitorFileEditViewWindowHelpActivity Mon Activity MonitorFileEditViewWindowHelpActivity MonitorAll ProcessesProcess Name% CPUkernel_taskVirtual Machine Service for DockerreplaydNotion HelperWindowServerPhpStormscreenpipeNotionmdsNotion Helper (Renderer)FirefoxCP Isolated Web Contentio.kandji.KandjiAgent.ESF-Extensionmdworker_sharedlaunchservicesdbackupdfseventsdiTerm2launchdcoreaudiodFirefoxClaudedeletedmdworker_sharedmdworker_sharedSlack HelperActivity Monitormdworker_sharedSlack Helper (Renderer)176,569,161,455,052,649,937,524,022,520,67,96,66,65,95,95,75,35,35,25,24,74,64,54,44,44,34,34,1$1CPU Time22:50:14,231:53:41,975:42:53,053:21,397:50:50,892:35:15,793:35:17,859:39,9328:56,4424:09,9533:47,2631:22,991,081:00:09,0355,687:23,351:03:33,2918:36,8757:05,931:46:50,0528:23,811:26,461,310,6414:28,756:50,421,0057:28,12System:Threads LL22CPUMemoryEnergyDiskNetworkIdle Wake-UpsKind % GPUAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleApple0,00,00,00,056,81,90,00,00,00,00,00,00,00,00,00,00,50,00,00,00,00,00,00,05,80,00,00,054.64%CPU LOADThreads:GPU Time0,000,000,000,003:20:18,477:48.975:03,900,000,000,000,000,000,000,000,000,0019,480,000,000,040,000,000,000,006:07,820,000,000,00ahlPID Userrootlukaslukaslukas_windowserverlukaslukaslukasrootlukaslukasrootlukasrootrootrootlukasroot_coreaudiodlukaslukaslukaslukaslukaslukaslukaslukas13506lukasPhpStorm5 240PS100% ['Thu 28 May 17:08:42Search...
|
NULL
|
2772711335548233732
|
NULL
|
visual_change
|
ocr
|
NULL
|
Activity MonitorFileEditViewWindowHelpActivity Mon Activity MonitorFileEditViewWindowHelpActivity MonitorAll ProcessesProcess Name% CPUkernel_taskVirtual Machine Service for DockerreplaydNotion HelperWindowServerPhpStormscreenpipeNotionmdsNotion Helper (Renderer)FirefoxCP Isolated Web Contentio.kandji.KandjiAgent.ESF-Extensionmdworker_sharedlaunchservicesdbackupdfseventsdiTerm2launchdcoreaudiodFirefoxClaudedeletedmdworker_sharedmdworker_sharedSlack HelperActivity Monitormdworker_sharedSlack Helper (Renderer)176,569,161,455,052,649,937,524,022,520,67,96,66,65,95,95,75,35,35,25,24,74,64,54,44,44,34,34,1$1CPU Time22:50:14,231:53:41,975:42:53,053:21,397:50:50,892:35:15,793:35:17,859:39,9328:56,4424:09,9533:47,2631:22,991,081:00:09,0355,687:23,351:03:33,2918:36,8757:05,931:46:50,0528:23,811:26,461,310,6414:28,756:50,421,0057:28,12System:Threads LL22CPUMemoryEnergyDiskNetworkIdle Wake-UpsKind % GPUAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleApple0,00,00,00,056,81,90,00,00,00,00,00,00,00,00,00,00,50,00,00,00,00,00,00,05,80,00,00,054.64%CPU LOADThreads:GPU Time0,000,000,000,003:20:18,477:48.975:03,900,000,000,000,000,000,000,000,000,0019,480,000,000,040,000,000,000,006:07,820,000,000,00ahlPID Userrootlukaslukaslukas_windowserverlukaslukaslukasrootlukaslukasrootlukasrootrootrootlukasroot_coreaudiodlukaslukaslukaslukaslukaslukaslukaslukas13506lukasPhpStorm5 240PS100% ['Thu 28 May 17:08:42Search...
|
86415
|
NULL
|
NULL
|
NULL
|
|
86416
|
2963
|
21
|
2026-05-28T14:08:41.348667+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-28/1779 /Users/lukas/.screenpipe/data/data/2026-05-28/1779977321348_m2.jpg...
|
iTerm2
|
NULL
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
rircrox•.••<ColltVieVhotonooouukiFoiLc.WVindowh rircrox•.••<ColltVieVhotonooouukiFoiLc.WVindowhiel:= github.com/jiminny/app/pull/12121(17 OpenJY-20963 fix deleted object import #12121LakyLak wants to merge 7 commits into master from JY-28963-fix-imoort-on-deleted-entityPipelines - jiminny/appBE upgrade libraries(JY-20613) Allow owner's role to bText relayDeleted object erroryyorokFix orcion key wiolntluminate/Database|Query&xceplJY-20983 fix deleted objecti xlooin t SiastorcaFeed - Jiminny - SentryM Inbox (1.734) - Itas.kowalik Slim'(JY-20979) Resolve PHP 8.5.5 deg8 Jiminaypihttoem Son dthloy - Pintormtranscript ss issue8 Jiminny7(59D-68811/00 demandl Transd8 Sona Subramacian at 27/05/20268 Ilivana Netsova at 27/05/2026, 18)8 Jimieny+ New Tab• Merge branch 'master' into JY-20963-fix-import-on-deleted-entitya @Jy-20963 code snella @ JY-28963 add code coveragea @Jy-20963 add tests coveragesonarqudecioua Bot commenico 2o minutes agol~ Quality Gate passedissues0 New issuesE 0 Accepted issuesweasuresyU sccurly noscorv 76.1% Coverage on New Code0.0% Duplication on New CodeSee analysis details on SonarQube CloudThis branch has not been deployedNo deploymentsRoviow rentiirerAtlaset 1 anmtouina tuimu ie tanutitad m tovimuore with writa sccaceAll checks have passed12 successful checksvO build_accept_deploy Successfulin 10m - Workflow: buiid_accept_deploy0 ci/circleci: build-backend — Your tests passed on CircleCl→0 cilcireleci- build-frontend = Yourtocte naceed on Cireloctlv O ci/circleci: checkout-code - Your tests passed on CircleCI!/ 0 cilcircleci: phostan — Your tests passed on Circleclv ci/circleci: setup - Your tests passed on CircleC!l0 cilcircleci: sonar cloudSOMOA100%, K%3 8 • Thu 28 May 17:08:40Veriricd X c9184961X 6bcSc12x 4le8cбbVebbaabs...
|
NULL
|
1330825001018677623
|
NULL
|
click
|
ocr
|
NULL
|
rircrox•.••<ColltVieVhotonooouukiFoiLc.WVindowh rircrox•.••<ColltVieVhotonooouukiFoiLc.WVindowhiel:= github.com/jiminny/app/pull/12121(17 OpenJY-20963 fix deleted object import #12121LakyLak wants to merge 7 commits into master from JY-28963-fix-imoort-on-deleted-entityPipelines - jiminny/appBE upgrade libraries(JY-20613) Allow owner's role to bText relayDeleted object erroryyorokFix orcion key wiolntluminate/Database|Query&xceplJY-20983 fix deleted objecti xlooin t SiastorcaFeed - Jiminny - SentryM Inbox (1.734) - Itas.kowalik Slim'(JY-20979) Resolve PHP 8.5.5 deg8 Jiminaypihttoem Son dthloy - Pintormtranscript ss issue8 Jiminny7(59D-68811/00 demandl Transd8 Sona Subramacian at 27/05/20268 Ilivana Netsova at 27/05/2026, 18)8 Jimieny+ New Tab• Merge branch 'master' into JY-20963-fix-import-on-deleted-entitya @Jy-20963 code snella @ JY-28963 add code coveragea @Jy-20963 add tests coveragesonarqudecioua Bot commenico 2o minutes agol~ Quality Gate passedissues0 New issuesE 0 Accepted issuesweasuresyU sccurly noscorv 76.1% Coverage on New Code0.0% Duplication on New CodeSee analysis details on SonarQube CloudThis branch has not been deployedNo deploymentsRoviow rentiirerAtlaset 1 anmtouina tuimu ie tanutitad m tovimuore with writa sccaceAll checks have passed12 successful checksvO build_accept_deploy Successfulin 10m - Workflow: buiid_accept_deploy0 ci/circleci: build-backend — Your tests passed on CircleCl→0 cilcireleci- build-frontend = Yourtocte naceed on Cireloctlv O ci/circleci: checkout-code - Your tests passed on CircleCI!/ 0 cilcircleci: phostan — Your tests passed on Circleclv ci/circleci: setup - Your tests passed on CircleC!l0 cilcircleci: sonar cloudSOMOA100%, K%3 8 • Thu 28 May 17:08:40Veriricd X c9184961X 6bcSc12x 4le8cбbVebbaabs...
|
86414
|
NULL
|
NULL
|
NULL
|
|
86415
|
2962
|
20
|
2026-05-28T14:08:38.364223+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-28/1779 /Users/lukas/.screenpipe/data/data/2026-05-28/1779977318364_m1.jpg...
|
iTerm2
|
NULL
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Activity MonitorFileEditViewWindowHelpActivity Mon Activity MonitorFileEditViewWindowHelpActivity MonitorAll ProcessesProcess Name% CPUkernel_taskVirtual Machine Service for DockerNotion HelperWindowServerreplaydmdsPhpStormNotion Helper (Renderer)Notionscreenpipemdworker_sharedmdworker_sharedActivity MonitorFirefoxCP Isolated Web Contentmds_storesmdworker_sharedmdworker_sharedio.kandji.KandjiAgent.ESF-Extensionfseventsdmdworker_sharedierm2Firefoxbackupdlaunchservicesdcoreaudiodmdworker_sharedmdworker_sharedRescueTime157,888,848,542,538,635,832,023,623,419,08,57,97,67,26,96,76,76,56,46,26,05,75,35,35,04,84,84,5CPU Time22:50:04,831:53:38,293:18,467:50:48,095:42:49,7828:55,242:35:13,1324:08,859:38,653:35:15,851,071,066:50,1933:46,841:03:39,210,920,6431:22,637:23,050,501:03:33,001:46:49,7855,361:00:08,7257:05,660,490,737:30,07System:User:Idle:Threads lahl100% <Thu 28 May 17:08:37CPUMemoryEnergyDiskNetworkIdle Wake-UpsKind % GPUAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleIntel43.79%48,76%7,45%0,00,00,049,20,00,01,80,00,00,00,00,00,00,00,00,00,00,00,00,00,50,00,00,00,00,00,00,0CPU LOADThreads:Processes:SearchGPU Time0,000,000,003:20:17,110,000,007:48,970,000,005:03,900,000,000,000,000,000,000,000,000,000,0019,470,040,000,000,000,000,000,00PID Userrootlukaslukas_windowserverlukasrootlukaslukaslukaslukaslukaslukaslukaslukasrootlukaslukasrootrootlukaslukaslukasrootroot_coreaudiodlukaslukaslukas...
|
NULL
|
8808591335737608869
|
NULL
|
visual_change
|
ocr
|
NULL
|
Activity MonitorFileEditViewWindowHelpActivity Mon Activity MonitorFileEditViewWindowHelpActivity MonitorAll ProcessesProcess Name% CPUkernel_taskVirtual Machine Service for DockerNotion HelperWindowServerreplaydmdsPhpStormNotion Helper (Renderer)Notionscreenpipemdworker_sharedmdworker_sharedActivity MonitorFirefoxCP Isolated Web Contentmds_storesmdworker_sharedmdworker_sharedio.kandji.KandjiAgent.ESF-Extensionfseventsdmdworker_sharedierm2Firefoxbackupdlaunchservicesdcoreaudiodmdworker_sharedmdworker_sharedRescueTime157,888,848,542,538,635,832,023,623,419,08,57,97,67,26,96,76,76,56,46,26,05,75,35,35,04,84,84,5CPU Time22:50:04,831:53:38,293:18,467:50:48,095:42:49,7828:55,242:35:13,1324:08,859:38,653:35:15,851,071,066:50,1933:46,841:03:39,210,920,6431:22,637:23,050,501:03:33,001:46:49,7855,361:00:08,7257:05,660,490,737:30,07System:User:Idle:Threads lahl100% <Thu 28 May 17:08:37CPUMemoryEnergyDiskNetworkIdle Wake-UpsKind % GPUAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleIntel43.79%48,76%7,45%0,00,00,049,20,00,01,80,00,00,00,00,00,00,00,00,00,00,00,00,00,50,00,00,00,00,00,00,0CPU LOADThreads:Processes:SearchGPU Time0,000,000,003:20:17,110,000,007:48,970,000,005:03,900,000,000,000,000,000,000,000,000,000,0019,470,040,000,000,000,000,000,00PID Userrootlukaslukas_windowserverlukasrootlukaslukaslukaslukaslukaslukaslukaslukasrootlukaslukasrootrootlukaslukaslukasrootroot_coreaudiodlukaslukaslukas...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
86414
|
2963
|
20
|
2026-05-28T14:08:35.406214+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-28/1779 /Users/lukas/.screenpipe/data/data/2026-05-28/1779977315406_m2.jpg...
|
iTerm2
|
NULL
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
rircrox•.••<ColltVieVhotonooouukiFoiLc.WVindowh rircrox•.••<ColltVieVhotonooouukiFoiLc.WVindowhiel:= github.com/jiminny/app/pull/12121(17 OpenJY-20963 fix deleted object import #12121LakyLak wants to merge 7 commits into master from JY-28963-fix-imoort-on-deleted-entityPipelines - jiminny/appBE upgrade libraries(JY-20613) Allow owner's role to bText relayDeleted object erroryyorokFix orcion key wiolntluminate/Database|Query&xceplJY-20983 fix deleted objecti xlooin t SiastorcaFeed - Jiminny - SentryM Inbox (1.734) - Itas.kowalik Slim'(JY-20979) Resolve PHP 8.5.5 deg8 Jiminaypihttoem Son dthloy - Pintormtranscript ss issue8 Jiminny7(59D-68811/00 demandl Transd8 Sona Subramacian at 27/05/20268 Ilivana Netsova at 27/05/2026, 18)8 Jimieny+ New Tab• Merge branch 'master' into JY-20963-fix-import-on-deleted-entitya @Jy-20963 code snella @ JY-28963 add code coveragea @Jy-20963 add tests coveragesonarqudecioua Bot commenico 2o minutes agol~ Quality Gate passedissues0 New issuesE 0 Accepted issuesweasuresyU sccurly noscorv 76.1% Coverage on New Code0.0% Duplication on New CodeSee analysis details on SonarQube CloudThis branch has not been deployedNo deploymentsRoviow rentiirerAtlaset 1 anmtouina tuimu ie tanutitad m tovimuore with writa sccaceAll checks have passed12 successful checksvO build_accept_deploy Successfulin 10m - Workflow: buiid_accept_deploy0 ci/circleci: build-backend — Your tests passed on CircleCl→0 cilcireleci- build-frontend = Yourtocte naceed on Cireloctlv O ci/circleci: checkout-code - Your tests passed on CircleCI!/ 0 cilcircleci: phostan — Your tests passed on Circleclv ci/circleci: setup - Your tests passed on CircleC!l0 cilcircleci: sonar cloudSOMOA100%, K%3 8 • Thu 28 May 17:08:35Veriricd X c9184961X 6bcSc12x 4le8cбbVebbaabs...
|
NULL
|
6213104983432534117
|
NULL
|
visual_change
|
ocr
|
NULL
|
rircrox•.••<ColltVieVhotonooouukiFoiLc.WVindowh rircrox•.••<ColltVieVhotonooouukiFoiLc.WVindowhiel:= github.com/jiminny/app/pull/12121(17 OpenJY-20963 fix deleted object import #12121LakyLak wants to merge 7 commits into master from JY-28963-fix-imoort-on-deleted-entityPipelines - jiminny/appBE upgrade libraries(JY-20613) Allow owner's role to bText relayDeleted object erroryyorokFix orcion key wiolntluminate/Database|Query&xceplJY-20983 fix deleted objecti xlooin t SiastorcaFeed - Jiminny - SentryM Inbox (1.734) - Itas.kowalik Slim'(JY-20979) Resolve PHP 8.5.5 deg8 Jiminaypihttoem Son dthloy - Pintormtranscript ss issue8 Jiminny7(59D-68811/00 demandl Transd8 Sona Subramacian at 27/05/20268 Ilivana Netsova at 27/05/2026, 18)8 Jimieny+ New Tab• Merge branch 'master' into JY-20963-fix-import-on-deleted-entitya @Jy-20963 code snella @ JY-28963 add code coveragea @Jy-20963 add tests coveragesonarqudecioua Bot commenico 2o minutes agol~ Quality Gate passedissues0 New issuesE 0 Accepted issuesweasuresyU sccurly noscorv 76.1% Coverage on New Code0.0% Duplication on New CodeSee analysis details on SonarQube CloudThis branch has not been deployedNo deploymentsRoviow rentiirerAtlaset 1 anmtouina tuimu ie tanutitad m tovimuore with writa sccaceAll checks have passed12 successful checksvO build_accept_deploy Successfulin 10m - Workflow: buiid_accept_deploy0 ci/circleci: build-backend — Your tests passed on CircleCl→0 cilcireleci- build-frontend = Yourtocte naceed on Cireloctlv O ci/circleci: checkout-code - Your tests passed on CircleCI!/ 0 cilcircleci: phostan — Your tests passed on Circleclv ci/circleci: setup - Your tests passed on CircleC!l0 cilcircleci: sonar cloudSOMOA100%, K%3 8 • Thu 28 May 17:08:35Veriricd X c9184961X 6bcSc12x 4le8cбbVebbaabs...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
86413
|
2962
|
19
|
2026-05-28T14:08:35.047713+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-28/1779 /Users/lukas/.screenpipe/data/data/2026-05-28/1779977315047_m1.jpg...
|
iTerm2
|
NULL
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Activity MonitorFileEditViewWindowHelpActivity Mon Activity MonitorFileEditViewWindowHelpActivity MonitorAll ProcessesProcess Name% CPUkernel_taskPhpStormVirtual Machine Service for DockerreplaydNotion HelperWindowServerNotion Helper (Renderer)screenpipeNotionmdsFirefoxFirefoxCP Isolated Web ContentFirefox GPU Helpermdworker_sharedFirefoxCP Isolated Web Contentcoreaudiodio.kandji.KandjiAgent.ESF-ExtensionbackupdActivity Monitormdworker_sharedSlack HelperiTerm2fseventsdmdworker_sharedmdworker_sharedmds_storesmdworker_sharedSlack Helper (Renderer)180,099,074,855,142,638,838,126,022,219,110,27,66,76,56,45,85,85,54,94,84,54,44,24,24,13,93,63,3CPU Time22:49:56,472:35:11,441:53:33,585:42:47,743:15,897:50:45,8324:07,603:35:14,849:37,4128:53,341:46:49,4833:46,452:23:40,030,484:50,8557:05,3931:22,2955,086:49,790,3514:28,301:03:32,687:22,710,720,641:03:38,840,3757:27,73System:User:Idle:Threads alel100% <2*8• Thu 28 May 17:08:34CPUMemoryEnergyDiskNetworkIdle Wake-UpsKind % GPUAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleApple54,15%41,70%4,15%0,05,40,00,00,040,50,00,00,00,00,00,01,20,00,00,00,00,00,00,04,40,40,00,00,00,00,00,0CPU LOADThreads:Processes:QSearchGPU Time0,007:48,930,000,000,003:20:15,370,005:03,900,000,000,040,0023:52,340,000,000,000,000,000,000,006:07.5919,460,000,000,000,000,000,00PID Userrootlukaslukaslukaslukas_windowserverlukaslukaslukasrootlukaslukaslukaslukaslukas_coreaudiodrootrootlukaslukaslukaslukasrootlukaslukasrootlukaslukas...
|
NULL
|
1843669194669094776
|
NULL
|
visual_change
|
ocr
|
NULL
|
Activity MonitorFileEditViewWindowHelpActivity Mon Activity MonitorFileEditViewWindowHelpActivity MonitorAll ProcessesProcess Name% CPUkernel_taskPhpStormVirtual Machine Service for DockerreplaydNotion HelperWindowServerNotion Helper (Renderer)screenpipeNotionmdsFirefoxFirefoxCP Isolated Web ContentFirefox GPU Helpermdworker_sharedFirefoxCP Isolated Web Contentcoreaudiodio.kandji.KandjiAgent.ESF-ExtensionbackupdActivity Monitormdworker_sharedSlack HelperiTerm2fseventsdmdworker_sharedmdworker_sharedmds_storesmdworker_sharedSlack Helper (Renderer)180,099,074,855,142,638,838,126,022,219,110,27,66,76,56,45,85,85,54,94,84,54,44,24,24,13,93,63,3CPU Time22:49:56,472:35:11,441:53:33,585:42:47,743:15,897:50:45,8324:07,603:35:14,849:37,4128:53,341:46:49,4833:46,452:23:40,030,484:50,8557:05,3931:22,2955,086:49,790,3514:28,301:03:32,687:22,710,720,641:03:38,840,3757:27,73System:User:Idle:Threads alel100% <2*8• Thu 28 May 17:08:34CPUMemoryEnergyDiskNetworkIdle Wake-UpsKind % GPUAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleApple54,15%41,70%4,15%0,05,40,00,00,040,50,00,00,00,00,00,01,20,00,00,00,00,00,00,04,40,40,00,00,00,00,00,0CPU LOADThreads:Processes:QSearchGPU Time0,007:48,930,000,000,003:20:15,370,005:03,900,000,000,040,0023:52,340,000,000,000,000,000,000,006:07.5919,460,000,000,000,000,000,00PID Userrootlukaslukaslukaslukas_windowserverlukaslukaslukasrootlukaslukaslukaslukaslukas_coreaudiodrootrootlukaslukaslukaslukasrootlukaslukasrootlukaslukas...
|
86408
|
NULL
|
NULL
|
NULL
|
|
86412
|
2963
|
19
|
2026-05-28T14:08:32.375361+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-28/1779 /Users/lukas/.screenpipe/data/data/2026-05-28/1779977312375_m2.jpg...
|
iTerm2
|
NULL
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
rirerox..••+EditViewristonooorineksProfilesToolsWi rirerox..••+EditViewristonooorineksProfilesToolsWindow Helt= github.com/inny/app/pull/12121(i7 OpenJY-20963 fix deleted object import #12121LakyLak wants to merge 7 commits into master from JY-28963-fix-imoort-on-deleted-entityPipelines - jiminny/appBE uporade libraries(JY-20613) Allow owner's role to bText relayDeleted object erroryyorokFix orcion key wiolntluminate/Database|Query&xceplJY-20983 fix deleted objecti xtooin t ShlastorcnFeed - Jiminny - SentryM Inbox (1.734) - Iias.kowalik Slim'(JY-20979) Resolve PHP 8.5.5 deg8 Jiminaypihttoem Son dthloy - Pintormtranscript ss issue8 Jiminny7(59D-68811/00 demandl Transd8 Sona Subramacian at 27/05/20268 Ilivana Netsova at 27/05/2026, 18)8 Jimieny+ New Tab• Merge branch 'master' into JY-20963-fix-import-on-deleted-entitya @Jy-20963 code snella @ JY-20963 add code coveragea @Jy-20963 add tests coveragesonarqudecioua Bot commenico 2o minutes agol~ Quality Gate passedissues0 New issuesE 0 Accepted issuesweasuresyU sccurly noscorv 76.1% Coverage on New Code0.0% Duplication on New CodeSee analysis details on SonarQube CloudThis branch has not been deployedNo deploymentsRoviow regtiirenAtlaset 1 anmtouina tuimu ie tanutitad m tovimuore with writa sccaceAll checks have passed12 successful checksvO build_accept_deploy Successfulin 10m - Workflow: buiid_accept_deploy0 ci/circleci: build-backend — Your tests passed on CircleCl→0 cilcireleci- build-frontend = Yourtocte naceed on Cireloctlv O ci/circleci: checkout-code - Your tests passed on CircleCI!/ 0 cilcircleci: phostan — Your tests passed on Circleclv ci/circleci: setup - Your tests passed on CircleC!l0 cilcircleci: sonar cloudOASOMОД* 100% K/a: 8- Thu 28 May 17:08:32Veriricd X c9184961X 6bcSc12x 4le8cбbVebbaabs...
|
NULL
|
-4929895292166943721
|
NULL
|
visual_change
|
ocr
|
NULL
|
rirerox..••+EditViewristonooorineksProfilesToolsWi rirerox..••+EditViewristonooorineksProfilesToolsWindow Helt= github.com/inny/app/pull/12121(i7 OpenJY-20963 fix deleted object import #12121LakyLak wants to merge 7 commits into master from JY-28963-fix-imoort-on-deleted-entityPipelines - jiminny/appBE uporade libraries(JY-20613) Allow owner's role to bText relayDeleted object erroryyorokFix orcion key wiolntluminate/Database|Query&xceplJY-20983 fix deleted objecti xtooin t ShlastorcnFeed - Jiminny - SentryM Inbox (1.734) - Iias.kowalik Slim'(JY-20979) Resolve PHP 8.5.5 deg8 Jiminaypihttoem Son dthloy - Pintormtranscript ss issue8 Jiminny7(59D-68811/00 demandl Transd8 Sona Subramacian at 27/05/20268 Ilivana Netsova at 27/05/2026, 18)8 Jimieny+ New Tab• Merge branch 'master' into JY-20963-fix-import-on-deleted-entitya @Jy-20963 code snella @ JY-20963 add code coveragea @Jy-20963 add tests coveragesonarqudecioua Bot commenico 2o minutes agol~ Quality Gate passedissues0 New issuesE 0 Accepted issuesweasuresyU sccurly noscorv 76.1% Coverage on New Code0.0% Duplication on New CodeSee analysis details on SonarQube CloudThis branch has not been deployedNo deploymentsRoviow regtiirenAtlaset 1 anmtouina tuimu ie tanutitad m tovimuore with writa sccaceAll checks have passed12 successful checksvO build_accept_deploy Successfulin 10m - Workflow: buiid_accept_deploy0 ci/circleci: build-backend — Your tests passed on CircleCl→0 cilcireleci- build-frontend = Yourtocte naceed on Cireloctlv O ci/circleci: checkout-code - Your tests passed on CircleCI!/ 0 cilcircleci: phostan — Your tests passed on Circleclv ci/circleci: setup - Your tests passed on CircleC!l0 cilcircleci: sonar cloudOASOMОД* 100% K/a: 8- Thu 28 May 17:08:32Veriricd X c9184961X 6bcSc12x 4le8cбbVebbaabs...
|
86411
|
NULL
|
NULL
|
NULL
|
|
86411
|
2963
|
18
|
2026-05-28T14:08:29.229477+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-28/1779 /Users/lukas/.screenpipe/data/data/2026-05-28/1779977309229_m2.jpg...
|
Firefox
|
JY-20963 fix deleted object import by LakyLak · Pu JY-20963 fix deleted object import by LakyLak · Pull Request #12121 · jiminny/app — Work...
|
1
|
github.com/jiminny/app/pull/12121
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Platform Sprint 5 Q2 - Platform Team - Scrum Board Platform Sprint 5 Q2 - Platform Team - Scrum Board - Jira
[SRD-6881] [On demand] Transcription in saved search disappears - Jira
Pipelines - jiminny/app
Pipelines - jiminny/app
BE upgrade libraries
[JY-20613] Allow owner's role to be selected when setting up a trial - Jira
[JY-20613] Allow owner's role to be selected when setting up a trial - Jira
Text relay
Deleted object error
[JY-20963] Fix foreign key violation when matching activities to deleted Salesforce opportunities - Jira
[JY-20963] Fix foreign key violation when matching activities to deleted Salesforce opportunities - Jira
Illuminate\Database\QueryException: SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint fails (`jiminny`.`activities`, CONSTRAINT `activities_contact_id_foreign` FOREIGN KEY (`contact_id`) REFERE
Illuminate\Database\QueryException: SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint fails (`jiminny`.`activities`, CONSTRAINT `activities_contact_id_foreign` FOREIGN KEY (`contact_id`) REFERE
JY-20963 fix deleted object import by LakyLak · Pull Request #12121 · jiminny/app
JY-20963 fix deleted object import by LakyLak · Pull Request #12121 · jiminny/app
Close tab
Login | Salesforce
Login | Salesforce
Feed — jiminny — Sentry
Feed — jiminny — Sentry
Inbox (1,734) - [EMAIL] - Jiminny Mail
Inbox (1,734) - [EMAIL] - Jiminny Mail
[JY-20979] Resolve PHP 8.5.5 deprications - Jira
[JY-20979] Resolve PHP 8.5.5 deprications - Jira
Jiminny
Jiminny
Platform Sprint 5 Q2 - Platform Team - Scrum Board - Jira
Platform Sprint 5 Q2 - Platform Team - Scrum Board - Jira
transcript ss issue
Jiminny
Jiminny
[SRD-6881] [On demand] Transcription in saved search disappears - Jira
[SRD-6881] [On demand] Transcription in saved search disappears - Jira
Sona Subramanian at 27/05/2026, 17:46:08 - Session Replay - LogRocket
Sona Subramanian at 27/05/2026, 17:46:08 - Session Replay - LogRocket
Iliyana Netseva at 27/05/2026, 18:48:18 - Session Replay - LogRocket
Iliyana Netseva at 27/05/2026, 18:48:18 - Session Replay - LogRocket
Jiminny
Jiminny
New Tab...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Platform Sprint 5 Q2 - Platform Team - Scrum Board - Jira","depth":4,"bounds":{"left":0.0018284575,"top":0.0518755,"width":0.038065158,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"[SRD-6881] [On demand] Transcription in saved search disappears - Jira","depth":4,"bounds":{"left":0.039893616,"top":0.0518755,"width":0.037898935,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"Pipelines - jiminny/app","depth":4,"bounds":{"left":0.0,"top":0.09497207,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pipelines - jiminny/app","depth":5,"bounds":{"left":0.013297873,"top":0.10614525,"width":0.039228722,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"BE upgrade libraries","depth":4,"bounds":{"left":0.0028257978,"top":0.13288109,"width":0.03939495,"height":0.01915403},"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"[JY-20613] Allow owner's role to be selected when setting up a trial - Jira","depth":4,"bounds":{"left":0.0,"top":0.15203512,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20613] Allow owner's role to be selected when setting up a trial - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.1632083,"width":0.12799202,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Text relay","depth":4,"bounds":{"left":0.0028257978,"top":0.18994413,"width":0.020279255,"height":0.01915403},"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Deleted object error","depth":4,"bounds":{"left":0.0028257978,"top":0.21428572,"width":0.039228722,"height":0.01915403},"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":true},{"role":"AXRadioButton","text":"[JY-20963] Fix foreign key violation when matching activities to deleted Salesforce opportunities - Jira","depth":4,"bounds":{"left":0.0028257978,"top":0.23782921,"width":0.07679521,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20963] Fix foreign key violation when matching activities to deleted Salesforce opportunities - Jira","depth":5,"bounds":{"left":0.015957447,"top":0.2490024,"width":0.17902261,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Illuminate\\Database\\QueryException: SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint fails (`jiminny`.`activities`, CONSTRAINT `activities_contact_id_foreign` FOREIGN KEY (`contact_id`) REFERE","depth":4,"bounds":{"left":0.0028257978,"top":0.27055067,"width":0.07679521,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Illuminate\\Database\\QueryException: SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint fails (`jiminny`.`activities`, CONSTRAINT `activities_contact_id_foreign` FOREIGN KEY (`contact_id`) REFERE","depth":5,"bounds":{"left":0.015957447,"top":0.28172386,"width":0.45611703,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"JY-20963 fix deleted object import by LakyLak · Pull Request #12121 · jiminny/app","depth":4,"bounds":{"left":0.0028257978,"top":0.30327216,"width":0.07679521,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXStaticText","text":"JY-20963 fix deleted object import by LakyLak · Pull Request #12121 · jiminny/app","depth":5,"bounds":{"left":0.015957447,"top":0.31444532,"width":0.14245346,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Close tab","depth":5,"bounds":{"left":0.06732048,"top":0.3104549,"width":0.007978723,"height":0.01915403},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"Login | Salesforce","depth":4,"bounds":{"left":0.0,"top":0.33599362,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Login | Salesforce","depth":5,"bounds":{"left":0.013297873,"top":0.3471668,"width":0.030917553,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Feed — jiminny — Sentry","depth":4,"bounds":{"left":0.0,"top":0.36871508,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Feed — jiminny — Sentry","depth":5,"bounds":{"left":0.013297873,"top":0.37988827,"width":0.042719416,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Inbox (1,734) - lukas.kovalik@jiminny.com - Jiminny Mail","depth":4,"bounds":{"left":0.0,"top":0.40143654,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Inbox (1,734) - lukas.kovalik@jiminny.com - Jiminny Mail","depth":5,"bounds":{"left":0.013297873,"top":0.41260973,"width":0.09757314,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20979] Resolve PHP 8.5.5 deprications - Jira","depth":4,"bounds":{"left":0.0,"top":0.43415803,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20979] Resolve PHP 8.5.5 deprications - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.44533122,"width":0.08543883,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Jiminny","depth":4,"bounds":{"left":0.0,"top":0.4668795,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Jiminny","depth":5,"bounds":{"left":0.013297873,"top":0.47805268,"width":0.013131649,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Platform Sprint 5 Q2 - Platform Team - Scrum Board - Jira","depth":4,"bounds":{"left":0.0,"top":0.49960095,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Platform Sprint 5 Q2 - Platform Team - Scrum Board - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.51077414,"width":0.10106383,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"transcript ss issue","depth":4,"bounds":{"left":0.0028257978,"top":0.53751,"width":0.036070477,"height":0.01915403},"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":true},{"role":"AXRadioButton","text":"Jiminny","depth":4,"bounds":{"left":0.0028257978,"top":0.56105345,"width":0.07679521,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Jiminny","depth":5,"bounds":{"left":0.015957447,"top":0.57222664,"width":0.013297873,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[SRD-6881] [On demand] Transcription in saved search disappears - Jira","depth":4,"bounds":{"left":0.0028257978,"top":0.5937749,"width":0.07679521,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[SRD-6881] [On demand] Transcription in saved search disappears - Jira","depth":5,"bounds":{"left":0.015957447,"top":0.6049481,"width":0.1271609,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Sona Subramanian at 27/05/2026, 17:46:08 - Session Replay - LogRocket","depth":4,"bounds":{"left":0.0028257978,"top":0.62649643,"width":0.07679521,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Sona Subramanian at 27/05/2026, 17:46:08 - Session Replay - LogRocket","depth":5,"bounds":{"left":0.015957447,"top":0.63766956,"width":0.12799202,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Iliyana Netseva at 27/05/2026, 18:48:18 - Session Replay - LogRocket","depth":4,"bounds":{"left":0.0028257978,"top":0.6592179,"width":0.07679521,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Iliyana Netseva at 27/05/2026, 18:48:18 - Session Replay - LogRocket","depth":5,"bounds":{"left":0.015957447,"top":0.6703911,"width":0.12134308,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Jiminny","depth":4,"bounds":{"left":0.0,"top":0.69193935,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Jiminny","depth":5,"bounds":{"left":0.013297873,"top":0.70311254,"width":0.013131649,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"New Tab","depth":4,"bounds":{"left":0.0028257978,"top":0.72625697,"width":0.07413564,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false}]...
|
-1090785327448043711
|
4955677166574597217
|
visual_change
|
accessibility
|
NULL
|
Platform Sprint 5 Q2 - Platform Team - Scrum Board Platform Sprint 5 Q2 - Platform Team - Scrum Board - Jira
[SRD-6881] [On demand] Transcription in saved search disappears - Jira
Pipelines - jiminny/app
Pipelines - jiminny/app
BE upgrade libraries
[JY-20613] Allow owner's role to be selected when setting up a trial - Jira
[JY-20613] Allow owner's role to be selected when setting up a trial - Jira
Text relay
Deleted object error
[JY-20963] Fix foreign key violation when matching activities to deleted Salesforce opportunities - Jira
[JY-20963] Fix foreign key violation when matching activities to deleted Salesforce opportunities - Jira
Illuminate\Database\QueryException: SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint fails (`jiminny`.`activities`, CONSTRAINT `activities_contact_id_foreign` FOREIGN KEY (`contact_id`) REFERE
Illuminate\Database\QueryException: SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint fails (`jiminny`.`activities`, CONSTRAINT `activities_contact_id_foreign` FOREIGN KEY (`contact_id`) REFERE
JY-20963 fix deleted object import by LakyLak · Pull Request #12121 · jiminny/app
JY-20963 fix deleted object import by LakyLak · Pull Request #12121 · jiminny/app
Close tab
Login | Salesforce
Login | Salesforce
Feed — jiminny — Sentry
Feed — jiminny — Sentry
Inbox (1,734) - [EMAIL] - Jiminny Mail
Inbox (1,734) - [EMAIL] - Jiminny Mail
[JY-20979] Resolve PHP 8.5.5 deprications - Jira
[JY-20979] Resolve PHP 8.5.5 deprications - Jira
Jiminny
Jiminny
Platform Sprint 5 Q2 - Platform Team - Scrum Board - Jira
Platform Sprint 5 Q2 - Platform Team - Scrum Board - Jira
transcript ss issue
Jiminny
Jiminny
[SRD-6881] [On demand] Transcription in saved search disappears - Jira
[SRD-6881] [On demand] Transcription in saved search disappears - Jira
Sona Subramanian at 27/05/2026, 17:46:08 - Session Replay - LogRocket
Sona Subramanian at 27/05/2026, 17:46:08 - Session Replay - LogRocket
Iliyana Netseva at 27/05/2026, 18:48:18 - Session Replay - LogRocket
Iliyana Netseva at 27/05/2026, 18:48:18 - Session Replay - LogRocket
Jiminny
Jiminny
New Tab...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
86410
|
2963
|
17
|
2026-05-28T14:08:26.075503+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-28/1779 /Users/lukas/.screenpipe/data/data/2026-05-28/1779977306075_m2.jpg...
|
Firefox
|
JY-20963 fix deleted object import by LakyLak · Pu JY-20963 fix deleted object import by LakyLak · Pull Request #12121 · jiminny/app — Work...
|
1
|
github.com/jiminny/app/pull/12121
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Platform Sprint 5 Q2 - Platform Team - Scrum Board Platform Sprint 5 Q2 - Platform Team - Scrum Board - Jira
[SRD-6881] [On demand] Transcription in saved search disappears - Jira...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Platform Sprint 5 Q2 - Platform Team - Scrum Board - Jira","depth":4,"bounds":{"left":0.0018284575,"top":0.0518755,"width":0.038065158,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"[SRD-6881] [On demand] Transcription in saved search disappears - Jira","depth":4,"bounds":{"left":0.039893616,"top":0.0518755,"width":0.037898935,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false}]...
|
-8578556159445200548
|
4739486653691733234
|
visual_change
|
hybrid
|
NULL
|
Platform Sprint 5 Q2 - Platform Team - Scrum Board Platform Sprint 5 Q2 - Platform Team - Scrum Board - Jira
[SRD-6881] [On demand] Transcription in saved search disappears - Jira
Firefox[PASSWORD_DOTS]© Pipelines - |IminnylappBE uporade libraries(JY-20613) Allow owner's role to bText relayDeleted object error(JY-20963) Fix foreign key violatluminate/Database|Query&xceplJY-20983 fix deleted objecti xtooin t ShlastorcnFeed - Jiminny - SentryM Inbox (1.734) - Iias.kowalik Slim'(JY-20979) Resolve PHP 8.5.5 deg8 Jiminaypiattoem Son dthloy -Pintormetranscript ss issue8 Jiminny7(59D-68811/00 demandl Transd8 Sona Subramacian at 27/05/20268 Ilivana Netsova at 27/05/2026, 18)8 Jimieny+ New TabEditViewmistonooountrnroitesToolsWindow= github.com/jiminny/app/pull/12121jiminny / app &<> Code17 Pull requests 34G. Agents• Actions |W Wiki© Security and quality 12lw InsightsE SettingsJY-20963 fix deleted object import #12121 •Lakylak wants to merce 7 commits into master from JY-28963-fix-imoort-on-deleted-entity12 open@) Conversation 4E Checks 3 Files changed 3LakyLak commented last week• edited by nikolay-yankovJIRA:Changes• Fix import obiect it deletedEf LakyLak and others added 2 commits last weeka @JY-28963 11x deleted object importX 8c151a1& Merge branch 'naster' into JY-28963-1ix-import-on-deleted-entityX 79dc986& nikolay-yankov force-pushed the JY-20963-fix-import-on-deleted-entity branch 2 times, most recently from86b558b to 79dc986 last weekComoartx , nikolay-yankov deleted a comment from github-actions Bot last weekx & nikolay-yankov deleted a comment from github-actions (Bot) last weeknikolay-yankov commented last week@claudeMerce branch "master' into JY-29963-1sx-inoort-on-deleted-entitvnikolay-yankov commented last weekX d3de6e8GMOДТ100% K53 8 • Thu 28 May 17:08:25Q Type to search+ONG0•Awaiting approvaleode-4483 -52 88881• yalokin-jiminny€ Vasil-Jiminnyilan-jiminnyRequestRequestRequestAt least 1 approving review is required to merge this pullStill in prooress? Convert to draftAssigneesNo one-assign yourseiftaboleNone yetProiecisNore yotMi mosonoDevelopmentSuccessfully merging this pull reguest may close thesissues.None yetNotificationsCuttomian& UnsubscribeYou're receiving notifications because you're watchingthis repository.2 participants...
|
86409
|
NULL
|
NULL
|
NULL
|
|
86409
|
2963
|
16
|
2026-05-28T14:08:22.924721+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-28/1779 /Users/lukas/.screenpipe/data/data/2026-05-28/1779977302924_m2.jpg...
|
Firefox
|
JY-20963 fix deleted object import by LakyLak · Pu JY-20963 fix deleted object import by LakyLak · Pull Request #12121 · jiminny/app — Work...
|
1
|
github.com/jiminny/app/pull/12121
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Platform Sprint 5 Q2 - Platform Team - Scrum Board Platform Sprint 5 Q2 - Platform Team - Scrum Board - Jira
[SRD-6881] [On demand] Transcription in saved search disappears - Jira
Pipelines - jiminny/app
Pipelines - jiminny/app...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Platform Sprint 5 Q2 - Platform Team - Scrum Board - Jira","depth":4,"bounds":{"left":0.0018284575,"top":0.0518755,"width":0.038065158,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"[SRD-6881] [On demand] Transcription in saved search disappears - Jira","depth":4,"bounds":{"left":0.039893616,"top":0.0518755,"width":0.037898935,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"Pipelines - jiminny/app","depth":4,"bounds":{"left":0.0,"top":0.09497207,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pipelines - jiminny/app","depth":5,"bounds":{"left":0.013297873,"top":0.10614525,"width":0.039228722,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"}]...
|
-2112793529402417778
|
4739293140786619618
|
visual_change
|
hybrid
|
NULL
|
Platform Sprint 5 Q2 - Platform Team - Scrum Board Platform Sprint 5 Q2 - Platform Team - Scrum Board - Jira
[SRD-6881] [On demand] Transcription in saved search disappears - Jira
Pipelines - jiminny/app
Pipelines - jiminny/app
rircroxViewhsttonooountrnToolsPotLe.= github.com/WindowHelpapp/pull/12121minny / app ÖPipelines - jiminny/app<> Code17 Pull requests6. Agents© ActionsQ0 Wiki© Security and qualitylw InsightsE Settings* (JY-20613) Allow owner's role to tJY-20963 fix deleted object import #12121 •17 OpenLakyLak wants to merge 7 commits into master from JY-20963-fix-import-on-deleted-entity LP Conversation 4yaorok Fix torcion key violntEl Checks 3B Files changed 3Illuminate\Database|QueryExceplCT All commitsJY-20983 fix deleted objecti xQ Ffiter files....tooin t ShlastorcnFeed - Jiminny - Sentryapp/Http/Controllers/API/ActivityController.php gee -378,6 +370,18 @l public function sumnarize(Activity Sactivity): mixedScontact = ScraService->syncContact(SobjectId):M Inbox (1734) - Iitns.kownlik Slim(JY-20979) Rescive PHP 8.5.5 deppihttoem Son dthloy - Pintormv — aocl• E Http/Controllers/API|© ActivityController.php~ # Services/Crm/Salesforce# Service.php~ E tests/Unit/Services/Crm/Salesfor...© ServiceTest.php3721374sthis->logger->info('resolving account*);Saccount = Sthis->resolveAccount(Stean, Scontact, ScraService, Sprospects);transcript ss issue8 Jiminny(SOD:6881) fOn demandll Trans8 Sona Subramacian at 27/05/202€app/Services/Crm/Salesforce/Service.phpee -1084,6 +1084,10 Cl private function importLead(ScraData): ?Lead8 Ilivana Netsova at 27/05/2026, 18)18841885Sthis-shandle0bjectDeletion(Slead, Scrbata);8 Jiminey+ New Tabrerurn "caoh108900 -1146,8 +1150,14 @ public function syncAccount(string Screid): ?Account1148return Sthis-sinoortAccount(SstAccount):11491158private function inportAccount(ScraData): AccountOESOMОД100% KS3 8 - Thu 28 May 17:08:22Q Type to search• Awaiting approval0 0/3 viewedSubmit review+DUU8Code(Preview) -4183 -52 88801O Viewed37037137237 :378379108410921093Sthis-shandleObjectDeletion(Slead, Screfata);if (Stead-»trashed()) €return null;return Slead;[CREDIT_CARD]+11541155+, retur sthis-adaportAccont (stAccount):private function importAccount(Scrabata): ?Account1f (1 empty(ScraData('IsDeleted"))) €, scontact = scrsenice-synContactsojecto);1f (l Scontact instanceof Contact) ‹break;Sthis→>logger→>info('resolving account");saccount = sthis-resolveÁccount(stean, scontact, scraService, Sprospects):...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
86408
|
2962
|
18
|
2026-05-28T14:08:20.850546+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-28/1779 /Users/lukas/.screenpipe/data/data/2026-05-28/1779977300850_m1.jpg...
|
Firefox
|
JY-20963 fix deleted object import by LakyLak · Pu JY-20963 fix deleted object import by LakyLak · Pull Request #12121 · jiminny/app — Work...
|
1
|
github.com/jiminny/app/pull/12121/changes
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Activity MonitorFileEditViewWindowHelpActivity Mon Activity MonitorFileEditViewWindowHelpActivity MonitorAll ProcessesProcess Name% CPUkernel_taskPhpStormVirtual Machine Service for DockerWindowServerscreenpipereplaydNotion Helper (Renderer)FirefoxNotionFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentNotion HelperlaunchservicesddeletediTerm2Firefox GPU HelpercoreaudiodSlack Helpermds_storeslaunchdActivity MonitorSlack Helper (Renderer)FirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentClaudeWispr Flow Helper (Renderer)mds203,9137,9CursorUlViewService (Not Responding)70,053,550,038,430,714,513,911,88,37,77,46,65,75,45,35,34,84,44,44,24,03,83,83,22,62,4CPU Time22:49:28,512:34:54,131:53:23,347:50:38,993:35:08,365:42:39,5624:00,661:46:46,589:34,4323:22,8333:45,263:11,471:00:08,011:25,221:03:31,912:23:38,4657:04,5314:27,561:03:38,3218:36,376:48,9957:27,1629:47,062:17,7628:23,083:46,8628:51,395:45,79System:User:Idle:Threads CPUMemoryEnergyDiskNetworkIdle Wake-UpsKind % GPUAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleApple0,08,50,043,40,00,00,00,00,00,00,00,00,00,00,40,00,04,60,00,00,00,00,00,00,00,00,00,051,17%48,71%0.12%CPU LOADThreads:Processes:GPU Time0,007:48,780,003:20:12,805:03,900,000,000,040,000,000,000,000,000,000,000,000,000,000,000,000,000,00‹ >0 (hlPID % CThu 28 May 17:08:20SearchUserrootlukaslukas_windowserverlukaslukaslukaslukaslukaslukaslukaslukasrootlukaslukaslukas_coreaudiodlukasrootrootlukaslukaslukaslukaslukaslukasrootlukas...
|
NULL
|
2459894309546914639
|
NULL
|
click
|
ocr
|
NULL
|
Activity MonitorFileEditViewWindowHelpActivity Mon Activity MonitorFileEditViewWindowHelpActivity MonitorAll ProcessesProcess Name% CPUkernel_taskPhpStormVirtual Machine Service for DockerWindowServerscreenpipereplaydNotion Helper (Renderer)FirefoxNotionFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentNotion HelperlaunchservicesddeletediTerm2Firefox GPU HelpercoreaudiodSlack Helpermds_storeslaunchdActivity MonitorSlack Helper (Renderer)FirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentClaudeWispr Flow Helper (Renderer)mds203,9137,9CursorUlViewService (Not Responding)70,053,550,038,430,714,513,911,88,37,77,46,65,75,45,35,34,84,44,44,24,03,83,83,22,62,4CPU Time22:49:28,512:34:54,131:53:23,347:50:38,993:35:08,365:42:39,5624:00,661:46:46,589:34,4323:22,8333:45,263:11,471:00:08,011:25,221:03:31,912:23:38,4657:04,5314:27,561:03:38,3218:36,376:48,9957:27,1629:47,062:17,7628:23,083:46,8628:51,395:45,79System:User:Idle:Threads CPUMemoryEnergyDiskNetworkIdle Wake-UpsKind % GPUAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleApple0,08,50,043,40,00,00,00,00,00,00,00,00,00,00,40,00,04,60,00,00,00,00,00,00,00,00,00,051,17%48,71%0.12%CPU LOADThreads:Processes:GPU Time0,007:48,780,003:20:12,805:03,900,000,000,040,000,000,000,000,000,000,000,000,000,000,000,000,000,00‹ >0 (hlPID % CThu 28 May 17:08:20SearchUserrootlukaslukas_windowserverlukaslukaslukaslukaslukaslukaslukaslukasrootlukaslukaslukas_coreaudiodlukasrootrootlukaslukaslukaslukaslukaslukasrootlukas...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
86407
|
2963
|
15
|
2026-05-28T14:08:19.732388+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-28/1779 /Users/lukas/.screenpipe/data/data/2026-05-28/1779977299732_m2.jpg...
|
Firefox
|
JY-20963 fix deleted object import by LakyLak · Pu JY-20963 fix deleted object import by LakyLak · Pull Request #12121 · jiminny/app — Work...
|
1
|
github.com/jiminny/app/pull/12121/changes
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
rircroxViewhsttonooountrnPotLe.ToolsWindowHelp[PAS rircroxViewhsttonooountrnPotLe.ToolsWindowHelp[PASSWORD_DOTS]= github.com/JY-20963 11x deleted obiect imoort #21211LakyLak wants to merge 7 commits into naster from JY-20963-fix-import-on-deleted-entityPipelines - jiminny/appCFitertec..* (JY-20613) Allow owner's role to t~ #i app~ $й Http/Controllers/AP1|2) ActivityController.phoDeleted object erroryyorokFix orcion key wiolnt~ E Services/Crm/Salesforce# Service.php373sthis->logger->info("resolving account');Saccount = Sthis->resolveAccount(Steam, Scontact, Scraßervice, Sprospects);Illuminate\Database|QueryExceplv=tests/Unit/Services/crm/Salestor…JY-20983 fix deleted objecti xServiceTest.phpv ao0/Services/Crn/Salesforce/Service.ohpglooin t Siastorca0Q -1884,6 +1884,18 @ private function importLead(ScrmData): ?LeadFeed - Jiminny - Sentry10841085Sthis-yhandleObjectDeletion(Slead, Scrabata);M Inbox (1.734) - Iias.kowalik Slim(JY-20979) Rescive PHP 8.5.5 deppihttoem Son dthloy - Pintorm108711088return Stead:ltranscript ss issue8 Jiminny00 -1146,8 1150,14 @l public function syncAccount(string Scrmid): ?AccountSOD:68811 (0n demandll Trans8 Sona Subramacian at 27/05/2026114611471148return Sthis->inportAccount(SsfAccount);orivate tunction tmoort ccountscrontal: Account8 Ilivana Netsova at 27/05/2026, 18)11508 Jimieny— Now Tab ScountryCode = Screbata|'B111ingCountryCode') 27 ScraDatal'ShippingCountryCode') 27 null;Sthis-shandleObjectDeletion(Saccount, ScraData):retum snccount:3%0 :3763773783791884піiіs10921093115eHÃA116312271228122912301231OESOMОД100% K/2 • Thu 28 May 17:08:19• 0 1 3 viewedAwattnt approvsthis->logger-info('resolving account');Saccount = Sthis->resolveAccount(Steam, Scontact, ScrnService, Sprospects):+78 -52 manoo Viewed.Sthis->handleObjectDeletion(Slead, screbata);if (Slead-»trashed()) (return nutl;return Slead;, retur sthtsalaporefc.count/(stAcoumt):private funetion iaportAccount (screbeta): RAccont1f (l empty(scrabatal'IsDeleted')) €Sthis->handleEntityDeletionByProviderId(Sthis->config->accounts(), ScrnData);return null;ScountryCode = Serebata('BI1lingCountryCode') 77 screbatal'ShippingCountryCode') 27 null;I/ Salesforce allows custon "countries" to be created. Disregard these.Sthis-shandleObjectDeletion(Saccount, scrabata);return Saccount-strashed() 2 nutl : Saccount;...
|
NULL
|
-4582857988412358498
|
NULL
|
visual_change
|
ocr
|
NULL
|
rircroxViewhsttonooountrnPotLe.ToolsWindowHelp[PAS rircroxViewhsttonooountrnPotLe.ToolsWindowHelp[PASSWORD_DOTS]= github.com/JY-20963 11x deleted obiect imoort #21211LakyLak wants to merge 7 commits into naster from JY-20963-fix-import-on-deleted-entityPipelines - jiminny/appCFitertec..* (JY-20613) Allow owner's role to t~ #i app~ $й Http/Controllers/AP1|2) ActivityController.phoDeleted object erroryyorokFix orcion key wiolnt~ E Services/Crm/Salesforce# Service.php373sthis->logger->info("resolving account');Saccount = Sthis->resolveAccount(Steam, Scontact, Scraßervice, Sprospects);Illuminate\Database|QueryExceplv=tests/Unit/Services/crm/Salestor…JY-20983 fix deleted objecti xServiceTest.phpv ao0/Services/Crn/Salesforce/Service.ohpglooin t Siastorca0Q -1884,6 +1884,18 @ private function importLead(ScrmData): ?LeadFeed - Jiminny - Sentry10841085Sthis-yhandleObjectDeletion(Slead, Scrabata);M Inbox (1.734) - Iias.kowalik Slim(JY-20979) Rescive PHP 8.5.5 deppihttoem Son dthloy - Pintorm108711088return Stead:ltranscript ss issue8 Jiminny00 -1146,8 1150,14 @l public function syncAccount(string Scrmid): ?AccountSOD:68811 (0n demandll Trans8 Sona Subramacian at 27/05/2026114611471148return Sthis->inportAccount(SsfAccount);orivate tunction tmoort ccountscrontal: Account8 Ilivana Netsova at 27/05/2026, 18)11508 Jimieny— Now Tab ScountryCode = Screbata|'B111ingCountryCode') 27 ScraDatal'ShippingCountryCode') 27 null;Sthis-shandleObjectDeletion(Saccount, ScraData):retum snccount:3%0 :3763773783791884піiіs10921093115eHÃA116312271228122912301231OESOMОД100% K/2 • Thu 28 May 17:08:19• 0 1 3 viewedAwattnt approvsthis->logger-info('resolving account');Saccount = Sthis->resolveAccount(Steam, Scontact, ScrnService, Sprospects):+78 -52 manoo Viewed.Sthis->handleObjectDeletion(Slead, screbata);if (Slead-»trashed()) (return nutl;return Slead;, retur sthtsalaporefc.count/(stAcoumt):private funetion iaportAccount (screbeta): RAccont1f (l empty(scrabatal'IsDeleted')) €Sthis->handleEntityDeletionByProviderId(Sthis->config->accounts(), ScrnData);return null;ScountryCode = Serebata('BI1lingCountryCode') 77 screbatal'ShippingCountryCode') 27 null;I/ Salesforce allows custon "countries" to be created. Disregard these.Sthis-shandleObjectDeletion(Saccount, scrabata);return Saccount-strashed() 2 nutl : Saccount;...
|
86406
|
NULL
|
NULL
|
NULL
|
|
86406
|
2963
|
14
|
2026-05-28T14:08:16.661149+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-28/1779 /Users/lukas/.screenpipe/data/data/2026-05-28/1779977296661_m2.jpg...
|
Firefox
|
JY-20963 fix deleted object import by LakyLak · Pu JY-20963 fix deleted object import by LakyLak · Pull Request #12121 · jiminny/app — Work...
|
1
|
github.com/jiminny/app/pull/12121/changes
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
rircroxViewhsttonHelp© Pipelines - |Iminnylapp* (J rircroxViewhsttonHelp© Pipelines - |Iminnylapp* (JY-20613) Allow owner's role to tText relayyaorok Fix torcion key violntluminate/Database|Query&xceplJY-20983 fix deleted objecti xlooin t SiastorcaFeed - Jiminny - SentryM Inbox (1.734) - Iias.kowalik Slim(JY-20979) Rescive PHP 8.5.5 deppiattoem Son dthloy -Pintormetranscript ss issue8 Jiminny(SOD:6881) fOn demandll Trans8 Sona Subramacian at 27/05/20268 Ilivana Netsova at 27/05/2026, 18)8 JimienyooountrnPotLe.Window8) JIMINNYSpaces / Jiminny (New)Platform Team 2.#: Summaryl& Timeline® BacklogC Search noarciREADY FOR DEV &mnnwewyerver versioninemport bydelque bizt2im- jirtbry/appUocrade 3t libraries - MaviMATIENAEEBacklogJY-19958Falthack mechanism for users with active SFtokens for CRM MatchingREDUCE CHURN# JY-20912Ucors can take otos durind and attermeetingsRECORDING MEETINOS WITHOUT VISIBLE NOTETAKERS (..Backlog( JY-208712.5 /2 0000 =Users can filter Scores in Team Insights byHost |AUTOMATED AT SCORNBacklogA JY-2084715.6600 —AI Call Scoring quick access in PlaybackneadeAUTOMATED AY SCORINGBackiooQ Search+ CreateQOMOX 100%K3 8• Thu 28 May 17:08:16*E ASKROVO A ® BID Active sprints8 CalendarE Reports4 Testing Board # List Forms C Components *, Development % Code Security & Releases Deployments E Archived work itemsF DocdEpicvType vOuick tilterswINDEV SCODE REVIEWBLOCKEDwoochadeuseisooeralistorcalisanetheir detailsUIMINNY MCP CONNECTORIn Dev( JY-2083310 11 .000 =0Fix foreign key violation when matchingactivities to deleted Salesforce opportun...SUPPORT TICKETS...X* JY-209631 •000= 0Evaluation for Exec ReportsAUTOMATE EXEC REPORTS IN THE PRODUCTIIn Dey[ JY-1989010 / 0000=ĐUser can log & save meeting information toRECORDING MEETINGS WITHOUT VISIBLE NOTETAKERS (.in DeyA JY-20753|13 •=0Call Scoring filter for Exec reportsAUTOMATID A CORINGin ner[ JY-20771•n •=0OA 2NSCP > Enable the Al to know detalis about theJIMINNY MCP CONNECTORIn QAA -2084612 1 •0 = 0oe eati uestr o got l st o odsandJIMINNY MCP CONNECTORIn QA[ JY-208355 n •=02 Shortcuts vSlack intearation$ Reporting Center +PO ACCEPTANCEComplete sprintDEPLOYGroup: Queries...
|
NULL
|
2207273653037159779
|
NULL
|
click
|
ocr
|
NULL
|
rircroxViewhsttonHelp© Pipelines - |Iminnylapp* (J rircroxViewhsttonHelp© Pipelines - |Iminnylapp* (JY-20613) Allow owner's role to tText relayyaorok Fix torcion key violntluminate/Database|Query&xceplJY-20983 fix deleted objecti xlooin t SiastorcaFeed - Jiminny - SentryM Inbox (1.734) - Iias.kowalik Slim(JY-20979) Rescive PHP 8.5.5 deppiattoem Son dthloy -Pintormetranscript ss issue8 Jiminny(SOD:6881) fOn demandll Trans8 Sona Subramacian at 27/05/20268 Ilivana Netsova at 27/05/2026, 18)8 JimienyooountrnPotLe.Window8) JIMINNYSpaces / Jiminny (New)Platform Team 2.#: Summaryl& Timeline® BacklogC Search noarciREADY FOR DEV &mnnwewyerver versioninemport bydelque bizt2im- jirtbry/appUocrade 3t libraries - MaviMATIENAEEBacklogJY-19958Falthack mechanism for users with active SFtokens for CRM MatchingREDUCE CHURN# JY-20912Ucors can take otos durind and attermeetingsRECORDING MEETINOS WITHOUT VISIBLE NOTETAKERS (..Backlog( JY-208712.5 /2 0000 =Users can filter Scores in Team Insights byHost |AUTOMATED AT SCORNBacklogA JY-2084715.6600 —AI Call Scoring quick access in PlaybackneadeAUTOMATED AY SCORINGBackiooQ Search+ CreateQOMOX 100%K3 8• Thu 28 May 17:08:16*E ASKROVO A ® BID Active sprints8 CalendarE Reports4 Testing Board # List Forms C Components *, Development % Code Security & Releases Deployments E Archived work itemsF DocdEpicvType vOuick tilterswINDEV SCODE REVIEWBLOCKEDwoochadeuseisooeralistorcalisanetheir detailsUIMINNY MCP CONNECTORIn Dev( JY-2083310 11 .000 =0Fix foreign key violation when matchingactivities to deleted Salesforce opportun...SUPPORT TICKETS...X* JY-209631 •000= 0Evaluation for Exec ReportsAUTOMATE EXEC REPORTS IN THE PRODUCTIIn Dey[ JY-1989010 / 0000=ĐUser can log & save meeting information toRECORDING MEETINGS WITHOUT VISIBLE NOTETAKERS (.in DeyA JY-20753|13 •=0Call Scoring filter for Exec reportsAUTOMATID A CORINGin ner[ JY-20771•n •=0OA 2NSCP > Enable the Al to know detalis about theJIMINNY MCP CONNECTORIn QAA -2084612 1 •0 = 0oe eati uestr o got l st o odsandJIMINNY MCP CONNECTORIn QA[ JY-208355 n •=02 Shortcuts vSlack intearation$ Reporting Center +PO ACCEPTANCEComplete sprintDEPLOYGroup: Queries...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
86405
|
2962
|
17
|
2026-05-28T14:08:16.533842+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-28/1779 /Users/lukas/.screenpipe/data/data/2026-05-28/1779977296533_m1.jpg...
|
Firefox
|
JY-20963 fix deleted object import by LakyLak · Pu JY-20963 fix deleted object import by LakyLak · Pull Request #12121 · jiminny/app — Work...
|
1
|
github.com/jiminny/app/pull/12121/changes
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Activity MonitorFileEditViewWindowHelpActivity Mon Activity MonitorFileEditViewWindowHelpActivity MonitorAll ProcessesProcess Name% CPUkernel_taskPhpStormVirtual Machine Service for DockerWindowServerscreenpipereplaydNotion Helper (Renderer)FirefoxNotionFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentNotion HelperlaunchservicesddeletediTerm2Firefox GPU HelpercoreaudiodSlack Helpermds_storeslaunchdActivity MonitorSlack Helper (Renderer)FirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentClaudeWispr Flow Helper (Renderer)mds203,9137,9CursorUlViewService (Not Responding)70,053,550,038,430,714,513,911,88,37,77,46,65,75,45,35,34,84,44,44,24,03,83,83,22,62,4CPU Time22:49:28,512:34:54,131:53:23,347:50:38,993:35:08,365:42:39,5624:00,661:46:46,589:34,4323:22,8333:45,263:11,471:00:08,011:25,221:03:31,912:23:38,4657:04,5314:27,561:03:38,3218:36,376:48,9957:27,1629:47,062:17,7628:23,083:46,8628:51,395:45,79System:User:Idle:Threads CPUMemoryEnergyDiskNetworkIdle Wake-UpsKind % GPUAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleApple0,08,50,043,40,00,00,00,00,00,00,00,00,00,00,40,00,04,60,00,00,00,00,00,00,00,00,00,044,48%54,54%0,98%CPU LOADThreads:Processes:GPU Time0,007:48,780,003:20:12,805:03,900,000,000,040,000,000,000,000,000,000,000,000,000,000,000,000,000,00‹ >0 (hlPID % CThu 28 May 17:08:16SearchUserrootlukaslukas_windowserverlukaslukaslukaslukaslukaslukaslukaslukasrootlukaslukaslukas_coreaudiodlukasrootrootlukaslukaslukaslukaslukaslukasrootlukas...
|
NULL
|
2340079644224860577
|
NULL
|
click
|
ocr
|
NULL
|
Activity MonitorFileEditViewWindowHelpActivity Mon Activity MonitorFileEditViewWindowHelpActivity MonitorAll ProcessesProcess Name% CPUkernel_taskPhpStormVirtual Machine Service for DockerWindowServerscreenpipereplaydNotion Helper (Renderer)FirefoxNotionFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentNotion HelperlaunchservicesddeletediTerm2Firefox GPU HelpercoreaudiodSlack Helpermds_storeslaunchdActivity MonitorSlack Helper (Renderer)FirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentClaudeWispr Flow Helper (Renderer)mds203,9137,9CursorUlViewService (Not Responding)70,053,550,038,430,714,513,911,88,37,77,46,65,75,45,35,34,84,44,44,24,03,83,83,22,62,4CPU Time22:49:28,512:34:54,131:53:23,347:50:38,993:35:08,365:42:39,5624:00,661:46:46,589:34,4323:22,8333:45,263:11,471:00:08,011:25,221:03:31,912:23:38,4657:04,5314:27,561:03:38,3218:36,376:48,9957:27,1629:47,062:17,7628:23,083:46,8628:51,395:45,79System:User:Idle:Threads CPUMemoryEnergyDiskNetworkIdle Wake-UpsKind % GPUAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleApple0,08,50,043,40,00,00,00,00,00,00,00,00,00,00,40,00,04,60,00,00,00,00,00,00,00,00,00,044,48%54,54%0,98%CPU LOADThreads:Processes:GPU Time0,007:48,780,003:20:12,805:03,900,000,000,040,000,000,000,000,000,000,000,000,000,000,000,000,000,00‹ >0 (hlPID % CThu 28 May 17:08:16SearchUserrootlukaslukas_windowserverlukaslukaslukaslukaslukaslukaslukaslukasrootlukaslukaslukas_coreaudiodlukasrootrootlukaslukaslukaslukaslukaslukasrootlukas...
|
86402
|
NULL
|
NULL
|
NULL
|
|
86404
|
2963
|
13
|
2026-05-28T14:08:13.815268+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-28/1779 /Users/lukas/.screenpipe/data/data/2026-05-28/1779977293815_m2.jpg...
|
Firefox
|
Platform Sprint 5 Q2 - Platform Team - Scrum Board Platform Sprint 5 Q2 - Platform Team - Scrum Board - Jira — Work...
|
1
|
jiminny.atlassian.net/jira/software/c/projects/JY/ jiminny.atlassian.net/jira/software/c/projects/JY/boards/37...
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
rircrox© Pipelines - |Iminnylapp* (JY-20613) Allow rircrox© Pipelines - |Iminnylapp* (JY-20613) Allow owner's role to tText relayDeleted object erroryaorok Fix torcion key violntluminate/Database|Query&xceplJY-20983 fix deleted object imptooin t ShlastorcnFeed - Jiminny - SentryM Inbox (1.734) - Iias.kowalik Slim(JY-20979) Rescive PHP 8.5.5 deppiattoem Son dthloy -Pintormetranscript ss issue8 Jiminny(SOD:6881) fOn demandll Trans8 Sona Subramacian at 27/05/20268 Ilivana Netsova at 27/05/2026, 18)8 JimienyhsttonooountrnroiuesWVindowHelp8) JIMINNYSpaces / Jiminny (New)Platform Team 2.#: Summaryl& TimelineE BacklogQ Search boardREADY FOR DEV &mnnwewyerver versionineJMINNY MOP CONNECTORBacklogUocrade 3t libraries - MaviMATIENAEEBacklogJY-19956Falthack mechanism for users with active SFtokens for CRM MatchingREDUCE CHURN# JY-20912Ucors can take otos durind and attermeetingsRECORDING MEETINOS WITHOUT VISIBLE NOTETAKERS (..Backlog( JY-208712.5 /2 0000 =Users can filter Scores in Team Insights byHost |AUTOMATED AT SCORNBacklogA JY-2084715.6600 —AI Call Scoring quick access in PlaybackneadeAUTOMATED AY SCORINGBacklooQ Search+ CreateQSOMOX 100%K3 8• Thu 28 May 17:08:13*E ASk ROVO A ® $ID Active sprints8 CalendarL Reports4 Testing Board # List Forms C Components *, Development % Code Security & Releases Deployments E Archived work itemsF DocdEpicvTypeOuick tilterswINDEV SCODE REVIEWBLOCKEDwoochadeuseisooeralistorcalisanetheir detailsJIMININY MCP CONNECTORIn Dev( JY-2083310 11 .000 =0Fix foreign key violation when matching...activities to deleted Salesforce opportun...SUPPORT TICKETSin DerX* JY-209631 •000= 0Evaluation for Exec ReportsAUTOMATE EXEC REPORTS IN THE PRODUCTIIn Dey[ JY-1989010 / 0000=0User can log & save meeting information toRECORDING MEETINGS WITHOUT VISIBLE NOTETAKERS (.in DeyA JY-20753|13 •=0Call Scoring filter for Exec reportsAUTOMATID A CORINGin ner[ JY-20771•n •=0OA 2NSCP > Enable the Al to know detalis about theJIMINNY MCP CONNECTORIn QAA JY-2084612 1 •0 = 0oe eati uestr o got l st o odsandJIMINNY MCP CONNECTORIn QA[ JY-208355 n •=02 Shortcuts vSlack intearation$ Reporting Center +PO ACCEPTANCEComplete sprintDEPLOYGroup: Queries...
|
NULL
|
6734906841629519138
|
NULL
|
visual_change
|
ocr
|
NULL
|
rircrox© Pipelines - |Iminnylapp* (JY-20613) Allow rircrox© Pipelines - |Iminnylapp* (JY-20613) Allow owner's role to tText relayDeleted object erroryaorok Fix torcion key violntluminate/Database|Query&xceplJY-20983 fix deleted object imptooin t ShlastorcnFeed - Jiminny - SentryM Inbox (1.734) - Iias.kowalik Slim(JY-20979) Rescive PHP 8.5.5 deppiattoem Son dthloy -Pintormetranscript ss issue8 Jiminny(SOD:6881) fOn demandll Trans8 Sona Subramacian at 27/05/20268 Ilivana Netsova at 27/05/2026, 18)8 JimienyhsttonooountrnroiuesWVindowHelp8) JIMINNYSpaces / Jiminny (New)Platform Team 2.#: Summaryl& TimelineE BacklogQ Search boardREADY FOR DEV &mnnwewyerver versionineJMINNY MOP CONNECTORBacklogUocrade 3t libraries - MaviMATIENAEEBacklogJY-19956Falthack mechanism for users with active SFtokens for CRM MatchingREDUCE CHURN# JY-20912Ucors can take otos durind and attermeetingsRECORDING MEETINOS WITHOUT VISIBLE NOTETAKERS (..Backlog( JY-208712.5 /2 0000 =Users can filter Scores in Team Insights byHost |AUTOMATED AT SCORNBacklogA JY-2084715.6600 —AI Call Scoring quick access in PlaybackneadeAUTOMATED AY SCORINGBacklooQ Search+ CreateQSOMOX 100%K3 8• Thu 28 May 17:08:13*E ASk ROVO A ® $ID Active sprints8 CalendarL Reports4 Testing Board # List Forms C Components *, Development % Code Security & Releases Deployments E Archived work itemsF DocdEpicvTypeOuick tilterswINDEV SCODE REVIEWBLOCKEDwoochadeuseisooeralistorcalisanetheir detailsJIMININY MCP CONNECTORIn Dev( JY-2083310 11 .000 =0Fix foreign key violation when matching...activities to deleted Salesforce opportun...SUPPORT TICKETSin DerX* JY-209631 •000= 0Evaluation for Exec ReportsAUTOMATE EXEC REPORTS IN THE PRODUCTIIn Dey[ JY-1989010 / 0000=0User can log & save meeting information toRECORDING MEETINGS WITHOUT VISIBLE NOTETAKERS (.in DeyA JY-20753|13 •=0Call Scoring filter for Exec reportsAUTOMATID A CORINGin ner[ JY-20771•n •=0OA 2NSCP > Enable the Al to know detalis about theJIMINNY MCP CONNECTORIn QAA JY-2084612 1 •0 = 0oe eati uestr o got l st o odsandJIMINNY MCP CONNECTORIn QA[ JY-208355 n •=02 Shortcuts vSlack intearation$ Reporting Center +PO ACCEPTANCEComplete sprintDEPLOYGroup: Queries...
|
86403
|
NULL
|
NULL
|
NULL
|
|
86403
|
2963
|
12
|
2026-05-28T14:08:09.365448+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-28/1779 /Users/lukas/.screenpipe/data/data/2026-05-28/1779977289365_m2.jpg...
|
PhpStorm
|
faVsco.js – CrmActivityService.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
#12121 on JY-20963-fix-im Project: faVsco.js, menu
#12121 on JY-20963-fix-import-on-deleted-entity, menu
Start Listening for PHP Debug Connections
ServiceTest
Run 'ServiceTest'
Debug 'ServiceTest'...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"#12121 on JY-20963-fix-import-on-deleted-entity, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.11569149,"height":0.025538707},"on_screen":true,"help_text":"Pull request #12121 exists for current branch JY-20963-fix-import-on-deleted-entity, but pull request details loading failed","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.85638297,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"ServiceTest","depth":6,"bounds":{"left":0.87167555,"top":0.019952115,"width":0.043882977,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'ServiceTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'ServiceTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-5925516468550469094
|
-8643040614315223040
|
idle
|
hybrid
|
NULL
|
Project: faVsco.js, menu
#12121 on JY-20963-fix-im Project: faVsco.js, menu
#12121 on JY-20963-fix-import-on-deleted-entity, menu
Start Listening for PHP Debug Connections
ServiceTest
Run 'ServiceTest'
Debug 'ServiceTest'
rapstomViewCoocWindowFV faVsco.|s ~#12121 on JY-20963-fx-proidet© SyncFieldAction.php© SyncRelatedActivityManaswechooksyncsatchprosd> a IntegrationApdausteners> Metadata> (0 Migration> (0 Pipedrivev (i Salesforce>D Fields> OpportunityMalcher>bOpportunitySyncstrateg)› (D ProspectSearchStrateay> (D ServiceTraitsc) etencohoc DecorateAcuviy.ohou,oele cociecisttar ohoc) Field detinitions.oho© PavloadBuilder.ohcЗАЗЗЯЗАЗЗОЗЯВЕЕc Pronle ond© QueryBuilder.phpCTouertand er.onoCloueriterator.cho©QueryResuits.php© Service.phpcsunchatchher ccamoeo177itral tsl179© BaseClient.phpRaseSence ahoeCachedcrmSanicanacorato 10%@ CountryCodeResotver.php© CrmActivityProviderintegrate© CrmActivityService.pho© CrmConfigurationSettingsSer 164eermobiectsresoiver.onoCTItAntOAANAAC MANGINTASIEcmwllnciocr.on© FindsProspectinterface.phooyouwins cuono© MatchDomainByEmallinterfac 201cOpportunvacuvityMatcher© OpportunitySvncStrateavinte 205© OpportunitvSvncStrateavRes 20%© ProspectCache.oheProspectSearchScooe.oho© ProspectSearchStrateavFact, 207ProspectSearchStratecwinte© ProviderReoistry.ohe8 RecordSelactor.chdResolveGomnanyNameRVfmletrmeperoditeraroeooo>Mimnorto orainAYowarkctneimooted=custom.logSF [minny@localhost)coneaieemlsOAcUViy.on© Team,phpA console (EUTconsolA ISTAGING"imiamyclass cruacevzcyserviceprivate function updateParticipantsCrnData(A2ASAYAND a.created at > DATE SUB(NOWO, INTERVAL 38 DAY)GROUP RY u.sd urenaitl, u.nane. M. softahone nunberORDER BY sms count DESC045 A1 A41 У 66 ^CrySactivity->updateParticipantOrnData(Srecords, Sparticipant):} catch (Throwable Sex) aSthis->loggen-seccor(•[ConActävitySenyicel Fafled to update pantsicinant CRM (ao%select * from teans where 1d = 1select * fron rolesexcro.oon s>yexo200.4e90nCONCAT(u.1d, CASE WHEN u.id = t.owner id THEN• (onner)' ELSE ** END) AS user idcontsinue:t.oaner id FROM social_accounts sShestWatch= Sthiso>ae-BestwarchlJoiN users u on u.1d = sa.soczablc1d1.n<->1: on taid = u.tean_idnatchedRecords: SmatchedRecordsnatchedbonasinRecords: SmatchedionarinRecondshere urreanbldsae nd sarorouiden & unuasdorSELENFRONNOOMICTOSTHERE MUSGTOMML8874020-2012048109104-09:896858748 E WUSA: B79983459 1ESSthis->logger->info('[CrmActivityService) CRM matching completed'. [ISAtutu Aie CoNtutu aotAeTneeMieles hErE uis to mnnsspoeuoeeouueioetutomasisee wuidr88186y97NeSELECT * FROM crn_configurations WHERE id = 1053:HERE SOTIWYselect * fron'participants processed' => Sparticipants->counto'exact natches' a> count(SmatchedRecords)select & from playbooks where 1d = sunzselect * fron playbook_categories where id = 43783;donannacches => count shatcheobonankecorosselect * fron playbook_categories where playbook_id = 5473'best natch found' => ! empty(SbestMatch)select + Eron eon 6eld values where com Seid 3d = 650242)recurn SbeschacchhSSIEN + FROM Co 45e1d data 4.JOIN cra fields + ON fd.crn field id = f.id*JOIN activities a ON fd.activity id = a.10anivate functsion shouldPenforelookun(Pantsicáoant Santáicioant. Tean Stean): bool!...1# AND f.crn provider id = 'hs activity type':onivate function validatedceConfiquration(Actávity Sactivity): voidt....1 usage732733734735736private function getßestMatch(?array SnatchedRecords, ?array SnatchedDomainRecords): arrz 75%SELECT * FROM actávity_nessages;select * fron text_relays where created at > *2826-85-01°:select * fron activities whereuser_id IN (7168, 18688) and created at > *2826-85-22' order by id descaselect * fron users where tean_id = 1 and id IN (18688. 13934, 7160)=select * fron activities where user id = 7169 order by id desc Linit 10:return RecordSelector:.oickBestFronLists(S-atchedPeconds._SnatchedDonainReconds)739select * fron accounts where team_id = 1 and nane = 'ColumnS':select * fron usens where nane Like '%Subrax": # 31954. 1117private function findCrmRecords(Panticipant Sparticipant, Activity Sactivity): Zarray(.74aSellect *0activity sparches where usen 1d= 31954select + fron actiwity sparch &iltens where actiuity seanch ia TN (9998). BROR)) :private function shouldSkipParticipant(Participant Sparticipant): boolf...}Inu co woy tbo.uServiceTest+0.Cecsdales Orcnnworceoeionh1/ 0rosers/ tuxos/2Toreach (Sparticiponts as spariicipant)Srecords = Sthis-s1indCreRecords(Sparticipant, Sactivity):coly piccororrionter thhieotwt:wwinhecorddierSaruich. Corsemhce../l & This nay call Salesforce::matchByDomain()Step 7: Domain Search tnggers opportunity synoboriin senrth irs necoun ers drieee" withu relmired oncortuntt/l To get opportunity details, it calls:Scraservicesyncopponontylscrrrovzoerso):# couts aaporcopportunztyl)Step 8: The bua - importo pportunity creates temporary Recoroaond/Users/lukas/1m1nny/app/app/Services/crm/Salestorce/Serv1cc.php:1430-1448oriivate tunctionimoort@sportunt tylScr iatal: 20000rtuntvVxino early chelete chack8a. Restore fron trash (tine 1430)SthcesrestorAcyTrachwEnttylSthisescontoesomoortintnsol. Ser Dataletaenuny na sests wich valbld0io.l0ones34305833Sopportunity = SthswoWrtreucreonowloer1ooruaeolnoalasosaat RosborunitweseVADio la.or7hosy5yiumoor edusin thesid edine42sthices oort DocortuntyerFel Data (Ser Datar SorBtelds, Conoortunt tyestd)eM8ar Noh celeteit ansin itine 1444Sthis-shandle0bjectDeletion(Sopportunity, ScrnData):Sopportuntty=trashedIl Methad returs null, ButAat anuhientua"eooh*4 space...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
86402
|
2962
|
16
|
2026-05-28T14:08:05.457158+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-28/1779 /Users/lukas/.screenpipe/data/data/2026-05-28/1779977285457_m1.jpg...
|
PhpStorm
|
faVsco.js – CrmActivityService.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
#12121 on JY-20963-fix-im Project: faVsco.js, menu
#12121 on JY-20963-fix-import-on-deleted-entity, menu
Start Listening for PHP Debug Connections
ServiceTest
Run 'ServiceTest'
Debug 'ServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
22
5
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Crm;
use Illuminate\Support\Collection;
use Jiminny\Contracts\Repositories\TeamRepository;
use Jiminny\Contracts\Services\Crm\ServiceInterface;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Models\Account;
use Jiminny\Models\Activity;
use Jiminny\Models\Contact;
use Jiminny\Models\Lead;
use Jiminny\Models\Opportunity;
use Jiminny\Models\Participant;
use Jiminny\Models\Stage;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Services\ResolveTeamCrmConnection;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Psr\Log\LoggerInterface;
use Exception;
use Throwable;
class CrmActivityService
{
public function __construct(
private readonly TeamRepository $teamRepository,
private readonly CachedCrmServiceDecorator $decorator,
private readonly EmailHelper $emailHelper,
private readonly ResolveTeamCrmConnection $teamCrmResolver,
private readonly LoggerInterface $logger,
) {
}
/**
* Updates CRM data for an activity and its participants.
*
* NOTE: This method performs multiple database writes and should be called
* within a transaction by the caller to ensure atomicity.
*
* @param Activity $activity
* @param bool $remoteSearch
*
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception
*/
public function updateCrmData(
Activity $activity,
bool $remoteSearch = false,
): void {
$crmService = null;
$participants = $activity->getParticipants();
$team = $activity->getTeam();
$prospectSearchStrategy = ProspectSearchStrategyFactory::match($team);
if ($prospectSearchStrategy->ignoreCrmMatchData()) {
$this->logger->info('[CrmActivityService] Ignoring crm data because of prospect strategy', [
'activity_id' => $activity->getId(),
'strategy' => get_class($prospectSearchStrategy),
]);
return;
}
if ($remoteSearch) {
try {
$crmService = $this->teamCrmResolver->resolveForTeam($team);
} catch (SocialAccountTokenInvalidException) {
$this->logger->warning('[CrmActivityService] CRM token expired, falling back to local search', [
'activity_id' => $activity->getId(),
'team_id' => $team->getId(),
]);
}
}
$records = $this->updateParticipantsCrmData(
team: $team,
activity: $activity,
participants: $participants,
crmService: $crmService,
);
if (! empty($records)) {
$activity->updateActivityCrmData($records);
}
$activity->refresh();
}
/**
* @param Collection<Participant> $participants
*
* @throws Exception
*
* @return array{
* Lead|null,
* Account|null,
* Opportunity|null,
* Contact|null,
* Stage|null,
* string|null
*}|array{}
*/
private function updateParticipantsCrmData(
Team $team,
Activity $activity,
Collection $participants,
?ServiceInterface $crmService = null,
): array {
$matchedRecords = [];
$matchedDomainRecords = [];
$this->validateCrmConfiguration($activity);
$this->decorator->setConfiguration($activity->getCrm());
$this->decorator->setCrmService($crmService);
foreach ($participants as $participant) {
if ($this->shouldSkipParticipant($participant)) {
continue;
}
if (! $this->shouldPerformLookup($participant, $team)) {
$this->logger->info('[CrmActivityService] Email domain belongs to the team, skipping crm lookup', [
'activity_id' => $activity->getId(),
'team_id' => $team->getId(),
'email' => $participant->getEmailAddress(),
]);
$this->attachUserIfExists($participant, $team);
continue;
}
$records = $this->findCrmRecords($participant, $activity);
if (! empty($records)) {
$matchedRecords[] = $records;
} else {
$records = $this->findCrmDomainRecords(
crmService: $crmService,
participant: $participant,
activity: $activity,
);
if (! empty($records)) {
$matchedDomainRecords[] = $records;
}
}
if (empty($records)) {
continue;
}
try {
$activity->updateParticipantCrmData($records, $participant);
} catch (Throwable $ex) {
$this->logger->error('[CrmActivityService] Failed to update participant CRM data', [
'activity_id' => $activity->getId(),
'participant_id' => $participant->getId(),
'exception' => $ex->getMessage(),
]);
continue;
}
}
$bestMatch = $this->getBestMatch(
matchedRecords : $matchedRecords,
matchedDomainRecords: $matchedDomainRecords,
);
$this->logger->info('[CrmActivityService] CRM matching completed', [
'activity_id' => $activity->getId(),
'participants_processed' => $participants->count(),
'exact_matches' => count($matchedRecords),
'domain_matches' => count($matchedDomainRecords),
'best_match_found' => ! empty($bestMatch),
]);
return $bestMatch;
}
private function shouldPerformLookup(Participant $participant, Team $team): bool
{
if ($participant->hasEmailAddress()) {
return $this->emailHelper->shouldPerformLookup($team, $participant->getEmailAddress());
}
return true;
}
private function validateCrmConfiguration(Activity $activity): void
{
if ($activity->getCrm() === null) {
throw new InvalidArgumentException('Cannot find CRM configuration');
}
}
private function getBestMatch(?array $matchedRecords, ?array $matchedDomainRecords): array
{
return RecordSelector::pickBestFromLists($matchedRecords, $matchedDomainRecords);
}
private function findCrmRecords(Participant $participant, Activity $activity): ?array
{
$records = null;
if ($participant->hasEmailAddress()) {
$records = $this->decorator->matchExactlyByEmail(
email: $participant->getEmailAddress(),
userId: $activity->getUser()->getId()
);
}
if (empty($records) && $participant->getPhoneNumber() !== null) {
$records = $this->decorator->matchByPhone(
phone: $participant->getPhoneNumber(),
userId: $activity->getUser()->getId(),
);
}
if (empty($records) && $participant->getName() !== null) {
$records = $this->decorator->matchByName(
name: $participant->getName(),
userId: $activity->getUser()->getId(),
);
}
return $records;
}
private function shouldSkipParticipant(Participant $participant): bool
{
return $participant->hasUser();
}
private function attachUserIfExists(Participant $participant, Team $team): void
{
if ($participant->hasEmailAddress() === false) {
return;
}
$user = $this->teamRepository->findActiveTeamMemberByEmail($team, $participant->getEmailAddress());
if ($user instanceof User) {
$participant->user_id = $user->getId();
$participant->save();
}
}
private function findCrmDomainRecords(
?ServiceInterface $crmService,
Participant $participant,
Activity $activity,
): array {
if ($participant->hasEmailAddress()) {
$this->decorator->setConfiguration($activity->getCrm());
$this->decorator->setCrmService($crmService);
$records = $this->decorator->matchByDomain(
email: $participant->getEmailAddress(),
userId: $activity->getUser()->getId()
);
if (! empty($records)) {
return $records;
}
}
return [];
}
}
Execute
Explain Plan
Browse Query History
View Parameters
Open Query Execution Settings…
In-Editor Results
Tx: Auto
Cancel Running Statements
Playground
jiminny...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"#12121 on JY-20963-fix-import-on-deleted-entity, menu","depth":5,"on_screen":true,"help_text":"Pull request #12121 exists for current branch JY-20963-fix-import-on-deleted-entity, but pull request details loading failed","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"ServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'ServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'ServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"22","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"5","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Crm;\n\nuse Illuminate\\Support\\Collection;\nuse Jiminny\\Contracts\\Repositories\\TeamRepository;\nuse Jiminny\\Contracts\\Services\\Crm\\ServiceInterface;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Exceptions\\SocialAccountTokenInvalidException;\nuse Jiminny\\Models\\Account;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Contact;\nuse Jiminny\\Models\\Lead;\nuse Jiminny\\Models\\Opportunity;\nuse Jiminny\\Models\\Participant;\nuse Jiminny\\Models\\Stage;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\ResolveTeamCrmConnection;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Psr\\Log\\LoggerInterface;\nuse Exception;\nuse Throwable;\n\nclass CrmActivityService\n{\n public function __construct(\n private readonly TeamRepository $teamRepository,\n private readonly CachedCrmServiceDecorator $decorator,\n private readonly EmailHelper $emailHelper,\n private readonly ResolveTeamCrmConnection $teamCrmResolver,\n private readonly LoggerInterface $logger,\n ) {\n }\n\n /**\n * Updates CRM data for an activity and its participants.\n *\n * NOTE: This method performs multiple database writes and should be called\n * within a transaction by the caller to ensure atomicity.\n *\n * @param Activity $activity\n * @param bool $remoteSearch\n *\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception\n */\n public function updateCrmData(\n Activity $activity,\n bool $remoteSearch = false,\n ): void {\n $crmService = null;\n $participants = $activity->getParticipants();\n $team = $activity->getTeam();\n\n $prospectSearchStrategy = ProspectSearchStrategyFactory::match($team);\n if ($prospectSearchStrategy->ignoreCrmMatchData()) {\n $this->logger->info('[CrmActivityService] Ignoring crm data because of prospect strategy', [\n 'activity_id' => $activity->getId(),\n 'strategy' => get_class($prospectSearchStrategy),\n ]);\n\n return;\n }\n\n if ($remoteSearch) {\n try {\n $crmService = $this->teamCrmResolver->resolveForTeam($team);\n } catch (SocialAccountTokenInvalidException) {\n $this->logger->warning('[CrmActivityService] CRM token expired, falling back to local search', [\n 'activity_id' => $activity->getId(),\n 'team_id' => $team->getId(),\n ]);\n }\n }\n\n $records = $this->updateParticipantsCrmData(\n team: $team,\n activity: $activity,\n participants: $participants,\n crmService: $crmService,\n );\n\n if (! empty($records)) {\n $activity->updateActivityCrmData($records);\n }\n\n $activity->refresh();\n }\n\n /**\n * @param Collection<Participant> $participants\n *\n * @throws Exception\n *\n * @return array{\n * Lead|null,\n * Account|null,\n * Opportunity|null,\n * Contact|null,\n * Stage|null,\n * string|null\n *}|array{}\n */\n private function updateParticipantsCrmData(\n Team $team,\n Activity $activity,\n Collection $participants,\n ?ServiceInterface $crmService = null,\n ): array {\n $matchedRecords = [];\n $matchedDomainRecords = [];\n\n $this->validateCrmConfiguration($activity);\n $this->decorator->setConfiguration($activity->getCrm());\n $this->decorator->setCrmService($crmService);\n\n foreach ($participants as $participant) {\n if ($this->shouldSkipParticipant($participant)) {\n continue;\n }\n\n if (! $this->shouldPerformLookup($participant, $team)) {\n $this->logger->info('[CrmActivityService] Email domain belongs to the team, skipping crm lookup', [\n 'activity_id' => $activity->getId(),\n 'team_id' => $team->getId(),\n 'email' => $participant->getEmailAddress(),\n ]);\n\n $this->attachUserIfExists($participant, $team);\n\n continue;\n }\n\n $records = $this->findCrmRecords($participant, $activity);\n\n if (! empty($records)) {\n $matchedRecords[] = $records;\n } else {\n $records = $this->findCrmDomainRecords(\n crmService: $crmService,\n participant: $participant,\n activity: $activity,\n );\n if (! empty($records)) {\n $matchedDomainRecords[] = $records;\n }\n }\n\n if (empty($records)) {\n continue;\n }\n\n try {\n $activity->updateParticipantCrmData($records, $participant);\n } catch (Throwable $ex) {\n $this->logger->error('[CrmActivityService] Failed to update participant CRM data', [\n 'activity_id' => $activity->getId(),\n 'participant_id' => $participant->getId(),\n 'exception' => $ex->getMessage(),\n ]);\n\n continue;\n }\n }\n\n $bestMatch = $this->getBestMatch(\n matchedRecords : $matchedRecords,\n matchedDomainRecords: $matchedDomainRecords,\n );\n\n $this->logger->info('[CrmActivityService] CRM matching completed', [\n 'activity_id' => $activity->getId(),\n 'participants_processed' => $participants->count(),\n 'exact_matches' => count($matchedRecords),\n 'domain_matches' => count($matchedDomainRecords),\n 'best_match_found' => ! empty($bestMatch),\n ]);\n\n return $bestMatch;\n }\n\n private function shouldPerformLookup(Participant $participant, Team $team): bool\n {\n if ($participant->hasEmailAddress()) {\n return $this->emailHelper->shouldPerformLookup($team, $participant->getEmailAddress());\n }\n\n return true;\n }\n\n private function validateCrmConfiguration(Activity $activity): void\n {\n if ($activity->getCrm() === null) {\n throw new InvalidArgumentException('Cannot find CRM configuration');\n }\n }\n\n private function getBestMatch(?array $matchedRecords, ?array $matchedDomainRecords): array\n {\n return RecordSelector::pickBestFromLists($matchedRecords, $matchedDomainRecords);\n }\n\n private function findCrmRecords(Participant $participant, Activity $activity): ?array\n {\n $records = null;\n\n if ($participant->hasEmailAddress()) {\n $records = $this->decorator->matchExactlyByEmail(\n email: $participant->getEmailAddress(),\n userId: $activity->getUser()->getId()\n );\n }\n\n if (empty($records) && $participant->getPhoneNumber() !== null) {\n $records = $this->decorator->matchByPhone(\n phone: $participant->getPhoneNumber(),\n userId: $activity->getUser()->getId(),\n );\n }\n\n if (empty($records) && $participant->getName() !== null) {\n $records = $this->decorator->matchByName(\n name: $participant->getName(),\n userId: $activity->getUser()->getId(),\n );\n }\n\n return $records;\n }\n\n private function shouldSkipParticipant(Participant $participant): bool\n {\n return $participant->hasUser();\n }\n\n private function attachUserIfExists(Participant $participant, Team $team): void\n {\n if ($participant->hasEmailAddress() === false) {\n return;\n }\n\n $user = $this->teamRepository->findActiveTeamMemberByEmail($team, $participant->getEmailAddress());\n\n if ($user instanceof User) {\n $participant->user_id = $user->getId();\n $participant->save();\n }\n }\n\n private function findCrmDomainRecords(\n ?ServiceInterface $crmService,\n Participant $participant,\n Activity $activity,\n ): array {\n if ($participant->hasEmailAddress()) {\n $this->decorator->setConfiguration($activity->getCrm());\n $this->decorator->setCrmService($crmService);\n\n $records = $this->decorator->matchByDomain(\n email: $participant->getEmailAddress(),\n userId: $activity->getUser()->getId()\n );\n if (! empty($records)) {\n return $records;\n }\n }\n\n return [];\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Crm;\n\nuse Illuminate\\Support\\Collection;\nuse Jiminny\\Contracts\\Repositories\\TeamRepository;\nuse Jiminny\\Contracts\\Services\\Crm\\ServiceInterface;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Exceptions\\SocialAccountTokenInvalidException;\nuse Jiminny\\Models\\Account;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Contact;\nuse Jiminny\\Models\\Lead;\nuse Jiminny\\Models\\Opportunity;\nuse Jiminny\\Models\\Participant;\nuse Jiminny\\Models\\Stage;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\ResolveTeamCrmConnection;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Psr\\Log\\LoggerInterface;\nuse Exception;\nuse Throwable;\n\nclass CrmActivityService\n{\n public function __construct(\n private readonly TeamRepository $teamRepository,\n private readonly CachedCrmServiceDecorator $decorator,\n private readonly EmailHelper $emailHelper,\n private readonly ResolveTeamCrmConnection $teamCrmResolver,\n private readonly LoggerInterface $logger,\n ) {\n }\n\n /**\n * Updates CRM data for an activity and its participants.\n *\n * NOTE: This method performs multiple database writes and should be called\n * within a transaction by the caller to ensure atomicity.\n *\n * @param Activity $activity\n * @param bool $remoteSearch\n *\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception\n */\n public function updateCrmData(\n Activity $activity,\n bool $remoteSearch = false,\n ): void {\n $crmService = null;\n $participants = $activity->getParticipants();\n $team = $activity->getTeam();\n\n $prospectSearchStrategy = ProspectSearchStrategyFactory::match($team);\n if ($prospectSearchStrategy->ignoreCrmMatchData()) {\n $this->logger->info('[CrmActivityService] Ignoring crm data because of prospect strategy', [\n 'activity_id' => $activity->getId(),\n 'strategy' => get_class($prospectSearchStrategy),\n ]);\n\n return;\n }\n\n if ($remoteSearch) {\n try {\n $crmService = $this->teamCrmResolver->resolveForTeam($team);\n } catch (SocialAccountTokenInvalidException) {\n $this->logger->warning('[CrmActivityService] CRM token expired, falling back to local search', [\n 'activity_id' => $activity->getId(),\n 'team_id' => $team->getId(),\n ]);\n }\n }\n\n $records = $this->updateParticipantsCrmData(\n team: $team,\n activity: $activity,\n participants: $participants,\n crmService: $crmService,\n );\n\n if (! empty($records)) {\n $activity->updateActivityCrmData($records);\n }\n\n $activity->refresh();\n }\n\n /**\n * @param Collection<Participant> $participants\n *\n * @throws Exception\n *\n * @return array{\n * Lead|null,\n * Account|null,\n * Opportunity|null,\n * Contact|null,\n * Stage|null,\n * string|null\n *}|array{}\n */\n private function updateParticipantsCrmData(\n Team $team,\n Activity $activity,\n Collection $participants,\n ?ServiceInterface $crmService = null,\n ): array {\n $matchedRecords = [];\n $matchedDomainRecords = [];\n\n $this->validateCrmConfiguration($activity);\n $this->decorator->setConfiguration($activity->getCrm());\n $this->decorator->setCrmService($crmService);\n\n foreach ($participants as $participant) {\n if ($this->shouldSkipParticipant($participant)) {\n continue;\n }\n\n if (! $this->shouldPerformLookup($participant, $team)) {\n $this->logger->info('[CrmActivityService] Email domain belongs to the team, skipping crm lookup', [\n 'activity_id' => $activity->getId(),\n 'team_id' => $team->getId(),\n 'email' => $participant->getEmailAddress(),\n ]);\n\n $this->attachUserIfExists($participant, $team);\n\n continue;\n }\n\n $records = $this->findCrmRecords($participant, $activity);\n\n if (! empty($records)) {\n $matchedRecords[] = $records;\n } else {\n $records = $this->findCrmDomainRecords(\n crmService: $crmService,\n participant: $participant,\n activity: $activity,\n );\n if (! empty($records)) {\n $matchedDomainRecords[] = $records;\n }\n }\n\n if (empty($records)) {\n continue;\n }\n\n try {\n $activity->updateParticipantCrmData($records, $participant);\n } catch (Throwable $ex) {\n $this->logger->error('[CrmActivityService] Failed to update participant CRM data', [\n 'activity_id' => $activity->getId(),\n 'participant_id' => $participant->getId(),\n 'exception' => $ex->getMessage(),\n ]);\n\n continue;\n }\n }\n\n $bestMatch = $this->getBestMatch(\n matchedRecords : $matchedRecords,\n matchedDomainRecords: $matchedDomainRecords,\n );\n\n $this->logger->info('[CrmActivityService] CRM matching completed', [\n 'activity_id' => $activity->getId(),\n 'participants_processed' => $participants->count(),\n 'exact_matches' => count($matchedRecords),\n 'domain_matches' => count($matchedDomainRecords),\n 'best_match_found' => ! empty($bestMatch),\n ]);\n\n return $bestMatch;\n }\n\n private function shouldPerformLookup(Participant $participant, Team $team): bool\n {\n if ($participant->hasEmailAddress()) {\n return $this->emailHelper->shouldPerformLookup($team, $participant->getEmailAddress());\n }\n\n return true;\n }\n\n private function validateCrmConfiguration(Activity $activity): void\n {\n if ($activity->getCrm() === null) {\n throw new InvalidArgumentException('Cannot find CRM configuration');\n }\n }\n\n private function getBestMatch(?array $matchedRecords, ?array $matchedDomainRecords): array\n {\n return RecordSelector::pickBestFromLists($matchedRecords, $matchedDomainRecords);\n }\n\n private function findCrmRecords(Participant $participant, Activity $activity): ?array\n {\n $records = null;\n\n if ($participant->hasEmailAddress()) {\n $records = $this->decorator->matchExactlyByEmail(\n email: $participant->getEmailAddress(),\n userId: $activity->getUser()->getId()\n );\n }\n\n if (empty($records) && $participant->getPhoneNumber() !== null) {\n $records = $this->decorator->matchByPhone(\n phone: $participant->getPhoneNumber(),\n userId: $activity->getUser()->getId(),\n );\n }\n\n if (empty($records) && $participant->getName() !== null) {\n $records = $this->decorator->matchByName(\n name: $participant->getName(),\n userId: $activity->getUser()->getId(),\n );\n }\n\n return $records;\n }\n\n private function shouldSkipParticipant(Participant $participant): bool\n {\n return $participant->hasUser();\n }\n\n private function attachUserIfExists(Participant $participant, Team $team): void\n {\n if ($participant->hasEmailAddress() === false) {\n return;\n }\n\n $user = $this->teamRepository->findActiveTeamMemberByEmail($team, $participant->getEmailAddress());\n\n if ($user instanceof User) {\n $participant->user_id = $user->getId();\n $participant->save();\n }\n }\n\n private function findCrmDomainRecords(\n ?ServiceInterface $crmService,\n Participant $participant,\n Activity $activity,\n ): array {\n if ($participant->hasEmailAddress()) {\n $this->decorator->setConfiguration($activity->getCrm());\n $this->decorator->setCrmService($crmService);\n\n $records = $this->decorator->matchByDomain(\n email: $participant->getEmailAddress(),\n userId: $activity->getUser()->getId()\n );\n if (! empty($records)) {\n return $records;\n }\n }\n\n return [];\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Execute","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Explain Plan","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Browse Query History","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"View Parameters","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Open Query Execution Settings…","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"In-Editor Results","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Tx: Auto","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Cancel Running Statements","depth":4,"on_screen":true,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Playground","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"jiminny","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
1896896393866459793
|
-8382375122341512834
|
visual_change
|
accessibility
|
NULL
|
Project: faVsco.js, menu
#12121 on JY-20963-fix-im Project: faVsco.js, menu
#12121 on JY-20963-fix-import-on-deleted-entity, menu
Start Listening for PHP Debug Connections
ServiceTest
Run 'ServiceTest'
Debug 'ServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
22
5
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Crm;
use Illuminate\Support\Collection;
use Jiminny\Contracts\Repositories\TeamRepository;
use Jiminny\Contracts\Services\Crm\ServiceInterface;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Models\Account;
use Jiminny\Models\Activity;
use Jiminny\Models\Contact;
use Jiminny\Models\Lead;
use Jiminny\Models\Opportunity;
use Jiminny\Models\Participant;
use Jiminny\Models\Stage;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Services\ResolveTeamCrmConnection;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Psr\Log\LoggerInterface;
use Exception;
use Throwable;
class CrmActivityService
{
public function __construct(
private readonly TeamRepository $teamRepository,
private readonly CachedCrmServiceDecorator $decorator,
private readonly EmailHelper $emailHelper,
private readonly ResolveTeamCrmConnection $teamCrmResolver,
private readonly LoggerInterface $logger,
) {
}
/**
* Updates CRM data for an activity and its participants.
*
* NOTE: This method performs multiple database writes and should be called
* within a transaction by the caller to ensure atomicity.
*
* @param Activity $activity
* @param bool $remoteSearch
*
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception
*/
public function updateCrmData(
Activity $activity,
bool $remoteSearch = false,
): void {
$crmService = null;
$participants = $activity->getParticipants();
$team = $activity->getTeam();
$prospectSearchStrategy = ProspectSearchStrategyFactory::match($team);
if ($prospectSearchStrategy->ignoreCrmMatchData()) {
$this->logger->info('[CrmActivityService] Ignoring crm data because of prospect strategy', [
'activity_id' => $activity->getId(),
'strategy' => get_class($prospectSearchStrategy),
]);
return;
}
if ($remoteSearch) {
try {
$crmService = $this->teamCrmResolver->resolveForTeam($team);
} catch (SocialAccountTokenInvalidException) {
$this->logger->warning('[CrmActivityService] CRM token expired, falling back to local search', [
'activity_id' => $activity->getId(),
'team_id' => $team->getId(),
]);
}
}
$records = $this->updateParticipantsCrmData(
team: $team,
activity: $activity,
participants: $participants,
crmService: $crmService,
);
if (! empty($records)) {
$activity->updateActivityCrmData($records);
}
$activity->refresh();
}
/**
* @param Collection<Participant> $participants
*
* @throws Exception
*
* @return array{
* Lead|null,
* Account|null,
* Opportunity|null,
* Contact|null,
* Stage|null,
* string|null
*}|array{}
*/
private function updateParticipantsCrmData(
Team $team,
Activity $activity,
Collection $participants,
?ServiceInterface $crmService = null,
): array {
$matchedRecords = [];
$matchedDomainRecords = [];
$this->validateCrmConfiguration($activity);
$this->decorator->setConfiguration($activity->getCrm());
$this->decorator->setCrmService($crmService);
foreach ($participants as $participant) {
if ($this->shouldSkipParticipant($participant)) {
continue;
}
if (! $this->shouldPerformLookup($participant, $team)) {
$this->logger->info('[CrmActivityService] Email domain belongs to the team, skipping crm lookup', [
'activity_id' => $activity->getId(),
'team_id' => $team->getId(),
'email' => $participant->getEmailAddress(),
]);
$this->attachUserIfExists($participant, $team);
continue;
}
$records = $this->findCrmRecords($participant, $activity);
if (! empty($records)) {
$matchedRecords[] = $records;
} else {
$records = $this->findCrmDomainRecords(
crmService: $crmService,
participant: $participant,
activity: $activity,
);
if (! empty($records)) {
$matchedDomainRecords[] = $records;
}
}
if (empty($records)) {
continue;
}
try {
$activity->updateParticipantCrmData($records, $participant);
} catch (Throwable $ex) {
$this->logger->error('[CrmActivityService] Failed to update participant CRM data', [
'activity_id' => $activity->getId(),
'participant_id' => $participant->getId(),
'exception' => $ex->getMessage(),
]);
continue;
}
}
$bestMatch = $this->getBestMatch(
matchedRecords : $matchedRecords,
matchedDomainRecords: $matchedDomainRecords,
);
$this->logger->info('[CrmActivityService] CRM matching completed', [
'activity_id' => $activity->getId(),
'participants_processed' => $participants->count(),
'exact_matches' => count($matchedRecords),
'domain_matches' => count($matchedDomainRecords),
'best_match_found' => ! empty($bestMatch),
]);
return $bestMatch;
}
private function shouldPerformLookup(Participant $participant, Team $team): bool
{
if ($participant->hasEmailAddress()) {
return $this->emailHelper->shouldPerformLookup($team, $participant->getEmailAddress());
}
return true;
}
private function validateCrmConfiguration(Activity $activity): void
{
if ($activity->getCrm() === null) {
throw new InvalidArgumentException('Cannot find CRM configuration');
}
}
private function getBestMatch(?array $matchedRecords, ?array $matchedDomainRecords): array
{
return RecordSelector::pickBestFromLists($matchedRecords, $matchedDomainRecords);
}
private function findCrmRecords(Participant $participant, Activity $activity): ?array
{
$records = null;
if ($participant->hasEmailAddress()) {
$records = $this->decorator->matchExactlyByEmail(
email: $participant->getEmailAddress(),
userId: $activity->getUser()->getId()
);
}
if (empty($records) && $participant->getPhoneNumber() !== null) {
$records = $this->decorator->matchByPhone(
phone: $participant->getPhoneNumber(),
userId: $activity->getUser()->getId(),
);
}
if (empty($records) && $participant->getName() !== null) {
$records = $this->decorator->matchByName(
name: $participant->getName(),
userId: $activity->getUser()->getId(),
);
}
return $records;
}
private function shouldSkipParticipant(Participant $participant): bool
{
return $participant->hasUser();
}
private function attachUserIfExists(Participant $participant, Team $team): void
{
if ($participant->hasEmailAddress() === false) {
return;
}
$user = $this->teamRepository->findActiveTeamMemberByEmail($team, $participant->getEmailAddress());
if ($user instanceof User) {
$participant->user_id = $user->getId();
$participant->save();
}
}
private function findCrmDomainRecords(
?ServiceInterface $crmService,
Participant $participant,
Activity $activity,
): array {
if ($participant->hasEmailAddress()) {
$this->decorator->setConfiguration($activity->getCrm());
$this->decorator->setCrmService($crmService);
$records = $this->decorator->matchByDomain(
email: $participant->getEmailAddress(),
userId: $activity->getUser()->getId()
);
if (! empty($records)) {
return $records;
}
}
return [];
}
}
Execute
Explain Plan
Browse Query History
View Parameters
Open Query Execution Settings…
In-Editor Results
Tx: Auto
Cancel Running Statements
Playground
jiminny...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
86401
|
2962
|
15
|
2026-05-28T14:08:02.305642+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-28/1779 /Users/lukas/.screenpipe/data/data/2026-05-28/1779977282305_m1.jpg...
|
PhpStorm
|
faVsco.js – CrmActivityService.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
8043719072324535154
|
-8628527368849355612
|
visual_change
|
hybrid
|
NULL
|
Project: faVsco.js, menu
Activity MonitorFileEditV Project: faVsco.js, menu
Activity MonitorFileEditViewWindowHelpActivity MonitorAll ProcessesProcess Name% CPUPhpStormkernel_taskVirtual Machine Service for DockerreplaydWindowServerscreenpipeFirefoxCP Isolated Web Contentmds_storesiTerm2coreaudioddeletedSlack HelperSlack Helper (Renderer)Activity Monitortccdlanguage_server_macos_armWispr Flow Helper (Renderer)FirefoxCP Isolated Web ContentsyspolicydWispr FlowNotion Helper (Renderer)DockertrustdFirefoxCP Isolated Web ContenttrustdControl CentreClaude292,4172,6101,064,021,219,89,47,66,35,85,04,93,63,33,13,02,92,32,32,01,71,51,51,41,31,31,3io.kandji.KandjiAgent.ESF-Extension1,2CPU Time2:34:22,0122:48:56,591:53:09,845:42:32,297:50:33,113:35:03,6533:43,831:03:37,191:03:31,1657:03,721:24,4014:26,7957:26,526:48,368:36,657:31,203:46,3815:14,2916:32,682:36,9523:57,511:26,7514:22,9323:22,045:03,1919:47,2728:22,6531:21,48System:User:Idle:Threads % C8• Thu 28 May 17:08:02CPUMemoryEnergyDiskNetworkIdle Wake-UpsKind% GPU100Apple AppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleApple8,30,00,00,033,90,00,00,00,30,00,03,50,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,049,07%50,82%0,12%CPU LOADThreads:Processes:SearchGPU Time7:48,700,000,000,003:20:11,065:03,900,000,0019,420,000,006:07,100,000,000,000,000,000,000,000,000,000,000,000,000,000,040,000,00PID alslUserlukasrootlukaslukas_windowserverlukaslukasrootlukas_coreaudiod...
|
86400
|
NULL
|
NULL
|
NULL
|
|
86399
|
2963
|
11
|
2026-05-28T14:07:38.876180+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-28/1779 /Users/lukas/.screenpipe/data/data/2026-05-28/1779977258876_m2.jpg...
|
PhpStorm
|
faVsco.js – CrmActivityService.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
#12121 on JY-20963-fix-im Project: faVsco.js, menu
#12121 on JY-20963-fix-import-on-deleted-entity, menu
Start Listening for PHP Debug Connections
ServiceTest
Run 'ServiceTest'
Debug 'ServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
22
5
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Crm;
use Illuminate\Support\Collection;
use Jiminny\Contracts\Repositories\TeamRepository;
use Jiminny\Contracts\Services\Crm\ServiceInterface;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Models\Account;
use Jiminny\Models\Activity;
use Jiminny\Models\Contact;
use Jiminny\Models\Lead;
use Jiminny\Models\Opportunity;
use Jiminny\Models\Participant;
use Jiminny\Models\Stage;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Services\ResolveTeamCrmConnection;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Psr\Log\LoggerInterface;
use Exception;
use Throwable;
class CrmActivityService
{
public function __construct(
private readonly TeamRepository $teamRepository,
private readonly CachedCrmServiceDecorator $decorator,
private readonly EmailHelper $emailHelper,
private readonly ResolveTeamCrmConnection $teamCrmResolver,
private readonly LoggerInterface $logger,
) {
}
/**
* Updates CRM data for an activity and its participants.
*
* NOTE: This method performs multiple database writes and should be called
* within a transaction by the caller to ensure atomicity.
*
* @param Activity $activity
* @param bool $remoteSearch
*
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception
*/
public function updateCrmData(
Activity $activity,
bool $remoteSearch = false,
): void {
$crmService = null;
$participants = $activity->getParticipants();
$team = $activity->getTeam();
$prospectSearchStrategy = ProspectSearchStrategyFactory::match($team);
if ($prospectSearchStrategy->ignoreCrmMatchData()) {
$this->logger->info('[CrmActivityService] Ignoring crm data because of prospect strategy', [
'activity_id' => $activity->getId(),
'strategy' => get_class($prospectSearchStrategy),
]);
return;
}
if ($remoteSearch) {
try {
$crmService = $this->teamCrmResolver->resolveForTeam($team);
} catch (SocialAccountTokenInvalidException) {
$this->logger->warning('[CrmActivityService] CRM token expired, falling back to local search', [
'activity_id' => $activity->getId(),
'team_id' => $team->getId(),
]);
}
}
$records = $this->updateParticipantsCrmData(
team: $team,
activity: $activity,
participants: $participants,
crmService: $crmService,
);
if (! empty($records)) {
$activity->updateActivityCrmData($records);
}
$activity->refresh();
}
/**
* @param Collection<Participant> $participants
*
* @throws Exception
*
* @return array{
* Lead|null,
* Account|null,
* Opportunity|null,
* Contact|null,
* Stage|null,
* string|null
*}|array{}
*/
private function updateParticipantsCrmData(
Team $team,
Activity $activity,
Collection $participants,
?ServiceInterface $crmService = null,
): array {
$matchedRecords = [];
$matchedDomainRecords = [];
$this->validateCrmConfiguration($activity);
$this->decorator->setConfiguration($activity->getCrm());
$this->decorator->setCrmService($crmService);
foreach ($participants as $participant) {
if ($this->shouldSkipParticipant($participant)) {
continue;
}
if (! $this->shouldPerformLookup($participant, $team)) {
$this->logger->info('[CrmActivityService] Email domain belongs to the team, skipping crm lookup', [
'activity_id' => $activity->getId(),
'team_id' => $team->getId(),
'email' => $participant->getEmailAddress(),
]);
$this->attachUserIfExists($participant, $team);
continue;
}
$records = $this->findCrmRecords($participant, $activity);
if (! empty($records)) {
$matchedRecords[] = $records;
} else {
$records = $this->findCrmDomainRecords(
crmService: $crmService,
participant: $participant,
activity: $activity,
);
if (! empty($records)) {
$matchedDomainRecords[] = $records;
}
}
if (empty($records)) {
continue;
}
try {
$activity->updateParticipantCrmData($records, $participant);
} catch (Throwable $ex) {
$this->logger->error('[CrmActivityService] Failed to update participant CRM data', [
'activity_id' => $activity->getId(),
'participant_id' => $participant->getId(),
'exception' => $ex->getMessage(),
]);
continue;
}
}
$bestMatch = $this->getBestMatch(
matchedRecords : $matchedRecords,
matchedDomainRecords: $matchedDomainRecords,
);
$this->logger->info('[CrmActivityService] CRM matching completed', [
'activity_id' => $activity->getId(),
'participants_processed' => $participants->count(),
'exact_matches' => count($matchedRecords),
'domain_matches' => count($matchedDomainRecords),
'best_match_found' => ! empty($bestMatch),
]);
return $bestMatch;
}
private function shouldPerformLookup(Participant $participant, Team $team): bool
{
if ($participant->hasEmailAddress()) {
return $this->emailHelper->shouldPerformLookup($team, $participant->getEmailAddress());
}
return true;
}
private function validateCrmConfiguration(Activity $activity): void
{
if ($activity->getCrm() === null) {
throw new InvalidArgumentException('Cannot find CRM configuration');
}
}
private function getBestMatch(?array $matchedRecords, ?array $matchedDomainRecords): array
{
return RecordSelector::pickBestFromLists($matchedRecords, $matchedDomainRecords);
}
private function findCrmRecords(Participant $participant, Activity $activity): ?array
{
$records = null;
if ($participant->hasEmailAddress()) {
$records = $this->decorator->matchExactlyByEmail(
email: $participant->getEmailAddress(),
userId: $activity->getUser()->getId()
);
}
if (empty($records) && $participant->getPhoneNumber() !== null) {
$records = $this->decorator->matchByPhone(
phone: $participant->getPhoneNumber(),
userId: $activity->getUser()->getId(),
);
}
if (empty($records) && $participant->getName() !== null) {
$records = $this->decorator->matchByName(
name: $participant->getName(),
userId: $activity->getUser()->getId(),
);
}
return $records;
}
private function shouldSkipParticipant(Participant $participant): bool
{
return $participant->hasUser();
}
private function attachUserIfExists(Participant $participant, Team $team): void
{
if ($participant->hasEmailAddress() === false) {
return;
}
$user = $this->teamRepository->findActiveTeamMemberByEmail($team, $participant->getEmailAddress());
if ($user instanceof User) {
$participant->user_id = $user->getId();
$participant->save();
}
}
private function findCrmDomainRecords(
?ServiceInterface $crmService,
Participant $participant,
Activity $activity,
): array {
if ($participant->hasEmailAddress()) {
$this->decorator->setConfiguration($activity->getCrm());
$this->decorator->setCrmService($crmService);
$records = $this->decorator->matchByDomain(
email: $participant->getEmailAddress(),
userId: $activity->getUser()->getId()
);
if (! empty($records)) {
return $records;
}
}
return [];
}
}
Execute
Explain Plan
Browse Query History
View Parameters
Open Query Execution Settings…
In-Editor Results
Tx: Auto
Cancel Running Statements
Playground
jiminny
Sync Changes
Hide This Notification
Code changed:
Hide
45
1
41
66
Previous Highlighted Error
Next Highlighted Error
SELECT * FROM teams WHERE name LIKE '%litify%'; # 1069, 994, 24993
SELECT * FROM users WHERE id = 25061;
SELECT * FROM crm_profiles WHERE crm_configuration_id = 994;
SELECT * FROM crm_profiles WHERE user_id = 25061;
select * from crm_configurations where id = 834;
SELECT * FROM teams WHERE id = 882;
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 882 and sa.provider = 'hubspot';
SELECT * FROM crm_configurations where id = 882; # 933 - GoGlobal
SELECT * FROM opportunities WHERE team_id = 933 order by updated_at desc;
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 933 and sa.provider = 'hubspot';
SELECT * FROM crm_configurations WHERE provider = 'hubspot' and crm_provider_id = 7270388;
SELECT * FROM contacts where crm_configuration_id = 834;
SELECT * FROM opportunities WHERE team_id = 933
# AND crm_provider_id IN ('20131586060','46017317898','52543911090','53451356564','54101251892','54323768459');
AND id IN (8482561,18352941,19042734,19232139,19445140,19472541);
SELECT * FROM opportunity_contacts
WHERE opportunity_id IN (8482561,18352941,19042734,19232139,19445140,19472541);
# [PASSWORD_DOTS]
SELECT * FROM crm_configurations where id = 485; #
SELECT * FROM opportunities WHERE team_id = 933 order by updated_at desc;
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 933 and sa.provider = 'hubspot';
select crm.provider, l.* from leads l join crm_configurations crm on l.crm_configuration_id = crm.id
where crm.provider NOT IN ('salesforce', 'integration-app', 'bullhorn', 'copper')
# and l.converted_at IS NOT NULL
;
# [PASSWORD_DOTS]
SELECT * FROM activities a WHERE type IN ('email-inbound', 'email-outbound')
and opportunity_id IS NULL
order by id desc;
SELECT * FROM teams WHERE id = 604; # 598
SELECT * FROM activities WHERE id = 74410828; # [EMAIL]
SELECT * FROM accounts WHERE id = 20068382;
SELECT * FROM accounts WHERE id = 35186038;
SELECT * FROM contacts WHERE team_id = 852 and updated_at > '2026-01-23 12:30:00' order by updated_at desc;
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 559 and sa.provider = 'hubspot';
SELECT * FROM activities WHERE uuid_to_bin('cb6342b6-a183-401c-b0af-ede92b2ae763') = uuid;
select * from sidekick_settings where team_id = 781;
SELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id = 26651871; # Teya
SELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id = 7562435;
SELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id = 8420347; # opflit 2100
SELECT * FROM crm_layouts WHERE crm_configuration_id = 711;
SELECT * FROM activities where crm_configuration_id = 711 and crm_provider_id IS NULL
and is_internal = 0 and status = 'completed'
order by id desc;
SELECT * FROM crm_layout_entities
WHERE crm_layout_id IN (2352, 2353);
;
SELECT * FROM crm_configurations where provider = 'hubspot' and id = 530;
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 556 and sa.provider = 'hubspot';
SELECT * FROM activities WHERE uuid_to_bin('c6ca4b22-7738-4563-a95d-b8a9598924ae') = uuid;
SELECT * FROM activities WHERE uuid_to_bin('442abb2b-28bd-4be8-9c25-19e9bf02766d') = uuid;
select * from contacts
where crm_configuration_id = 530
and crm_provider_id = 872252;
select * from activities where crm_configuration_id = 530
and user_id = 14343 and type like '%softphone%'
and created_at between '2026-01-28 15:00:00' and '2026-01-28 15:10:00';
SELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id = 25666868; # Teya
SELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id = 8646335; # Teya
SELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id IN (5933397);
SELECT t.name, t.id, t.owner_id, c.id, c.provider, c.crm_base_url FROM teams t
JOIN crm_configurations c ON t.id = c.team_id
WHERE t.status = 'active';
SELECT * FROM teams where id = 1091;
SELECT * FROM crm_configurations where team_id = 1091;
SELECT * FROM activity_providers where team_id = 1091;
SELECT * FROM activities where crm_configuration_id = 1024 and type IN ('softphone', 'softphone-outbound')
and provider NOT IN ('hubspot', 'aircall')
# and telephony_provider_id = '019c1131-a22f-4792-b9ea-20adf6a02ed0'
order by id desc;
SELECT * FROM teams WHERE name LIKE '%Leadventure%';
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 1091 and sa.provider = 'salesforce';
SELECT * FROM teams WHERE name LIKE '%Wilson%'; # 862, 812
SELECT * FROM teams where id = 862;
SELECT * FROM crm_configurations where team_id = 862;
SELECT * FROM activity_providers where team_id = 862;
SELECT * FROM activities where crm_configuration_id = 812 and type IN ('softphone', 'softphone-outbound')
and provider NOT IN ('hubspot', 'aircall')
# and telephony_provider_id = '019c1131-a22f-4792-b9ea-20adf6a02ed0'
order by id desc;
SELECT t.id, crm.id, crm.provider, ap.* FROM teams t
join crm_configurations crm on t.id = crm.team_id
join activity_providers ap on t.id = ap.team_id
where t.status = 'active' and ap.is_enabled = 1
and crm.provider = 'hubspot'
and ap.provider NOT IN ('hubspot', 'aircall', 'uploader', 'gong', 'twilio', 'zoom-bot', 'google-meet', 'ms-teams',
'outreach', 'close', 'ringcentral', 'dialpad', 'zoom-phone');
SELECT * FROM teams where id = 1068;
SELECT * FROM crm_configurations where team_id = 1068;
SELECT * FROM activity_providers where team_id = 1068;
SELECT * FROM activities a
where crm_configuration_id = 993 and type IN ('softphone', 'softphone-outbound')
and a.provider NOT IN ('hubspot', 'uploader', 'gong', 'twilio', 'google-meet', 'ms-teams','close'
)
# and telephony_provider_id = '019c1131-a22f-4792-b9ea-20adf6a02ed0'
order by a.id desc;
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 1068 and sa.provider = 'hubspot';
# [PASSWORD_DOTS]
# [PASSWORD_DOTS]
# [PASSWORD_DOTS]
SELECT * FROM crm_configurations where id = 882; # 933 - GoGlobal , portalId: 6017093
SELECT * FROM opportunities WHERE team_id = 933 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 933 and updated_at > '2026-02-06 00:00:00' order by updated_at desc;
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 933 and sa.provider = 'hubspot';
# [PASSWORD_DOTS]
SELECT * FROM crm_configurations where id = 834; # 882 - AnyVan , portalId: 5468262
SELECT * FROM contacts WHERE crm_configuration_id = 834 and updated_at > '2026-03-30 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE crm_configuration_id = 834 and updated_at > '2026-03-04 08:00:00' order by updated_at desc;
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 882 and sa.provider = 'hubspot';
select * from crm_layouts where crm_configuration_id = 834;
select * from crm_layout_entities where crm_layout_id = 2780;
select * from crm_fields where id IN (321153,321192,321193,321194);
SELECT * FROM opportunities WHERE crm_configuration_id = 834 and id = 10993426;
# [PASSWORD_DOTS]
SELECT * FROM crm_configurations where id = 988; # 1057 - Teya (543ce4f4-168c-4571-91ea-5b35c253f06f) , portalId: 26651871
SELECT * FROM opportunities WHERE team_id = 1057 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 1057 and updated_at > '2026-02-04 00:00:00' order by updated_at desc;
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 1057 and sa.provider = 'hubspot';
SELECT * FROM crm_configurations where id = 533; # 559 - Connectd , portalId: 6710988
SELECT * FROM opportunities WHERE team_id = 559 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 559 and updated_at > '2026-02-04 00:00:00' order by updated_at desc;
SELECT * FROM crm_configurations where id = 801; # 852 - Rise Vision , portalId: 2700250
SELECT * FROM opportunities WHERE team_id = 852 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 852 and updated_at > '2026-02-04 00:00:00' order by updated_at desc; # 6th last
SELECT * FROM crm_configurations where id = 962; # 1034 - evergrowth.io , portalId: 143180990
SELECT * FROM opportunities WHERE team_id = 1034 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 1034 and updated_at > '2026-02-04 00:00:00' order by updated_at desc;
# [PASSWORD_DOTS]
SELECT * FROM crm_configurations where id = 1037; # 1102 - Jibble , portalId: 6649755
SELECT * FROM opportunities WHERE team_id = 1102 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 1102 and updated_at > '2026-02-04 12:30:00' order by updated_at desc; # 8
SELECT * FROM crm_configurations where id = 1015; # 1049 - Travefy , portalId: 48904401
SELECT * FROM opportunities WHERE team_id = 1049 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 1049 and updated_at > '2026-02-04 12:30:00' order by updated_at desc; # 20
SELECT * FROM crm_configurations where id = 64; # 70 - SalaryFinance , portalId: 3404115
SELECT * FROM opportunities WHERE team_id = 70 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 70 and updated_at > '2026-02-04 12:30:00' order by updated_at desc; # 6th last
SELECT * FROM crm_configurations where id = 802; # 853 - Street Group , portalId: 7658438
SELECT * FROM opportunities WHERE team_id = 853 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 853 and updated_at > '2026-02-04 12:30:00' order by updated_at desc; # 10
SELECT * FROM crm_configurations where id = 872; # 921 - In Professional Development , portalId: 9238273
SELECT * FROM opportunities WHERE team_id = 921 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 921 and updated_at > '2026-02-04 12:30:00' order by updated_at desc; # 2
# [PASSWORD_DOTS]
SELECT * FROM crm_configurations where id = 550; # 576 - SeedLegals , portalId: 3028661
SELECT * FROM opportunities WHERE team_id = 576 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 576 and updated_at > '2026-02-05 14:00:00' order by updated_at desc;
SELECT * FROM crm_configurations where id = 989; # 1058 - rtaoutdoor.com , portalId: 22371204
SELECT * FROM opportunities WHERE team_id = 1058 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 1058 and updated_at > '2026-02-05 14:00:00' order by updated_at desc;
SELECT * FROM crm_configurations where id = 896; # 946 - Mintago , portalId: 6621281
SELECT * FROM opportunities WHERE team_id = 946 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 946 and updated_at > '2026-02-05 14:00:00' order by updated_at desc;
SELECT * FROM crm_configurations where id = 617; # 641 - PCS , portalId: 5244937
SELECT * FROM opportunities WHERE team_id = 641 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 641 and updated_at > '2026-02-05 14:00:00' order by updated_at desc; # 7th
# [PASSWORD_DOTS]
SELECT * FROM crm_configurations where id = 649; # 670 - Eventeny , portalId: 4492849
SELECT * FROM opportunities WHERE team_id = 670 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 670 and updated_at > '2026-02-09 08:00:00' order by updated_at desc; #
SELECT * FROM crm_configurations where id = 48; # 51 - CleanCloud , portalId: 4373137
SELECT * FROM opportunities WHERE team_id = 51 and updated_at > '2026-03-04 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 51 and updated_at > '2026-02-09 08:00:00' order by updated_at desc;
select * from users where team_id = 51; # 7783
SELECT * FROM groups WHERE uuid_to_bin('8a8d2cb6-8b55-4fa3-8b5c-5f0e3d8de59a') = uuid; # 1130
select * from activity_searches where user_id = 7783;
select * from activity_search_filters where activity_search_id IN (32291, 32292);
SELECT asf.activity_search_id, asf.id, asf.value
FROM activity_search_filters asf
WHERE asf.filter = 'group_id'
AND asf.value IN (
SELECT CONCAT(
HEX(SUBSTR(uuid, 5, 4)), '-',
HEX(SUBSTR(uuid, 3, 2)), '-',
HEX(SUBSTR(uuid, 1, 2)), '-',
HEX(SUBSTR(uuid, 9, 2)), '-',
HEX(SUBSTR(uuid, 11))
)
FROM groups
WHERE deleted_at IS NOT NULL
);
SELECT * FROM crm_configurations where id = 272; # 290 - Bonham & Brook , portalId: 5705856
SELECT * FROM opportunities WHERE team_id = 290 and updated_at > '2026-02-05 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 290 and updated_at > '2026-02-09 08:00:00' order by updated_at desc; # 6th
# [PASSWORD_DOTS]
SELECT * FROM crm_configurations where provider = 'hubspot';
SELECT * FROM crm_configurations where id = 1056; # 1119 - Chromatic , portalId: 45602133
SELECT * FROM opportunities WHERE team_id = 1119 and remotely_created_at > '2026-02-01 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 1119 and updated_at > '2026-02-09 09:00:00' order by updated_at desc; # null
# [PASSWORD_DOTS]
select * from contacts where crm_provider_id = '003Uu00000ojD4NIAU';
select
cp.*
# DISTINCT t.id
# cp.id, cp.user_id, t.id, cp.crm_configuration_id, cp.contact_fields
FROM crm_profiles cp
JOIN crm_configurations crm on crm.id = cp.crm_configuration_id
JOIN users u on u.id = cp.user_id
JOIN teams t ON t.id = crm.team_id
WHERE crm.provider = 'salesforce' and t.status = 'active'
and cp.archived_at IS NULL and u.deleted_at IS NULL
and t.id NOT IN (1093)
and t.id = 2
and cp.contact_fields IS NULL;
# and c.crm_provider_id = '003Uu00000ojD4NIAU';
SELECT * FROM users WHERE id = 26484;
SELECT * FROM crm_profiles WHERE user_id = 26484;
SELECT * FROM social_accounts WHERE sociable_id = 26484;
SELECT * FROM crm_configurations where provider = 'salesforce';
select * from users where id IN (10022, 10403);
select * from users where team_id IN (526);
select * from teams where id IN (526, 532);
select * from crm_configurations where id IN (500, 516);
select * from crm_profiles where crm_configuration_id IN (500, 516) and user_id IN (10022, 10403);
select * from contacts where crm_configuration_id IN (500, 516) and crm_provider_id = '003Uu00000ojD4NIAU';
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 526 and sa.provider = 'salesforce';
select * from team_settings where team_id IN (526, 532);
select * from users where id IN (22824);
select * from crm_profiles where crm_configuration_id IN (1026);
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 1093 and sa.provider = 'salesforce';
select * from teams where id = 1099;
select * from users where id = 29643
select * from activity_processing_states;
SELECT * FROM teams where name LIKE '%Fare%'; # 233
SELECT * FROM opportunities where crm_configuration_id = 215
# and crm_provider_id = 'oppo_ogESZf2P50nDrd1nGPvKDXeA6sSaTN5v51Lp4ayVzKR'
;
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 1088 and sa.provider = 'hubspot';
SELECT * FROM teams order by updated_at DESC
SELECT * FROM crm_configurations WHERE id = 1019; # SimpleConsign 1088 - no social account
select * from crm_configurations where provider = 'pipedrive';
select * from teams where id = 957;
select * from crm_configurations where id = 957;
SELECT * FROM teams WHERE name LIKE '%Prolific%'; # 544, 518, 10743
SELECT * FROM opportunities where crm_configuration_id = 518 order by id desc;
select * from users where team_id = 1; # 26726 - Gabriela Dureva
SELECT * FROM opportunities where user_id = 26726; # 16834447 - Prolific
select * from activities where user_id = 26726 order by id desc;
select * from contacts where crm_configuration_id = 1
and email IN ('[EMAIL]', '[EMAIL]'); # 2094416, 2093620
SELECT * FROM contacts WHERE id = 6284931;
SELECT p.* FROM activities a JOIN participants p ON a.id = p.activity_id
WHERE a.user_id = 26726 and p.lead_id IN (2094416, 2093620) and a.created_at > '2026-01-01 00:00:00' order by p.email;
select * from activities where id IN (75509259,75509261,75509261,75511034,75026464,75517602,75517605);
select * from crm_configurations where id = 1;
43801692-1aeb-32ce-acba-5b80a479701a
44c3c9cf-6f5e-75f3-8179-bc9f75dd2b1b
405975c0-b3d0-7aaa-821f-09d59cae6dd1
4caf848d-4bed-2299-b248-7788d41f9fca
49bedc3f-f196-eef3-89c3-dea6a3b4aa63
43420989-a09d-b8f8-9806-c8bbf7a02aac
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 1 and sa.provider = 'salesforce';
SELECT * FROM activities WHERE id = 75461988;
SELECT * FROM activities WHERE uuid_to_bin('d6c5052e-e972-49e9-8912-26f2f7d6c5f6') = uuid;
select * from contacts where id = 17900517;
select * from contact_roles cr join crm_configurations crm on cr.crm_configuration_id = crm.id
where crm.provider != 'salesforce';
select * from users where id = 21047;
SELECT * FROM crm_configurations WHERE id = 892;
SELECT * FROM teams WHERE id = 942;
select * from opportunities where team_id = 942 order by updated_at desc;
select * from contacts where team_id = 942 order by updated_at desc;
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 942 and sa.provider = 'hubspot';
SELECT * FROM opportunities where team_id = 1 and crm_provider_id IN ('006Pq00000NeH6XIAV', '006Pq000007z8kdIAA'); # 10697889, 6621430
SELECT * FROM crm_configurations WHERE id = 1;
SELECT * FROM teams WHERE crm_id = 1;
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 1 and sa.provider = 'salesforce';
select id, user_id, opportunity_fields from crm_profiles where crm_configuration_id = 1
SELECT * FROM opportunities where team_id = 1 order by updated_at desc; # 10697889, 6621430
select * from teams where id = 852;
select * from groups where id = 2286;
select * from sidekick_settings where team_id = 852;
select * from default_activity_types where team_id = 852;
SELECT cc.provider, cc.id, p.id, u.*
FROM users u
LEFT JOIN crm_profiles p ON u.id = p.user_id AND p.id IS NULL -- no profile
INNER JOIN teams t ON u.team_id = t.id AND t.status = 'active' -- team is active
INNER JOIN crm_configurations cc ON t.crm_id = cc.id
WHERE u.status = 1 AND u.deleted_at IS NULL
AND u.crm_required = 1
AND u.team_id = 1
ORDER BY u.team_id;
SELECT * FROM crm_profiles cp where cp.crm_configuration_id = 1 and cp.user_id IN (
18481
);
SELECT cc.provider, cc.id, p.id, u.*
FROM users u
LEFT JOIN crm_profiles p ON u.id = p.user_id
INNER JOIN teams t ON u.team_id = t.id AND t.status = 'active'
INNER JOIN crm_configurations cc ON t.crm_id = cc.id
WHERE u.status = 1
AND u.deleted_at IS NULL
AND u.crm_required = 1
# AND u.team_id = 1
AND p.id IS NULL -- Move this condition to WHERE clause
ORDER BY u.team_id;
SELECT * FROM opportunities WHERE id = 20002609;
select * from teams where id = 1122; # Velatir, 29953 - [EMAIL]
select * from crm_configurations where id = 1060;
select * from crm_layouts where crm_configuration_id = 1060;
SELECT * FROM crm_layout_entities WHERE crm_layout_id = 3596;
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 1122 and sa.provider = 'hubspot';
select * from opportunities where team_id = 1122 order by updated_at desc;
select * from crm_field_data where object_type = 'contact';
SELECT * FROM activities WHERE uuid_to_bin('374fc8ed-3315-4c9f-9b25-318b7fd2928f') = uuid; # 76584262
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 248 and sa.provider = 'salesforce';
SELECT * FROM crm_profiles where user_id = 24115; # 005QF000002CswMYAS
SELECT * FROM users where id = 24115;
SELECT * FROM accounts where id = 4002896;
SELECT * FROM teams WHERE name LIKE '%adswerve%';
SELECT * FROM opportunities where crm_configuration_id = 230 AND crm_provider_id IN ("0069N000003GIQ9QAO","0061r000019yGP9AAM","0066900001S2KWlAAN","0066900001TDpj2AAD","0066900001b8uEwAAI","0069N000001rQi0QAE","006QF00000KD40mYAD","006QF00000LzpRJYAZ","0069N000002uomtQAA","0069N000002xlMLQAY","0066900001NV6ubAAD","0061r00001HJp45AAD","006QF00000uTlUoYAK","006QF00000v0bZqYAI");
SELECT * FROM opportunities WHERE crm_configuration_id = 230 AND crm_provider_id = '0069N000003GIQ9QAO'; # 6272203
SELECT u.id, u.email, ac.name, a.* FROM activities a
JOIN users u ON a.user_id = u.id
JOIN accounts ac ON a.account_id = ac.id
WHERE
uuid_to_bin('e3269598-b562-44fb-b5e9-9d2694dc63e0') = a.uuid or
uuid_to_bin('66ddc3ab-4e15-45aa-af0c-248c1eece593') = a.uuid or
uuid_to_bin('826bd328-e1cc-4213-b8d8-572454cacc07') = a.uuid;
select * from users where id = 5825;
SELECT * FROM activities WHERE uuid_to_bin('e56aa2e8-231a-421b-ab1f-cb38ed2bf573') = uuid;
select * from activities where uuid_to_bin('91e13b2f-2d1b-45f8-b1fd-1141b6563782') = uuid;
19594, 862
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 862 and sa.provider = 'salesforce';
select * from automated_reports where id = 36;
select ar.frequency, r.*, ar.* from automated_report_results r
join automated_reports ar on r.report_id = ar.id
where ar.frequency != 'one_off';
select s.* from activity_searches s join users u ON s.user_id = u.id where u.team_id = 882;
select * from nudges n where n.activity_search_id
select * from teams where created_at > '2026-03-09';
SELECT * FROM crm_layouts WHERE crm_configuration_id = 1065; # 1065
SELECT * FROM crm_layout_entities WHERE crm_layout_id = 3617;
select * from users where team_id = 1 and name like '%Lukas%'; # 7160
SELECT * FROM teams WHERE id = 575;
select * from opportunities where team_id = 575;
SELECT * FROM teams WHERE name LIKE '%Integrum ESG%'; # 1126, 1065,
select * from opportunities where team_id = 1126;
SELECT * FROM teams WHERE name LIKE '%Base%'; # 1125, 1063,
select * from opportunities where team_id = 1125;
select * from contacts c
where c.team_id = 882;
SELECT * FROM activities WHERE id = 76822967;
SELECT * FROM crm_profiles WHERE user_id = 15440;
SELECT * FROM crm_profiles WHERE crm_configuration_id = 555;
SELECT * FROM crm_configurations WHERE id = 555;
SELECT * FROM users WHERE id = 15440; # team. 581, gr. 15440, pl. 3911, act. field 162182
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 581 and sa.provider = 'salesforce';
SELECT * FROM automated_report_results order by id desc;
select * from features;
select * from team_features where feature_id = 40;
select * from teams where id = 556;
select * from automated_reports;
where id = 54; # 4fdd41f6-dcf0-30d0-b339-7345381b6044 , ["pdf","podcast"]
SELECT * FROM automated_report_results WHERE uuid_to_bin('822fa41b-afd3-43a9-a248-86b0e36f3131') = uuid;
select * from automated_report_results order by id desc;
SELECT * FROM automated_report_results WHERE id = 1919;
select * from automated_report_results WHERE report_id = 54;
select * from opportunities where id = 7594349;
SELECT * FROM teams WHERE name LIKE '%Les%'; # 711, 692, 16067 - [EMAIL]
select * from playbooks where team_id = 711; # event 226147
SELECT * FROM playbook_categories WHERE playbook_id = 5515;
SELECT * FROM crm_fields WHERE crm_configuration_id = 692 and object_type = 'event';
SELECT * FROM crm_fields WHERE id = 226147;
SELECT * FROM crm_field_values WHERE crm_field_id = 226147;
SELECT * FROM crm_configurations WHERE id = 692;
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 711 and sa.provider = 'salesforce';
SELECT * FROM crm_profiles cp JOIN users u on u.id = cp.user_id WHERE u.team_id = 711;
select * from leads;
select * from calendars;
SELECT
t.id AS team_id,
t.name,
LOWER(SUBSTRING_INDEX(c.calendar_provider_id, '@', -1)) AS calendar_domain
FROM teams t
JOIN users u ON u.team_id = t.id
JOIN calendars c ON c.user_id = u.id AND c.status = 'active' AND c.calendar_provider_id LIKE '%@%'
LEFT JOIN team_domains td
ON td.team_id = t.id
AND td.deleted_at IS NULL
AND td.domain = LOWER(SUBSTRING_INDEX(c.calendar_provider_id, '@', -1))
GROUP BY t.id, t.name, calendar_domain
ORDER BY t.name, calendar_domain;
select * from users u join calendars c on c.user_id = u.id
where u.team_id = 882;
select * from activities where id = 74049485; # team 563 crm 537
select * from activities where id = 73272382; # team 563 crm 537
select * from activities where id = 64400389; # team 563 crm 537
select * from activities where id = 58081273; # team 563 crm 537
select * from activities where id = 54520297; # team 563 crm 537
select * from participants where activity_id = 58081273;
select * from activities where crm_configuration_id = 537 and provider = 'aircall'
and account_id = 19003658 order by updated_at desc;
select * from contacts where crm_configuration_id = 537 and id = 35957759;
select * from accounts where crm_configuration_id = 537 and id = 19003658;
select * from automated_report_results where id = 1976;
select * from automated_reports where id = 583;
select * from activity_searches where id = 87714;
select * from activity_search_filters where activity_search_id = 87714;
SELECT * FROM activities WHERE uuid_to_bin('8827f672-202d-4162-9d04-73ff5f0566a9') = uuid
or uuid_to_bin('47842446-af51-4bcb-854f-cc6560290101') = uuid;
SELECT * FROM crm_configurations WHERE provider = 'hubspot';
select * from rate_limits;
select * from automated_report_results where media_type = 'pdf' and status = 2
and id IN (18, 1872);
select * from automated_reports where id = 54;
SELECT * FROM users WHERE id IN (24623,29443,29613);
SELECT * FROM automated_report_results WHERE uuid_to_bin('822fa41b-afd3-43a9-a248-86b0e36f3131') = uuid;
select * from text_relays where created_at > '2026-05-01';
and id IN (32415, 32416);
# and id = 32412;
select * from users where team_id = 2 and email like '%scott%' and id = 29510;
SELECT * FROM activities WHERE uuid_to_bin('67cebfc2-ed56-44a2-8c68-7a0286ed8618') = uuid; # 79763436
SELECT email_provider_id, COUNT(*) as count, GROUP_CONCAT(id) as ids, GROUP_CONCAT(status) as statuses
FROM text_relays
WHERE email_provider_id IN ('19e2027868a64b42', '19e2033ed8ea6b10')
GROUP BY email_provider_id;
SELECT id, status, telephony_provider_id, created_at
FROM activities
WHERE id IN (80028719, 80028846);
SELECT id, status, code, email_sent_at, created_at, updated_at
FROM text_relays
WHERE id IN (32415, 32416);
SELECT id, status, code, sender, recipient, created_at
FROM text_relays
WHERE sender LIKE '%mario.georgiev%' OR sender LIKE '%stoyan.tomov%'
ORDER BY created_at DESC
LIMIT 10;
SELECT id, uuid, status, code, sender, recipient, created_at, updated_at
FROM text_relays
WHERE uuid = uuid_to_bin('0626141c-27a6-4d8c-aff8-7c8020a2c656');
# [PASSWORD_DOTS]
SELECT DISTINCT u.id, u.email, u.name, u.softphone_number, COUNT(a.id) as sms_count
FROM users u
INNER JOIN activities a ON u.id = .user_id
WHERE a.type LIKE 'sms%'
AND a.created_at > DATE_SUB(NOW(), INTERVAL 30 DAY)
GROUP BY u.id, u.email, u.name, u.softphone_number
ORDER BY sms_count DESC;
select * from teams where id = 1;
select * from roles;
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 1117 and sa.provider = 'hubspot';
SELECT * FROM activities WHERE uuid_to_bin('8024fffb-2df7-4017-91f4-d9f896050248') = uuid; # 79933459 YES
SELECT * FROM activities WHERE uuid_to_bin('[CREDIT_CARD]-927f-4f4da2a8185c') = uuid; # 80186192 NO
SELECT * FROM crm_configurations WHERE id = 1053;
SELECT * FROM teams WHERE id = 1117;
select * from users where id = 30249;
select * from playbooks where id = 5473;
select * from playbook_categories where id = 43783;
select * from playbook_categories where playbook_id = 5473;
select * from crm_fields where id = 659242;
select * from crm_field_values where crm_field_id = 659242;
SELECT * FROM crm_field_data fd
# JOIN crm_fields f ON fd.crm_field_id = f.id
# JOIN activities a ON fd.activity_id = a.id
WHERE activity_id = 79933459
# AND f.crm_provider_id = 'hs_activity_type';
SELECT * FROM activity_messages;
select * from text_relays where created_at > '2026-05-01';
select * from activities where user_id IN (7160, 18608) and created_at > '2026-05-22' order by id desc;
select * from users where team_id = 1 and id IN (18608, 13934, 7160);
select * from activities where user_id = 7160 order by id desc limit 10;
select * from accounts where team_id = 1 and name = 'Column5';
select * from users where name like '%Subra%'; # 31054, 1117
select * from teams where id = 1117;
select * from activity_searches where user_id = 31054;
select * from activity_search_filters where activity_search_id IN (88882, 88902);...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"#12121 on JY-20963-fix-import-on-deleted-entity, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.11569149,"height":0.025538707},"on_screen":true,"help_text":"Pull request #12121 exists for current branch JY-20963-fix-import-on-deleted-entity, but pull request details loading failed","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.85638297,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"ServiceTest","depth":6,"bounds":{"left":0.87167555,"top":0.019952115,"width":0.043882977,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'ServiceTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'ServiceTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"22","depth":4,"bounds":{"left":0.34042552,"top":0.12529927,"width":0.009973404,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"5","depth":4,"bounds":{"left":0.35239363,"top":0.12529927,"width":0.007978723,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.3620346,"top":0.123703115,"width":0.00731383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.3693484,"top":0.123703115,"width":0.006981383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Crm;\n\nuse Illuminate\\Support\\Collection;\nuse Jiminny\\Contracts\\Repositories\\TeamRepository;\nuse Jiminny\\Contracts\\Services\\Crm\\ServiceInterface;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Exceptions\\SocialAccountTokenInvalidException;\nuse Jiminny\\Models\\Account;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Contact;\nuse Jiminny\\Models\\Lead;\nuse Jiminny\\Models\\Opportunity;\nuse Jiminny\\Models\\Participant;\nuse Jiminny\\Models\\Stage;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\ResolveTeamCrmConnection;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Psr\\Log\\LoggerInterface;\nuse Exception;\nuse Throwable;\n\nclass CrmActivityService\n{\n public function __construct(\n private readonly TeamRepository $teamRepository,\n private readonly CachedCrmServiceDecorator $decorator,\n private readonly EmailHelper $emailHelper,\n private readonly ResolveTeamCrmConnection $teamCrmResolver,\n private readonly LoggerInterface $logger,\n ) {\n }\n\n /**\n * Updates CRM data for an activity and its participants.\n *\n * NOTE: This method performs multiple database writes and should be called\n * within a transaction by the caller to ensure atomicity.\n *\n * @param Activity $activity\n * @param bool $remoteSearch\n *\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception\n */\n public function updateCrmData(\n Activity $activity,\n bool $remoteSearch = false,\n ): void {\n $crmService = null;\n $participants = $activity->getParticipants();\n $team = $activity->getTeam();\n\n $prospectSearchStrategy = ProspectSearchStrategyFactory::match($team);\n if ($prospectSearchStrategy->ignoreCrmMatchData()) {\n $this->logger->info('[CrmActivityService] Ignoring crm data because of prospect strategy', [\n 'activity_id' => $activity->getId(),\n 'strategy' => get_class($prospectSearchStrategy),\n ]);\n\n return;\n }\n\n if ($remoteSearch) {\n try {\n $crmService = $this->teamCrmResolver->resolveForTeam($team);\n } catch (SocialAccountTokenInvalidException) {\n $this->logger->warning('[CrmActivityService] CRM token expired, falling back to local search', [\n 'activity_id' => $activity->getId(),\n 'team_id' => $team->getId(),\n ]);\n }\n }\n\n $records = $this->updateParticipantsCrmData(\n team: $team,\n activity: $activity,\n participants: $participants,\n crmService: $crmService,\n );\n\n if (! empty($records)) {\n $activity->updateActivityCrmData($records);\n }\n\n $activity->refresh();\n }\n\n /**\n * @param Collection<Participant> $participants\n *\n * @throws Exception\n *\n * @return array{\n * Lead|null,\n * Account|null,\n * Opportunity|null,\n * Contact|null,\n * Stage|null,\n * string|null\n *}|array{}\n */\n private function updateParticipantsCrmData(\n Team $team,\n Activity $activity,\n Collection $participants,\n ?ServiceInterface $crmService = null,\n ): array {\n $matchedRecords = [];\n $matchedDomainRecords = [];\n\n $this->validateCrmConfiguration($activity);\n $this->decorator->setConfiguration($activity->getCrm());\n $this->decorator->setCrmService($crmService);\n\n foreach ($participants as $participant) {\n if ($this->shouldSkipParticipant($participant)) {\n continue;\n }\n\n if (! $this->shouldPerformLookup($participant, $team)) {\n $this->logger->info('[CrmActivityService] Email domain belongs to the team, skipping crm lookup', [\n 'activity_id' => $activity->getId(),\n 'team_id' => $team->getId(),\n 'email' => $participant->getEmailAddress(),\n ]);\n\n $this->attachUserIfExists($participant, $team);\n\n continue;\n }\n\n $records = $this->findCrmRecords($participant, $activity);\n\n if (! empty($records)) {\n $matchedRecords[] = $records;\n } else {\n $records = $this->findCrmDomainRecords(\n crmService: $crmService,\n participant: $participant,\n activity: $activity,\n );\n if (! empty($records)) {\n $matchedDomainRecords[] = $records;\n }\n }\n\n if (empty($records)) {\n continue;\n }\n\n try {\n $activity->updateParticipantCrmData($records, $participant);\n } catch (Throwable $ex) {\n $this->logger->error('[CrmActivityService] Failed to update participant CRM data', [\n 'activity_id' => $activity->getId(),\n 'participant_id' => $participant->getId(),\n 'exception' => $ex->getMessage(),\n ]);\n\n continue;\n }\n }\n\n $bestMatch = $this->getBestMatch(\n matchedRecords : $matchedRecords,\n matchedDomainRecords: $matchedDomainRecords,\n );\n\n $this->logger->info('[CrmActivityService] CRM matching completed', [\n 'activity_id' => $activity->getId(),\n 'participants_processed' => $participants->count(),\n 'exact_matches' => count($matchedRecords),\n 'domain_matches' => count($matchedDomainRecords),\n 'best_match_found' => ! empty($bestMatch),\n ]);\n\n return $bestMatch;\n }\n\n private function shouldPerformLookup(Participant $participant, Team $team): bool\n {\n if ($participant->hasEmailAddress()) {\n return $this->emailHelper->shouldPerformLookup($team, $participant->getEmailAddress());\n }\n\n return true;\n }\n\n private function validateCrmConfiguration(Activity $activity): void\n {\n if ($activity->getCrm() === null) {\n throw new InvalidArgumentException('Cannot find CRM configuration');\n }\n }\n\n private function getBestMatch(?array $matchedRecords, ?array $matchedDomainRecords): array\n {\n return RecordSelector::pickBestFromLists($matchedRecords, $matchedDomainRecords);\n }\n\n private function findCrmRecords(Participant $participant, Activity $activity): ?array\n {\n $records = null;\n\n if ($participant->hasEmailAddress()) {\n $records = $this->decorator->matchExactlyByEmail(\n email: $participant->getEmailAddress(),\n userId: $activity->getUser()->getId()\n );\n }\n\n if (empty($records) && $participant->getPhoneNumber() !== null) {\n $records = $this->decorator->matchByPhone(\n phone: $participant->getPhoneNumber(),\n userId: $activity->getUser()->getId(),\n );\n }\n\n if (empty($records) && $participant->getName() !== null) {\n $records = $this->decorator->matchByName(\n name: $participant->getName(),\n userId: $activity->getUser()->getId(),\n );\n }\n\n return $records;\n }\n\n private function shouldSkipParticipant(Participant $participant): bool\n {\n return $participant->hasUser();\n }\n\n private function attachUserIfExists(Participant $participant, Team $team): void\n {\n if ($participant->hasEmailAddress() === false) {\n return;\n }\n\n $user = $this->teamRepository->findActiveTeamMemberByEmail($team, $participant->getEmailAddress());\n\n if ($user instanceof User) {\n $participant->user_id = $user->getId();\n $participant->save();\n }\n }\n\n private function findCrmDomainRecords(\n ?ServiceInterface $crmService,\n Participant $participant,\n Activity $activity,\n ): array {\n if ($participant->hasEmailAddress()) {\n $this->decorator->setConfiguration($activity->getCrm());\n $this->decorator->setCrmService($crmService);\n\n $records = $this->decorator->matchByDomain(\n email: $participant->getEmailAddress(),\n userId: $activity->getUser()->getId()\n );\n if (! empty($records)) {\n return $records;\n }\n }\n\n return [];\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Crm;\n\nuse Illuminate\\Support\\Collection;\nuse Jiminny\\Contracts\\Repositories\\TeamRepository;\nuse Jiminny\\Contracts\\Services\\Crm\\ServiceInterface;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Exceptions\\SocialAccountTokenInvalidException;\nuse Jiminny\\Models\\Account;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Contact;\nuse Jiminny\\Models\\Lead;\nuse Jiminny\\Models\\Opportunity;\nuse Jiminny\\Models\\Participant;\nuse Jiminny\\Models\\Stage;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\ResolveTeamCrmConnection;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Psr\\Log\\LoggerInterface;\nuse Exception;\nuse Throwable;\n\nclass CrmActivityService\n{\n public function __construct(\n private readonly TeamRepository $teamRepository,\n private readonly CachedCrmServiceDecorator $decorator,\n private readonly EmailHelper $emailHelper,\n private readonly ResolveTeamCrmConnection $teamCrmResolver,\n private readonly LoggerInterface $logger,\n ) {\n }\n\n /**\n * Updates CRM data for an activity and its participants.\n *\n * NOTE: This method performs multiple database writes and should be called\n * within a transaction by the caller to ensure atomicity.\n *\n * @param Activity $activity\n * @param bool $remoteSearch\n *\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception\n */\n public function updateCrmData(\n Activity $activity,\n bool $remoteSearch = false,\n ): void {\n $crmService = null;\n $participants = $activity->getParticipants();\n $team = $activity->getTeam();\n\n $prospectSearchStrategy = ProspectSearchStrategyFactory::match($team);\n if ($prospectSearchStrategy->ignoreCrmMatchData()) {\n $this->logger->info('[CrmActivityService] Ignoring crm data because of prospect strategy', [\n 'activity_id' => $activity->getId(),\n 'strategy' => get_class($prospectSearchStrategy),\n ]);\n\n return;\n }\n\n if ($remoteSearch) {\n try {\n $crmService = $this->teamCrmResolver->resolveForTeam($team);\n } catch (SocialAccountTokenInvalidException) {\n $this->logger->warning('[CrmActivityService] CRM token expired, falling back to local search', [\n 'activity_id' => $activity->getId(),\n 'team_id' => $team->getId(),\n ]);\n }\n }\n\n $records = $this->updateParticipantsCrmData(\n team: $team,\n activity: $activity,\n participants: $participants,\n crmService: $crmService,\n );\n\n if (! empty($records)) {\n $activity->updateActivityCrmData($records);\n }\n\n $activity->refresh();\n }\n\n /**\n * @param Collection<Participant> $participants\n *\n * @throws Exception\n *\n * @return array{\n * Lead|null,\n * Account|null,\n * Opportunity|null,\n * Contact|null,\n * Stage|null,\n * string|null\n *}|array{}\n */\n private function updateParticipantsCrmData(\n Team $team,\n Activity $activity,\n Collection $participants,\n ?ServiceInterface $crmService = null,\n ): array {\n $matchedRecords = [];\n $matchedDomainRecords = [];\n\n $this->validateCrmConfiguration($activity);\n $this->decorator->setConfiguration($activity->getCrm());\n $this->decorator->setCrmService($crmService);\n\n foreach ($participants as $participant) {\n if ($this->shouldSkipParticipant($participant)) {\n continue;\n }\n\n if (! $this->shouldPerformLookup($participant, $team)) {\n $this->logger->info('[CrmActivityService] Email domain belongs to the team, skipping crm lookup', [\n 'activity_id' => $activity->getId(),\n 'team_id' => $team->getId(),\n 'email' => $participant->getEmailAddress(),\n ]);\n\n $this->attachUserIfExists($participant, $team);\n\n continue;\n }\n\n $records = $this->findCrmRecords($participant, $activity);\n\n if (! empty($records)) {\n $matchedRecords[] = $records;\n } else {\n $records = $this->findCrmDomainRecords(\n crmService: $crmService,\n participant: $participant,\n activity: $activity,\n );\n if (! empty($records)) {\n $matchedDomainRecords[] = $records;\n }\n }\n\n if (empty($records)) {\n continue;\n }\n\n try {\n $activity->updateParticipantCrmData($records, $participant);\n } catch (Throwable $ex) {\n $this->logger->error('[CrmActivityService] Failed to update participant CRM data', [\n 'activity_id' => $activity->getId(),\n 'participant_id' => $participant->getId(),\n 'exception' => $ex->getMessage(),\n ]);\n\n continue;\n }\n }\n\n $bestMatch = $this->getBestMatch(\n matchedRecords : $matchedRecords,\n matchedDomainRecords: $matchedDomainRecords,\n );\n\n $this->logger->info('[CrmActivityService] CRM matching completed', [\n 'activity_id' => $activity->getId(),\n 'participants_processed' => $participants->count(),\n 'exact_matches' => count($matchedRecords),\n 'domain_matches' => count($matchedDomainRecords),\n 'best_match_found' => ! empty($bestMatch),\n ]);\n\n return $bestMatch;\n }\n\n private function shouldPerformLookup(Participant $participant, Team $team): bool\n {\n if ($participant->hasEmailAddress()) {\n return $this->emailHelper->shouldPerformLookup($team, $participant->getEmailAddress());\n }\n\n return true;\n }\n\n private function validateCrmConfiguration(Activity $activity): void\n {\n if ($activity->getCrm() === null) {\n throw new InvalidArgumentException('Cannot find CRM configuration');\n }\n }\n\n private function getBestMatch(?array $matchedRecords, ?array $matchedDomainRecords): array\n {\n return RecordSelector::pickBestFromLists($matchedRecords, $matchedDomainRecords);\n }\n\n private function findCrmRecords(Participant $participant, Activity $activity): ?array\n {\n $records = null;\n\n if ($participant->hasEmailAddress()) {\n $records = $this->decorator->matchExactlyByEmail(\n email: $participant->getEmailAddress(),\n userId: $activity->getUser()->getId()\n );\n }\n\n if (empty($records) && $participant->getPhoneNumber() !== null) {\n $records = $this->decorator->matchByPhone(\n phone: $participant->getPhoneNumber(),\n userId: $activity->getUser()->getId(),\n );\n }\n\n if (empty($records) && $participant->getName() !== null) {\n $records = $this->decorator->matchByName(\n name: $participant->getName(),\n userId: $activity->getUser()->getId(),\n );\n }\n\n return $records;\n }\n\n private function shouldSkipParticipant(Participant $participant): bool\n {\n return $participant->hasUser();\n }\n\n private function attachUserIfExists(Participant $participant, Team $team): void\n {\n if ($participant->hasEmailAddress() === false) {\n return;\n }\n\n $user = $this->teamRepository->findActiveTeamMemberByEmail($team, $participant->getEmailAddress());\n\n if ($user instanceof User) {\n $participant->user_id = $user->getId();\n $participant->save();\n }\n }\n\n private function findCrmDomainRecords(\n ?ServiceInterface $crmService,\n Participant $participant,\n Activity $activity,\n ): array {\n if ($participant->hasEmailAddress()) {\n $this->decorator->setConfiguration($activity->getCrm());\n $this->decorator->setCrmService($crmService);\n\n $records = $this->decorator->matchByDomain(\n email: $participant->getEmailAddress(),\n userId: $activity->getUser()->getId()\n );\n if (! empty($records)) {\n return $records;\n }\n }\n\n return [];\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Execute","depth":4,"bounds":{"left":0.37799203,"top":0.09896249,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Explain Plan","depth":4,"bounds":{"left":0.38663563,"top":0.09896249,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Browse Query History","depth":4,"bounds":{"left":0.39760637,"top":0.09896249,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"View Parameters","depth":4,"bounds":{"left":0.40625,"top":0.09896249,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Open Query Execution Settings…","depth":4,"bounds":{"left":0.41489363,"top":0.09896249,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"In-Editor Results","depth":4,"bounds":{"left":0.42586437,"top":0.09896249,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Tx: Auto","depth":4,"bounds":{"left":0.4368351,"top":0.09896249,"width":0.024268618,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Cancel Running Statements","depth":4,"bounds":{"left":0.46343085,"top":0.09896249,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Playground","depth":4,"bounds":{"left":0.4744016,"top":0.09896249,"width":0.029587766,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"jiminny","depth":4,"bounds":{"left":0.64261967,"top":0.09896249,"width":0.02825798,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"45","depth":4,"bounds":{"left":0.61269945,"top":0.123703115,"width":0.010305851,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"1","depth":4,"bounds":{"left":0.625,"top":0.123703115,"width":0.00731383,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"41","depth":4,"bounds":{"left":0.6343085,"top":0.123703115,"width":0.009640957,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"66","depth":4,"bounds":{"left":0.6459442,"top":0.123703115,"width":0.010305851,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.65791225,"top":0.12210695,"width":0.00731383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.66522604,"top":0.12210695,"width":0.006981383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"SELECT * FROM teams WHERE name LIKE '%litify%'; # 1069, 994, 24993\nSELECT * FROM users WHERE id = 25061;\nSELECT * FROM crm_profiles WHERE crm_configuration_id = 994;\nSELECT * FROM crm_profiles WHERE user_id = 25061;\n\nselect * from crm_configurations where id = 834;\nSELECT * FROM teams WHERE id = 882;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 882 and sa.provider = 'hubspot';\n\nSELECT * FROM crm_configurations where id = 882; # 933 - GoGlobal\nSELECT * FROM opportunities WHERE team_id = 933 order by updated_at desc;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 933 and sa.provider = 'hubspot';\n\nSELECT * FROM crm_configurations WHERE provider = 'hubspot' and crm_provider_id = 7270388;\n\nSELECT * FROM contacts where crm_configuration_id = 834;\nSELECT * FROM opportunities WHERE team_id = 933\n# AND crm_provider_id IN ('20131586060','46017317898','52543911090','53451356564','54101251892','54323768459');\nAND id IN (8482561,18352941,19042734,19232139,19445140,19472541);\nSELECT * FROM opportunity_contacts\nWHERE opportunity_id IN (8482561,18352941,19042734,19232139,19445140,19472541);\n\n# ********************************************************************\nSELECT * FROM crm_configurations where id = 485; #\nSELECT * FROM opportunities WHERE team_id = 933 order by updated_at desc;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 933 and sa.provider = 'hubspot';\n\nselect crm.provider, l.* from leads l join crm_configurations crm on l.crm_configuration_id = crm.id\nwhere crm.provider NOT IN ('salesforce', 'integration-app', 'bullhorn', 'copper')\n# and l.converted_at IS NOT NULL\n;\n\n# ********************************************************************\nSELECT * FROM activities a WHERE type IN ('email-inbound', 'email-outbound')\nand opportunity_id IS NULL\norder by id desc;\n\nSELECT * FROM teams WHERE id = 604; # 598\nSELECT * FROM activities WHERE id = 74410828; # chelseaw@allvoices.co\nSELECT * FROM accounts WHERE id = 20068382;\nSELECT * FROM accounts WHERE id = 35186038;\n\nSELECT * FROM contacts WHERE team_id = 852 and updated_at > '2026-01-23 12:30:00' order by updated_at desc;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 559 and sa.provider = 'hubspot';\n\nSELECT * FROM activities WHERE uuid_to_bin('cb6342b6-a183-401c-b0af-ede92b2ae763') = uuid;\nselect * from sidekick_settings where team_id = 781;\n\nSELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id = 26651871; # Teya\nSELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id = 7562435;\nSELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id = 8420347; # opflit 2100\n\nSELECT * FROM crm_layouts WHERE crm_configuration_id = 711;\nSELECT * FROM activities where crm_configuration_id = 711 and crm_provider_id IS NULL\nand is_internal = 0 and status = 'completed'\norder by id desc;\n\nSELECT * FROM crm_layout_entities\nWHERE crm_layout_id IN (2352, 2353);\n;\n\nSELECT * FROM crm_configurations where provider = 'hubspot' and id = 530;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 556 and sa.provider = 'hubspot';\n\nSELECT * FROM activities WHERE uuid_to_bin('c6ca4b22-7738-4563-a95d-b8a9598924ae') = uuid;\nSELECT * FROM activities WHERE uuid_to_bin('442abb2b-28bd-4be8-9c25-19e9bf02766d') = uuid;\nselect * from contacts\nwhere crm_configuration_id = 530\nand crm_provider_id = 872252;\n\nselect * from activities where crm_configuration_id = 530\nand user_id = 14343 and type like '%softphone%'\nand created_at between '2026-01-28 15:00:00' and '2026-01-28 15:10:00';\n\nSELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id = 25666868; # Teya\nSELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id = 8646335; # Teya\nSELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id IN (5933397);\n\n\nSELECT t.name, t.id, t.owner_id, c.id, c.provider, c.crm_base_url FROM teams t\nJOIN crm_configurations c ON t.id = c.team_id\nWHERE t.status = 'active';\n\nSELECT * FROM teams where id = 1091;\nSELECT * FROM crm_configurations where team_id = 1091;\nSELECT * FROM activity_providers where team_id = 1091;\nSELECT * FROM activities where crm_configuration_id = 1024 and type IN ('softphone', 'softphone-outbound')\nand provider NOT IN ('hubspot', 'aircall')\n# and telephony_provider_id = '019c1131-a22f-4792-b9ea-20adf6a02ed0'\norder by id desc;\n\n\nSELECT * FROM teams WHERE name LIKE '%Leadventure%';\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1091 and sa.provider = 'salesforce';\n\nSELECT * FROM teams WHERE name LIKE '%Wilson%'; # 862, 812\nSELECT * FROM teams where id = 862;\nSELECT * FROM crm_configurations where team_id = 862;\nSELECT * FROM activity_providers where team_id = 862;\nSELECT * FROM activities where crm_configuration_id = 812 and type IN ('softphone', 'softphone-outbound')\nand provider NOT IN ('hubspot', 'aircall')\n# and telephony_provider_id = '019c1131-a22f-4792-b9ea-20adf6a02ed0'\norder by id desc;\n\n\nSELECT t.id, crm.id, crm.provider, ap.* FROM teams t\njoin crm_configurations crm on t.id = crm.team_id\njoin activity_providers ap on t.id = ap.team_id\nwhere t.status = 'active' and ap.is_enabled = 1\nand crm.provider = 'hubspot'\nand ap.provider NOT IN ('hubspot', 'aircall', 'uploader', 'gong', 'twilio', 'zoom-bot', 'google-meet', 'ms-teams',\n 'outreach', 'close', 'ringcentral', 'dialpad', 'zoom-phone');\n\nSELECT * FROM teams where id = 1068;\nSELECT * FROM crm_configurations where team_id = 1068;\nSELECT * FROM activity_providers where team_id = 1068;\n\nSELECT * FROM activities a\nwhere crm_configuration_id = 993 and type IN ('softphone', 'softphone-outbound')\nand a.provider NOT IN ('hubspot', 'uploader', 'gong', 'twilio', 'google-meet', 'ms-teams','close'\n )\n# and telephony_provider_id = '019c1131-a22f-4792-b9ea-20adf6a02ed0'\norder by a.id desc;\n\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1068 and sa.provider = 'hubspot';\n\n# ********************************************************************\n# ********************************************************************\n# ********************************************************************\nSELECT * FROM crm_configurations where id = 882; # 933 - GoGlobal , portalId: 6017093\nSELECT * FROM opportunities WHERE team_id = 933 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 933 and updated_at > '2026-02-06 00:00:00' order by updated_at desc;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 933 and sa.provider = 'hubspot';\n\n# ********************************************************************\nSELECT * FROM crm_configurations where id = 834; # 882 - AnyVan , portalId: 5468262\nSELECT * FROM contacts WHERE crm_configuration_id = 834 and updated_at > '2026-03-30 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE crm_configuration_id = 834 and updated_at > '2026-03-04 08:00:00' order by updated_at desc;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 882 and sa.provider = 'hubspot';\nselect * from crm_layouts where crm_configuration_id = 834;\nselect * from crm_layout_entities where crm_layout_id = 2780;\nselect * from crm_fields where id IN (321153,321192,321193,321194);\n\nSELECT * FROM opportunities WHERE crm_configuration_id = 834 and id = 10993426;\n# ********************************************************************\nSELECT * FROM crm_configurations where id = 988; # 1057 - Teya (543ce4f4-168c-4571-91ea-5b35c253f06f) , portalId: 26651871\nSELECT * FROM opportunities WHERE team_id = 1057 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 1057 and updated_at > '2026-02-04 00:00:00' order by updated_at desc;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1057 and sa.provider = 'hubspot';\n\nSELECT * FROM crm_configurations where id = 533; # 559 - Connectd , portalId: 6710988\nSELECT * FROM opportunities WHERE team_id = 559 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 559 and updated_at > '2026-02-04 00:00:00' order by updated_at desc;\n\nSELECT * FROM crm_configurations where id = 801; # 852 - Rise Vision , portalId: 2700250\nSELECT * FROM opportunities WHERE team_id = 852 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 852 and updated_at > '2026-02-04 00:00:00' order by updated_at desc; # 6th last\n\nSELECT * FROM crm_configurations where id = 962; # 1034 - evergrowth.io , portalId: 143180990\nSELECT * FROM opportunities WHERE team_id = 1034 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 1034 and updated_at > '2026-02-04 00:00:00' order by updated_at desc;\n\n# ********************************************************************\nSELECT * FROM crm_configurations where id = 1037; # 1102 - Jibble , portalId: 6649755\nSELECT * FROM opportunities WHERE team_id = 1102 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 1102 and updated_at > '2026-02-04 12:30:00' order by updated_at desc; # 8\n\nSELECT * FROM crm_configurations where id = 1015; # 1049 - Travefy , portalId: 48904401\nSELECT * FROM opportunities WHERE team_id = 1049 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 1049 and updated_at > '2026-02-04 12:30:00' order by updated_at desc; # 20\n\nSELECT * FROM crm_configurations where id = 64; # 70 - SalaryFinance , portalId: 3404115\nSELECT * FROM opportunities WHERE team_id = 70 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 70 and updated_at > '2026-02-04 12:30:00' order by updated_at desc; # 6th last\n\nSELECT * FROM crm_configurations where id = 802; # 853 - Street Group , portalId: 7658438\nSELECT * FROM opportunities WHERE team_id = 853 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 853 and updated_at > '2026-02-04 12:30:00' order by updated_at desc; # 10\n\nSELECT * FROM crm_configurations where id = 872; # 921 - In Professional Development , portalId: 9238273\nSELECT * FROM opportunities WHERE team_id = 921 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 921 and updated_at > '2026-02-04 12:30:00' order by updated_at desc; # 2\n\n# ********************************************************************\nSELECT * FROM crm_configurations where id = 550; # 576 - SeedLegals , portalId: 3028661\nSELECT * FROM opportunities WHERE team_id = 576 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 576 and updated_at > '2026-02-05 14:00:00' order by updated_at desc;\n\nSELECT * FROM crm_configurations where id = 989; # 1058 - rtaoutdoor.com , portalId: 22371204\nSELECT * FROM opportunities WHERE team_id = 1058 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 1058 and updated_at > '2026-02-05 14:00:00' order by updated_at desc;\n\nSELECT * FROM crm_configurations where id = 896; # 946 - Mintago , portalId: 6621281\nSELECT * FROM opportunities WHERE team_id = 946 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 946 and updated_at > '2026-02-05 14:00:00' order by updated_at desc;\n\nSELECT * FROM crm_configurations where id = 617; # 641 - PCS , portalId: 5244937\nSELECT * FROM opportunities WHERE team_id = 641 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 641 and updated_at > '2026-02-05 14:00:00' order by updated_at desc; # 7th\n# ********************************************************************\nSELECT * FROM crm_configurations where id = 649; # 670 - Eventeny , portalId: 4492849\nSELECT * FROM opportunities WHERE team_id = 670 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 670 and updated_at > '2026-02-09 08:00:00' order by updated_at desc; #\n\nSELECT * FROM crm_configurations where id = 48; # 51 - CleanCloud , portalId: 4373137\nSELECT * FROM opportunities WHERE team_id = 51 and updated_at > '2026-03-04 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 51 and updated_at > '2026-02-09 08:00:00' order by updated_at desc;\nselect * from users where team_id = 51; # 7783\nSELECT * FROM groups WHERE uuid_to_bin('8a8d2cb6-8b55-4fa3-8b5c-5f0e3d8de59a') = uuid; # 1130\nselect * from activity_searches where user_id = 7783;\nselect * from activity_search_filters where activity_search_id IN (32291, 32292);\n\nSELECT asf.activity_search_id, asf.id, asf.value\nFROM activity_search_filters asf\nWHERE asf.filter = 'group_id'\nAND asf.value IN (\n SELECT CONCAT(\n HEX(SUBSTR(uuid, 5, 4)), '-',\n HEX(SUBSTR(uuid, 3, 2)), '-',\n HEX(SUBSTR(uuid, 1, 2)), '-',\n HEX(SUBSTR(uuid, 9, 2)), '-',\n HEX(SUBSTR(uuid, 11))\n )\n FROM groups\n WHERE deleted_at IS NOT NULL\n);\n\nSELECT * FROM crm_configurations where id = 272; # 290 - Bonham & Brook , portalId: 5705856\nSELECT * FROM opportunities WHERE team_id = 290 and updated_at > '2026-02-05 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 290 and updated_at > '2026-02-09 08:00:00' order by updated_at desc; # 6th\n# ********************************************************************\nSELECT * FROM crm_configurations where provider = 'hubspot';\nSELECT * FROM crm_configurations where id = 1056; # 1119 - Chromatic , portalId: 45602133\nSELECT * FROM opportunities WHERE team_id = 1119 and remotely_created_at > '2026-02-01 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 1119 and updated_at > '2026-02-09 09:00:00' order by updated_at desc; # null\n# ********************************************************************\n\nselect * from contacts where crm_provider_id = '003Uu00000ojD4NIAU';\nselect\n cp.*\n# DISTINCT t.id\n# cp.id, cp.user_id, t.id, cp.crm_configuration_id, cp.contact_fields\nFROM crm_profiles cp\nJOIN crm_configurations crm on crm.id = cp.crm_configuration_id\nJOIN users u on u.id = cp.user_id\nJOIN teams t ON t.id = crm.team_id\nWHERE crm.provider = 'salesforce' and t.status = 'active'\n and cp.archived_at IS NULL and u.deleted_at IS NULL\n and t.id NOT IN (1093)\n and t.id = 2\n and cp.contact_fields IS NULL;\n# and c.crm_provider_id = '003Uu00000ojD4NIAU';\n\nSELECT * FROM users WHERE id = 26484;\nSELECT * FROM crm_profiles WHERE user_id = 26484;\nSELECT * FROM social_accounts WHERE sociable_id = 26484;\nSELECT * FROM crm_configurations where provider = 'salesforce';\nselect * from users where id IN (10022, 10403);\nselect * from users where team_id IN (526);\nselect * from teams where id IN (526, 532);\nselect * from crm_configurations where id IN (500, 516);\nselect * from crm_profiles where crm_configuration_id IN (500, 516) and user_id IN (10022, 10403);\nselect * from contacts where crm_configuration_id IN (500, 516) and crm_provider_id = '003Uu00000ojD4NIAU';\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 526 and sa.provider = 'salesforce';\nselect * from team_settings where team_id IN (526, 532);\n\nselect * from users where id IN (22824);\nselect * from crm_profiles where crm_configuration_id IN (1026);\n\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1093 and sa.provider = 'salesforce';\n\nselect * from teams where id = 1099;\nselect * from users where id = 29643\n\nselect * from activity_processing_states;\n\nSELECT * FROM teams where name LIKE '%Fare%'; # 233\nSELECT * FROM opportunities where crm_configuration_id = 215\n# and crm_provider_id = 'oppo_ogESZf2P50nDrd1nGPvKDXeA6sSaTN5v51Lp4ayVzKR'\n;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1088 and sa.provider = 'hubspot';\n\nSELECT * FROM teams order by updated_at DESC\nSELECT * FROM crm_configurations WHERE id = 1019; # SimpleConsign 1088 - no social account\n\nselect * from crm_configurations where provider = 'pipedrive';\n\nselect * from teams where id = 957;\nselect * from crm_configurations where id = 957;\n\nSELECT * FROM teams WHERE name LIKE '%Prolific%'; # 544, 518, 10743\nSELECT * FROM opportunities where crm_configuration_id = 518 order by id desc;\n\nselect * from users where team_id = 1; # 26726 - Gabriela Dureva\nSELECT * FROM opportunities where user_id = 26726; # 16834447 - Prolific\nselect * from activities where user_id = 26726 order by id desc;\nselect * from contacts where crm_configuration_id = 1\nand email IN ('charlotte.ward@prolific.com', 'frankie.bryant@prolific.com'); # 2094416, 2093620\nSELECT * FROM contacts WHERE id = 6284931;\n\nSELECT p.* FROM activities a JOIN participants p ON a.id = p.activity_id\nWHERE a.user_id = 26726 and p.lead_id IN (2094416, 2093620) and a.created_at > '2026-01-01 00:00:00' order by p.email;\n\nselect * from activities where id IN (75509259,75509261,75509261,75511034,75026464,75517602,75517605);\nselect * from crm_configurations where id = 1;\n\n43801692-1aeb-32ce-acba-5b80a479701a\n44c3c9cf-6f5e-75f3-8179-bc9f75dd2b1b\n405975c0-b3d0-7aaa-821f-09d59cae6dd1\n4caf848d-4bed-2299-b248-7788d41f9fca\n49bedc3f-f196-eef3-89c3-dea6a3b4aa63\n43420989-a09d-b8f8-9806-c8bbf7a02aac\n\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1 and sa.provider = 'salesforce';\n\nSELECT * FROM activities WHERE id = 75461988;\n\nSELECT * FROM activities WHERE uuid_to_bin('d6c5052e-e972-49e9-8912-26f2f7d6c5f6') = uuid;\n\nselect * from contacts where id = 17900517;\n\nselect * from contact_roles cr join crm_configurations crm on cr.crm_configuration_id = crm.id\nwhere crm.provider != 'salesforce';\n\nselect * from users where id = 21047;\nSELECT * FROM crm_configurations WHERE id = 892;\nSELECT * FROM teams WHERE id = 942;\nselect * from opportunities where team_id = 942 order by updated_at desc;\nselect * from contacts where team_id = 942 order by updated_at desc;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 942 and sa.provider = 'hubspot';\n\nSELECT * FROM opportunities where team_id = 1 and crm_provider_id IN ('006Pq00000NeH6XIAV', '006Pq000007z8kdIAA'); # 10697889, 6621430\nSELECT * FROM crm_configurations WHERE id = 1;\nSELECT * FROM teams WHERE crm_id = 1;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1 and sa.provider = 'salesforce';\n\nselect id, user_id, opportunity_fields from crm_profiles where crm_configuration_id = 1\nSELECT * FROM opportunities where team_id = 1 order by updated_at desc; # 10697889, 6621430\n\nselect * from teams where id = 852;\nselect * from groups where id = 2286;\nselect * from sidekick_settings where team_id = 852;\nselect * from default_activity_types where team_id = 852;\n\n\nSELECT cc.provider, cc.id, p.id, u.*\nFROM users u\nLEFT JOIN crm_profiles p ON u.id = p.user_id AND p.id IS NULL -- no profile\nINNER JOIN teams t ON u.team_id = t.id AND t.status = 'active' -- team is active\nINNER JOIN crm_configurations cc ON t.crm_id = cc.id\nWHERE u.status = 1 AND u.deleted_at IS NULL\nAND u.crm_required = 1\nAND u.team_id = 1\nORDER BY u.team_id;\n\nSELECT * FROM crm_profiles cp where cp.crm_configuration_id = 1 and cp.user_id IN (\n18481\n );\n\nSELECT cc.provider, cc.id, p.id, u.*\nFROM users u\nLEFT JOIN crm_profiles p ON u.id = p.user_id\nINNER JOIN teams t ON u.team_id = t.id AND t.status = 'active'\nINNER JOIN crm_configurations cc ON t.crm_id = cc.id\nWHERE u.status = 1\n AND u.deleted_at IS NULL\n AND u.crm_required = 1\n# AND u.team_id = 1\n AND p.id IS NULL -- Move this condition to WHERE clause\nORDER BY u.team_id;\n\nSELECT * FROM opportunities WHERE id = 20002609;\nselect * from teams where id = 1122; # Velatir, 29953 - christian@velatir.com\nselect * from crm_configurations where id = 1060;\nselect * from crm_layouts where crm_configuration_id = 1060;\nSELECT * FROM crm_layout_entities WHERE crm_layout_id = 3596;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1122 and sa.provider = 'hubspot';\nselect * from opportunities where team_id = 1122 order by updated_at desc;\n\nselect * from crm_field_data where object_type = 'contact';\n\nSELECT * FROM activities WHERE uuid_to_bin('374fc8ed-3315-4c9f-9b25-318b7fd2928f') = uuid; # 76584262\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 248 and sa.provider = 'salesforce';\n\nSELECT * FROM crm_profiles where user_id = 24115; # 005QF000002CswMYAS\nSELECT * FROM users where id = 24115;\nSELECT * FROM accounts where id = 4002896;\nSELECT * FROM teams WHERE name LIKE '%adswerve%';\nSELECT * FROM opportunities where crm_configuration_id = 230 AND crm_provider_id IN (\"0069N000003GIQ9QAO\",\"0061r000019yGP9AAM\",\"0066900001S2KWlAAN\",\"0066900001TDpj2AAD\",\"0066900001b8uEwAAI\",\"0069N000001rQi0QAE\",\"006QF00000KD40mYAD\",\"006QF00000LzpRJYAZ\",\"0069N000002uomtQAA\",\"0069N000002xlMLQAY\",\"0066900001NV6ubAAD\",\"0061r00001HJp45AAD\",\"006QF00000uTlUoYAK\",\"006QF00000v0bZqYAI\");\nSELECT * FROM opportunities WHERE crm_configuration_id = 230 AND crm_provider_id = '0069N000003GIQ9QAO'; # 6272203\n\nSELECT u.id, u.email, ac.name, a.* FROM activities a\nJOIN users u ON a.user_id = u.id\nJOIN accounts ac ON a.account_id = ac.id\nWHERE\nuuid_to_bin('e3269598-b562-44fb-b5e9-9d2694dc63e0') = a.uuid or\nuuid_to_bin('66ddc3ab-4e15-45aa-af0c-248c1eece593') = a.uuid or\nuuid_to_bin('826bd328-e1cc-4213-b8d8-572454cacc07') = a.uuid;\n\nselect * from users where id = 5825;\nSELECT * FROM activities WHERE uuid_to_bin('e56aa2e8-231a-421b-ab1f-cb38ed2bf573') = uuid;\n\nselect * from activities where uuid_to_bin('91e13b2f-2d1b-45f8-b1fd-1141b6563782') = uuid;\n19594, 862\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 862 and sa.provider = 'salesforce';\n\nselect * from automated_reports where id = 36;\nselect ar.frequency, r.*, ar.* from automated_report_results r\njoin automated_reports ar on r.report_id = ar.id\nwhere ar.frequency != 'one_off';\n\nselect s.* from activity_searches s join users u ON s.user_id = u.id where u.team_id = 882;\nselect * from nudges n where n.activity_search_id\n\nselect * from teams where created_at > '2026-03-09';\nSELECT * FROM crm_layouts WHERE crm_configuration_id = 1065; # 1065\nSELECT * FROM crm_layout_entities WHERE crm_layout_id = 3617;\n\nselect * from users where team_id = 1 and name like '%Lukas%'; # 7160\n\nSELECT * FROM teams WHERE id = 575;\nselect * from opportunities where team_id = 575;\nSELECT * FROM teams WHERE name LIKE '%Integrum ESG%'; # 1126, 1065,\nselect * from opportunities where team_id = 1126;\nSELECT * FROM teams WHERE name LIKE '%Base%'; # 1125, 1063,\nselect * from opportunities where team_id = 1125;\nselect * from contacts c\nwhere c.team_id = 882;\n\nSELECT * FROM activities WHERE id = 76822967;\nSELECT * FROM crm_profiles WHERE user_id = 15440;\nSELECT * FROM crm_profiles WHERE crm_configuration_id = 555;\nSELECT * FROM crm_configurations WHERE id = 555;\nSELECT * FROM users WHERE id = 15440; # team. 581, gr. 15440, pl. 3911, act. field 162182\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 581 and sa.provider = 'salesforce';\n\nSELECT * FROM automated_report_results order by id desc;\n\nselect * from features;\nselect * from team_features where feature_id = 40;\n\nselect * from teams where id = 556;\n\nselect * from automated_reports;\nwhere id = 54; # 4fdd41f6-dcf0-30d0-b339-7345381b6044 , [\"pdf\",\"podcast\"]\nSELECT * FROM automated_report_results WHERE uuid_to_bin('822fa41b-afd3-43a9-a248-86b0e36f3131') = uuid;\nselect * from automated_report_results order by id desc;\nSELECT * FROM automated_report_results WHERE id = 1919;\n\nselect * from automated_report_results WHERE report_id = 54;\n\nselect * from opportunities where id = 7594349;\n\nSELECT * FROM teams WHERE name LIKE '%Les%'; # 711, 692, 16067 - jiminnyintegration@lesmills.com\nselect * from playbooks where team_id = 711; # event 226147\nSELECT * FROM playbook_categories WHERE playbook_id = 5515;\nSELECT * FROM crm_fields WHERE crm_configuration_id = 692 and object_type = 'event';\nSELECT * FROM crm_fields WHERE id = 226147;\nSELECT * FROM crm_field_values WHERE crm_field_id = 226147;\n\nSELECT * FROM crm_configurations WHERE id = 692;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 711 and sa.provider = 'salesforce';\n\nSELECT * FROM crm_profiles cp JOIN users u on u.id = cp.user_id WHERE u.team_id = 711;\n\nselect * from leads;\n\nselect * from calendars;\n\nSELECT\n t.id AS team_id,\n t.name,\n LOWER(SUBSTRING_INDEX(c.calendar_provider_id, '@', -1)) AS calendar_domain\nFROM teams t\nJOIN users u ON u.team_id = t.id\nJOIN calendars c ON c.user_id = u.id AND c.status = 'active' AND c.calendar_provider_id LIKE '%@%'\nLEFT JOIN team_domains td\n ON td.team_id = t.id\n AND td.deleted_at IS NULL\n AND td.domain = LOWER(SUBSTRING_INDEX(c.calendar_provider_id, '@', -1))\nGROUP BY t.id, t.name, calendar_domain\nORDER BY t.name, calendar_domain;\n\nselect * from users u join calendars c on c.user_id = u.id\nwhere u.team_id = 882;\n\n\nselect * from activities where id = 74049485; # team 563 crm 537\nselect * from activities where id = 73272382; # team 563 crm 537\nselect * from activities where id = 64400389; # team 563 crm 537\nselect * from activities where id = 58081273; # team 563 crm 537\nselect * from activities where id = 54520297; # team 563 crm 537\nselect * from participants where activity_id = 58081273;\n\nselect * from activities where crm_configuration_id = 537 and provider = 'aircall'\nand account_id = 19003658 order by updated_at desc;\n\nselect * from contacts where crm_configuration_id = 537 and id = 35957759;\nselect * from accounts where crm_configuration_id = 537 and id = 19003658;\n\nselect * from automated_report_results where id = 1976;\nselect * from automated_reports where id = 583;\nselect * from activity_searches where id = 87714;\nselect * from activity_search_filters where activity_search_id = 87714;\n\nSELECT * FROM activities WHERE uuid_to_bin('8827f672-202d-4162-9d04-73ff5f0566a9') = uuid\nor uuid_to_bin('47842446-af51-4bcb-854f-cc6560290101') = uuid;\n\nSELECT * FROM crm_configurations WHERE provider = 'hubspot';\nselect * from rate_limits;\n\nselect * from automated_report_results where media_type = 'pdf' and status = 2\nand id IN (18, 1872);\nselect * from automated_reports where id = 54;\nSELECT * FROM users WHERE id IN (24623,29443,29613);\n\nSELECT * FROM automated_report_results WHERE uuid_to_bin('822fa41b-afd3-43a9-a248-86b0e36f3131') = uuid;\n\nselect * from text_relays where created_at > '2026-05-01';\nand id IN (32415, 32416);\n# and id = 32412;\n\nselect * from users where team_id = 2 and email like '%scott%' and id = 29510;\n\nSELECT * FROM activities WHERE uuid_to_bin('67cebfc2-ed56-44a2-8c68-7a0286ed8618') = uuid; # 79763436\n\nSELECT email_provider_id, COUNT(*) as count, GROUP_CONCAT(id) as ids, GROUP_CONCAT(status) as statuses\nFROM text_relays\nWHERE email_provider_id IN ('19e2027868a64b42', '19e2033ed8ea6b10')\nGROUP BY email_provider_id;\nSELECT id, status, telephony_provider_id, created_at\nFROM activities\nWHERE id IN (80028719, 80028846);\nSELECT id, status, code, email_sent_at, created_at, updated_at\nFROM text_relays\nWHERE id IN (32415, 32416);\nSELECT id, status, code, sender, recipient, created_at\nFROM text_relays\nWHERE sender LIKE '%mario.georgiev%' OR sender LIKE '%stoyan.tomov%'\nORDER BY created_at DESC\nLIMIT 10;\n\nSELECT id, uuid, status, code, sender, recipient, created_at, updated_at\nFROM text_relays\nWHERE uuid = uuid_to_bin('0626141c-27a6-4d8c-aff8-7c8020a2c656');\n\n# ***************\nSELECT DISTINCT u.id, u.email, u.name, u.softphone_number, COUNT(a.id) as sms_count\nFROM users u\nINNER JOIN activities a ON u.id = .user_id\nWHERE a.type LIKE 'sms%'\nAND a.created_at > DATE_SUB(NOW(), INTERVAL 30 DAY)\nGROUP BY u.id, u.email, u.name, u.softphone_number\nORDER BY sms_count DESC;\n\nselect * from teams where id = 1;\n\nselect * from roles;\n\n\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1117 and sa.provider = 'hubspot';\nSELECT * FROM activities WHERE uuid_to_bin('8024fffb-2df7-4017-91f4-d9f896050248') = uuid; # 79933459 YES\nSELECT * FROM activities WHERE uuid_to_bin('25529043-8094-4781-927f-4f4da2a8185c') = uuid; # 80186192 NO\nSELECT * FROM crm_configurations WHERE id = 1053;\nSELECT * FROM teams WHERE id = 1117;\nselect * from users where id = 30249;\nselect * from playbooks where id = 5473;\nselect * from playbook_categories where id = 43783;\nselect * from playbook_categories where playbook_id = 5473;\nselect * from crm_fields where id = 659242;\nselect * from crm_field_values where crm_field_id = 659242;\n\nSELECT * FROM crm_field_data fd\n# JOIN crm_fields f ON fd.crm_field_id = f.id\n# JOIN activities a ON fd.activity_id = a.id\nWHERE activity_id = 79933459\n# AND f.crm_provider_id = 'hs_activity_type';\n\n\nSELECT * FROM activity_messages;\nselect * from text_relays where created_at > '2026-05-01';\nselect * from activities where user_id IN (7160, 18608) and created_at > '2026-05-22' order by id desc;\nselect * from users where team_id = 1 and id IN (18608, 13934, 7160);\nselect * from activities where user_id = 7160 order by id desc limit 10;\n\nselect * from accounts where team_id = 1 and name = 'Column5';\n\nselect * from users where name like '%Subra%'; # 31054, 1117\nselect * from teams where id = 1117;\nselect * from activity_searches where user_id = 31054;\nselect * from activity_search_filters where activity_search_id IN (88882, 88902);","depth":4,"on_screen":true,"value":"SELECT * FROM teams WHERE name LIKE '%litify%'; # 1069, 994, 24993\nSELECT * FROM users WHERE id = 25061;\nSELECT * FROM crm_profiles WHERE crm_configuration_id = 994;\nSELECT * FROM crm_profiles WHERE user_id = 25061;\n\nselect * from crm_configurations where id = 834;\nSELECT * FROM teams WHERE id = 882;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 882 and sa.provider = 'hubspot';\n\nSELECT * FROM crm_configurations where id = 882; # 933 - GoGlobal\nSELECT * FROM opportunities WHERE team_id = 933 order by updated_at desc;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 933 and sa.provider = 'hubspot';\n\nSELECT * FROM crm_configurations WHERE provider = 'hubspot' and crm_provider_id = 7270388;\n\nSELECT * FROM contacts where crm_configuration_id = 834;\nSELECT * FROM opportunities WHERE team_id = 933\n# AND crm_provider_id IN ('20131586060','46017317898','52543911090','53451356564','54101251892','54323768459');\nAND id IN (8482561,18352941,19042734,19232139,19445140,19472541);\nSELECT * FROM opportunity_contacts\nWHERE opportunity_id IN (8482561,18352941,19042734,19232139,19445140,19472541);\n\n# ********************************************************************\nSELECT * FROM crm_configurations where id = 485; #\nSELECT * FROM opportunities WHERE team_id = 933 order by updated_at desc;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 933 and sa.provider = 'hubspot';\n\nselect crm.provider, l.* from leads l join crm_configurations crm on l.crm_configuration_id = crm.id\nwhere crm.provider NOT IN ('salesforce', 'integration-app', 'bullhorn', 'copper')\n# and l.converted_at IS NOT NULL\n;\n\n# ********************************************************************\nSELECT * FROM activities a WHERE type IN ('email-inbound', 'email-outbound')\nand opportunity_id IS NULL\norder by id desc;\n\nSELECT * FROM teams WHERE id = 604; # 598\nSELECT * FROM activities WHERE id = 74410828; # chelseaw@allvoices.co\nSELECT * FROM accounts WHERE id = 20068382;\nSELECT * FROM accounts WHERE id = 35186038;\n\nSELECT * FROM contacts WHERE team_id = 852 and updated_at > '2026-01-23 12:30:00' order by updated_at desc;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 559 and sa.provider = 'hubspot';\n\nSELECT * FROM activities WHERE uuid_to_bin('cb6342b6-a183-401c-b0af-ede92b2ae763') = uuid;\nselect * from sidekick_settings where team_id = 781;\n\nSELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id = 26651871; # Teya\nSELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id = 7562435;\nSELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id = 8420347; # opflit 2100\n\nSELECT * FROM crm_layouts WHERE crm_configuration_id = 711;\nSELECT * FROM activities where crm_configuration_id = 711 and crm_provider_id IS NULL\nand is_internal = 0 and status = 'completed'\norder by id desc;\n\nSELECT * FROM crm_layout_entities\nWHERE crm_layout_id IN (2352, 2353);\n;\n\nSELECT * FROM crm_configurations where provider = 'hubspot' and id = 530;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 556 and sa.provider = 'hubspot';\n\nSELECT * FROM activities WHERE uuid_to_bin('c6ca4b22-7738-4563-a95d-b8a9598924ae') = uuid;\nSELECT * FROM activities WHERE uuid_to_bin('442abb2b-28bd-4be8-9c25-19e9bf02766d') = uuid;\nselect * from contacts\nwhere crm_configuration_id = 530\nand crm_provider_id = 872252;\n\nselect * from activities where crm_configuration_id = 530\nand user_id = 14343 and type like '%softphone%'\nand created_at between '2026-01-28 15:00:00' and '2026-01-28 15:10:00';\n\nSELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id = 25666868; # Teya\nSELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id = 8646335; # Teya\nSELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id IN (5933397);\n\n\nSELECT t.name, t.id, t.owner_id, c.id, c.provider, c.crm_base_url FROM teams t\nJOIN crm_configurations c ON t.id = c.team_id\nWHERE t.status = 'active';\n\nSELECT * FROM teams where id = 1091;\nSELECT * FROM crm_configurations where team_id = 1091;\nSELECT * FROM activity_providers where team_id = 1091;\nSELECT * FROM activities where crm_configuration_id = 1024 and type IN ('softphone', 'softphone-outbound')\nand provider NOT IN ('hubspot', 'aircall')\n# and telephony_provider_id = '019c1131-a22f-4792-b9ea-20adf6a02ed0'\norder by id desc;\n\n\nSELECT * FROM teams WHERE name LIKE '%Leadventure%';\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1091 and sa.provider = 'salesforce';\n\nSELECT * FROM teams WHERE name LIKE '%Wilson%'; # 862, 812\nSELECT * FROM teams where id = 862;\nSELECT * FROM crm_configurations where team_id = 862;\nSELECT * FROM activity_providers where team_id = 862;\nSELECT * FROM activities where crm_configuration_id = 812 and type IN ('softphone', 'softphone-outbound')\nand provider NOT IN ('hubspot', 'aircall')\n# and telephony_provider_id = '019c1131-a22f-4792-b9ea-20adf6a02ed0'\norder by id desc;\n\n\nSELECT t.id, crm.id, crm.provider, ap.* FROM teams t\njoin crm_configurations crm on t.id = crm.team_id\njoin activity_providers ap on t.id = ap.team_id\nwhere t.status = 'active' and ap.is_enabled = 1\nand crm.provider = 'hubspot'\nand ap.provider NOT IN ('hubspot', 'aircall', 'uploader', 'gong', 'twilio', 'zoom-bot', 'google-meet', 'ms-teams',\n 'outreach', 'close', 'ringcentral', 'dialpad', 'zoom-phone');\n\nSELECT * FROM teams where id = 1068;\nSELECT * FROM crm_configurations where team_id = 1068;\nSELECT * FROM activity_providers where team_id = 1068;\n\nSELECT * FROM activities a\nwhere crm_configuration_id = 993 and type IN ('softphone', 'softphone-outbound')\nand a.provider NOT IN ('hubspot', 'uploader', 'gong', 'twilio', 'google-meet', 'ms-teams','close'\n )\n# and telephony_provider_id = '019c1131-a22f-4792-b9ea-20adf6a02ed0'\norder by a.id desc;\n\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1068 and sa.provider = 'hubspot';\n\n# ********************************************************************\n# ********************************************************************\n# ********************************************************************\nSELECT * FROM crm_configurations where id = 882; # 933 - GoGlobal , portalId: 6017093\nSELECT * FROM opportunities WHERE team_id = 933 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 933 and updated_at > '2026-02-06 00:00:00' order by updated_at desc;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 933 and sa.provider = 'hubspot';\n\n# ********************************************************************\nSELECT * FROM crm_configurations where id = 834; # 882 - AnyVan , portalId: 5468262\nSELECT * FROM contacts WHERE crm_configuration_id = 834 and updated_at > '2026-03-30 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE crm_configuration_id = 834 and updated_at > '2026-03-04 08:00:00' order by updated_at desc;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 882 and sa.provider = 'hubspot';\nselect * from crm_layouts where crm_configuration_id = 834;\nselect * from crm_layout_entities where crm_layout_id = 2780;\nselect * from crm_fields where id IN (321153,321192,321193,321194);\n\nSELECT * FROM opportunities WHERE crm_configuration_id = 834 and id = 10993426;\n# ********************************************************************\nSELECT * FROM crm_configurations where id = 988; # 1057 - Teya (543ce4f4-168c-4571-91ea-5b35c253f06f) , portalId: 26651871\nSELECT * FROM opportunities WHERE team_id = 1057 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 1057 and updated_at > '2026-02-04 00:00:00' order by updated_at desc;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1057 and sa.provider = 'hubspot';\n\nSELECT * FROM crm_configurations where id = 533; # 559 - Connectd , portalId: 6710988\nSELECT * FROM opportunities WHERE team_id = 559 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 559 and updated_at > '2026-02-04 00:00:00' order by updated_at desc;\n\nSELECT * FROM crm_configurations where id = 801; # 852 - Rise Vision , portalId: 2700250\nSELECT * FROM opportunities WHERE team_id = 852 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 852 and updated_at > '2026-02-04 00:00:00' order by updated_at desc; # 6th last\n\nSELECT * FROM crm_configurations where id = 962; # 1034 - evergrowth.io , portalId: 143180990\nSELECT * FROM opportunities WHERE team_id = 1034 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 1034 and updated_at > '2026-02-04 00:00:00' order by updated_at desc;\n\n# ********************************************************************\nSELECT * FROM crm_configurations where id = 1037; # 1102 - Jibble , portalId: 6649755\nSELECT * FROM opportunities WHERE team_id = 1102 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 1102 and updated_at > '2026-02-04 12:30:00' order by updated_at desc; # 8\n\nSELECT * FROM crm_configurations where id = 1015; # 1049 - Travefy , portalId: 48904401\nSELECT * FROM opportunities WHERE team_id = 1049 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 1049 and updated_at > '2026-02-04 12:30:00' order by updated_at desc; # 20\n\nSELECT * FROM crm_configurations where id = 64; # 70 - SalaryFinance , portalId: 3404115\nSELECT * FROM opportunities WHERE team_id = 70 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 70 and updated_at > '2026-02-04 12:30:00' order by updated_at desc; # 6th last\n\nSELECT * FROM crm_configurations where id = 802; # 853 - Street Group , portalId: 7658438\nSELECT * FROM opportunities WHERE team_id = 853 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 853 and updated_at > '2026-02-04 12:30:00' order by updated_at desc; # 10\n\nSELECT * FROM crm_configurations where id = 872; # 921 - In Professional Development , portalId: 9238273\nSELECT * FROM opportunities WHERE team_id = 921 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 921 and updated_at > '2026-02-04 12:30:00' order by updated_at desc; # 2\n\n# ********************************************************************\nSELECT * FROM crm_configurations where id = 550; # 576 - SeedLegals , portalId: 3028661\nSELECT * FROM opportunities WHERE team_id = 576 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 576 and updated_at > '2026-02-05 14:00:00' order by updated_at desc;\n\nSELECT * FROM crm_configurations where id = 989; # 1058 - rtaoutdoor.com , portalId: 22371204\nSELECT * FROM opportunities WHERE team_id = 1058 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 1058 and updated_at > '2026-02-05 14:00:00' order by updated_at desc;\n\nSELECT * FROM crm_configurations where id = 896; # 946 - Mintago , portalId: 6621281\nSELECT * FROM opportunities WHERE team_id = 946 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 946 and updated_at > '2026-02-05 14:00:00' order by updated_at desc;\n\nSELECT * FROM crm_configurations where id = 617; # 641 - PCS , portalId: 5244937\nSELECT * FROM opportunities WHERE team_id = 641 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 641 and updated_at > '2026-02-05 14:00:00' order by updated_at desc; # 7th\n# ********************************************************************\nSELECT * FROM crm_configurations where id = 649; # 670 - Eventeny , portalId: 4492849\nSELECT * FROM opportunities WHERE team_id = 670 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 670 and updated_at > '2026-02-09 08:00:00' order by updated_at desc; #\n\nSELECT * FROM crm_configurations where id = 48; # 51 - CleanCloud , portalId: 4373137\nSELECT * FROM opportunities WHERE team_id = 51 and updated_at > '2026-03-04 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 51 and updated_at > '2026-02-09 08:00:00' order by updated_at desc;\nselect * from users where team_id = 51; # 7783\nSELECT * FROM groups WHERE uuid_to_bin('8a8d2cb6-8b55-4fa3-8b5c-5f0e3d8de59a') = uuid; # 1130\nselect * from activity_searches where user_id = 7783;\nselect * from activity_search_filters where activity_search_id IN (32291, 32292);\n\nSELECT asf.activity_search_id, asf.id, asf.value\nFROM activity_search_filters asf\nWHERE asf.filter = 'group_id'\nAND asf.value IN (\n SELECT CONCAT(\n HEX(SUBSTR(uuid, 5, 4)), '-',\n HEX(SUBSTR(uuid, 3, 2)), '-',\n HEX(SUBSTR(uuid, 1, 2)), '-',\n HEX(SUBSTR(uuid, 9, 2)), '-',\n HEX(SUBSTR(uuid, 11))\n )\n FROM groups\n WHERE deleted_at IS NOT NULL\n);\n\nSELECT * FROM crm_configurations where id = 272; # 290 - Bonham & Brook , portalId: 5705856\nSELECT * FROM opportunities WHERE team_id = 290 and updated_at > '2026-02-05 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 290 and updated_at > '2026-02-09 08:00:00' order by updated_at desc; # 6th\n# ********************************************************************\nSELECT * FROM crm_configurations where provider = 'hubspot';\nSELECT * FROM crm_configurations where id = 1056; # 1119 - Chromatic , portalId: 45602133\nSELECT * FROM opportunities WHERE team_id = 1119 and remotely_created_at > '2026-02-01 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 1119 and updated_at > '2026-02-09 09:00:00' order by updated_at desc; # null\n# ********************************************************************\n\nselect * from contacts where crm_provider_id = '003Uu00000ojD4NIAU';\nselect\n cp.*\n# DISTINCT t.id\n# cp.id, cp.user_id, t.id, cp.crm_configuration_id, cp.contact_fields\nFROM crm_profiles cp\nJOIN crm_configurations crm on crm.id = cp.crm_configuration_id\nJOIN users u on u.id = cp.user_id\nJOIN teams t ON t.id = crm.team_id\nWHERE crm.provider = 'salesforce' and t.status = 'active'\n and cp.archived_at IS NULL and u.deleted_at IS NULL\n and t.id NOT IN (1093)\n and t.id = 2\n and cp.contact_fields IS NULL;\n# and c.crm_provider_id = '003Uu00000ojD4NIAU';\n\nSELECT * FROM users WHERE id = 26484;\nSELECT * FROM crm_profiles WHERE user_id = 26484;\nSELECT * FROM social_accounts WHERE sociable_id = 26484;\nSELECT * FROM crm_configurations where provider = 'salesforce';\nselect * from users where id IN (10022, 10403);\nselect * from users where team_id IN (526);\nselect * from teams where id IN (526, 532);\nselect * from crm_configurations where id IN (500, 516);\nselect * from crm_profiles where crm_configuration_id IN (500, 516) and user_id IN (10022, 10403);\nselect * from contacts where crm_configuration_id IN (500, 516) and crm_provider_id = '003Uu00000ojD4NIAU';\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 526 and sa.provider = 'salesforce';\nselect * from team_settings where team_id IN (526, 532);\n\nselect * from users where id IN (22824);\nselect * from crm_profiles where crm_configuration_id IN (1026);\n\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1093 and sa.provider = 'salesforce';\n\nselect * from teams where id = 1099;\nselect * from users where id = 29643\n\nselect * from activity_processing_states;\n\nSELECT * FROM teams where name LIKE '%Fare%'; # 233\nSELECT * FROM opportunities where crm_configuration_id = 215\n# and crm_provider_id = 'oppo_ogESZf2P50nDrd1nGPvKDXeA6sSaTN5v51Lp4ayVzKR'\n;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1088 and sa.provider = 'hubspot';\n\nSELECT * FROM teams order by updated_at DESC\nSELECT * FROM crm_configurations WHERE id = 1019; # SimpleConsign 1088 - no social account\n\nselect * from crm_configurations where provider = 'pipedrive';\n\nselect * from teams where id = 957;\nselect * from crm_configurations where id = 957;\n\nSELECT * FROM teams WHERE name LIKE '%Prolific%'; # 544, 518, 10743\nSELECT * FROM opportunities where crm_configuration_id = 518 order by id desc;\n\nselect * from users where team_id = 1; # 26726 - Gabriela Dureva\nSELECT * FROM opportunities where user_id = 26726; # 16834447 - Prolific\nselect * from activities where user_id = 26726 order by id desc;\nselect * from contacts where crm_configuration_id = 1\nand email IN ('charlotte.ward@prolific.com', 'frankie.bryant@prolific.com'); # 2094416, 2093620\nSELECT * FROM contacts WHERE id = 6284931;\n\nSELECT p.* FROM activities a JOIN participants p ON a.id = p.activity_id\nWHERE a.user_id = 26726 and p.lead_id IN (2094416, 2093620) and a.created_at > '2026-01-01 00:00:00' order by p.email;\n\nselect * from activities where id IN (75509259,75509261,75509261,75511034,75026464,75517602,75517605);\nselect * from crm_configurations where id = 1;\n\n43801692-1aeb-32ce-acba-5b80a479701a\n44c3c9cf-6f5e-75f3-8179-bc9f75dd2b1b\n405975c0-b3d0-7aaa-821f-09d59cae6dd1\n4caf848d-4bed-2299-b248-7788d41f9fca\n49bedc3f-f196-eef3-89c3-dea6a3b4aa63\n43420989-a09d-b8f8-9806-c8bbf7a02aac\n\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1 and sa.provider = 'salesforce';\n\nSELECT * FROM activities WHERE id = 75461988;\n\nSELECT * FROM activities WHERE uuid_to_bin('d6c5052e-e972-49e9-8912-26f2f7d6c5f6') = uuid;\n\nselect * from contacts where id = 17900517;\n\nselect * from contact_roles cr join crm_configurations crm on cr.crm_configuration_id = crm.id\nwhere crm.provider != 'salesforce';\n\nselect * from users where id = 21047;\nSELECT * FROM crm_configurations WHERE id = 892;\nSELECT * FROM teams WHERE id = 942;\nselect * from opportunities where team_id = 942 order by updated_at desc;\nselect * from contacts where team_id = 942 order by updated_at desc;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 942 and sa.provider = 'hubspot';\n\nSELECT * FROM opportunities where team_id = 1 and crm_provider_id IN ('006Pq00000NeH6XIAV', '006Pq000007z8kdIAA'); # 10697889, 6621430\nSELECT * FROM crm_configurations WHERE id = 1;\nSELECT * FROM teams WHERE crm_id = 1;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1 and sa.provider = 'salesforce';\n\nselect id, user_id, opportunity_fields from crm_profiles where crm_configuration_id = 1\nSELECT * FROM opportunities where team_id = 1 order by updated_at desc; # 10697889, 6621430\n\nselect * from teams where id = 852;\nselect * from groups where id = 2286;\nselect * from sidekick_settings where team_id = 852;\nselect * from default_activity_types where team_id = 852;\n\n\nSELECT cc.provider, cc.id, p.id, u.*\nFROM users u\nLEFT JOIN crm_profiles p ON u.id = p.user_id AND p.id IS NULL -- no profile\nINNER JOIN teams t ON u.team_id = t.id AND t.status = 'active' -- team is active\nINNER JOIN crm_configurations cc ON t.crm_id = cc.id\nWHERE u.status = 1 AND u.deleted_at IS NULL\nAND u.crm_required = 1\nAND u.team_id = 1\nORDER BY u.team_id;\n\nSELECT * FROM crm_profiles cp where cp.crm_configuration_id = 1 and cp.user_id IN (\n18481\n );\n\nSELECT cc.provider, cc.id, p.id, u.*\nFROM users u\nLEFT JOIN crm_profiles p ON u.id = p.user_id\nINNER JOIN teams t ON u.team_id = t.id AND t.status = 'active'\nINNER JOIN crm_configurations cc ON t.crm_id = cc.id\nWHERE u.status = 1\n AND u.deleted_at IS NULL\n AND u.crm_required = 1\n# AND u.team_id = 1\n AND p.id IS NULL -- Move this condition to WHERE clause\nORDER BY u.team_id;\n\nSELECT * FROM opportunities WHERE id = 20002609;\nselect * from teams where id = 1122; # Velatir, 29953 - christian@velatir.com\nselect * from crm_configurations where id = 1060;\nselect * from crm_layouts where crm_configuration_id = 1060;\nSELECT * FROM crm_layout_entities WHERE crm_layout_id = 3596;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1122 and sa.provider = 'hubspot';\nselect * from opportunities where team_id = 1122 order by updated_at desc;\n\nselect * from crm_field_data where object_type = 'contact';\n\nSELECT * FROM activities WHERE uuid_to_bin('374fc8ed-3315-4c9f-9b25-318b7fd2928f') = uuid; # 76584262\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 248 and sa.provider = 'salesforce';\n\nSELECT * FROM crm_profiles where user_id = 24115; # 005QF000002CswMYAS\nSELECT * FROM users where id = 24115;\nSELECT * FROM accounts where id = 4002896;\nSELECT * FROM teams WHERE name LIKE '%adswerve%';\nSELECT * FROM opportunities where crm_configuration_id = 230 AND crm_provider_id IN (\"0069N000003GIQ9QAO\",\"0061r000019yGP9AAM\",\"0066900001S2KWlAAN\",\"0066900001TDpj2AAD\",\"0066900001b8uEwAAI\",\"0069N000001rQi0QAE\",\"006QF00000KD40mYAD\",\"006QF00000LzpRJYAZ\",\"0069N000002uomtQAA\",\"0069N000002xlMLQAY\",\"0066900001NV6ubAAD\",\"0061r00001HJp45AAD\",\"006QF00000uTlUoYAK\",\"006QF00000v0bZqYAI\");\nSELECT * FROM opportunities WHERE crm_configuration_id = 230 AND crm_provider_id = '0069N000003GIQ9QAO'; # 6272203\n\nSELECT u.id, u.email, ac.name, a.* FROM activities a\nJOIN users u ON a.user_id = u.id\nJOIN accounts ac ON a.account_id = ac.id\nWHERE\nuuid_to_bin('e3269598-b562-44fb-b5e9-9d2694dc63e0') = a.uuid or\nuuid_to_bin('66ddc3ab-4e15-45aa-af0c-248c1eece593') = a.uuid or\nuuid_to_bin('826bd328-e1cc-4213-b8d8-572454cacc07') = a.uuid;\n\nselect * from users where id = 5825;\nSELECT * FROM activities WHERE uuid_to_bin('e56aa2e8-231a-421b-ab1f-cb38ed2bf573') = uuid;\n\nselect * from activities where uuid_to_bin('91e13b2f-2d1b-45f8-b1fd-1141b6563782') = uuid;\n19594, 862\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 862 and sa.provider = 'salesforce';\n\nselect * from automated_reports where id = 36;\nselect ar.frequency, r.*, ar.* from automated_report_results r\njoin automated_reports ar on r.report_id = ar.id\nwhere ar.frequency != 'one_off';\n\nselect s.* from activity_searches s join users u ON s.user_id = u.id where u.team_id = 882;\nselect * from nudges n where n.activity_search_id\n\nselect * from teams where created_at > '2026-03-09';\nSELECT * FROM crm_layouts WHERE crm_configuration_id = 1065; # 1065\nSELECT * FROM crm_layout_entities WHERE crm_layout_id = 3617;\n\nselect * from users where team_id = 1 and name like '%Lukas%'; # 7160\n\nSELECT * FROM teams WHERE id = 575;\nselect * from opportunities where team_id = 575;\nSELECT * FROM teams WHERE name LIKE '%Integrum ESG%'; # 1126, 1065,\nselect * from opportunities where team_id = 1126;\nSELECT * FROM teams WHERE name LIKE '%Base%'; # 1125, 1063,\nselect * from opportunities where team_id = 1125;\nselect * from contacts c\nwhere c.team_id = 882;\n\nSELECT * FROM activities WHERE id = 76822967;\nSELECT * FROM crm_profiles WHERE user_id = 15440;\nSELECT * FROM crm_profiles WHERE crm_configuration_id = 555;\nSELECT * FROM crm_configurations WHERE id = 555;\nSELECT * FROM users WHERE id = 15440; # team. 581, gr. 15440, pl. 3911, act. field 162182\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 581 and sa.provider = 'salesforce';\n\nSELECT * FROM automated_report_results order by id desc;\n\nselect * from features;\nselect * from team_features where feature_id = 40;\n\nselect * from teams where id = 556;\n\nselect * from automated_reports;\nwhere id = 54; # 4fdd41f6-dcf0-30d0-b339-7345381b6044 , [\"pdf\",\"podcast\"]\nSELECT * FROM automated_report_results WHERE uuid_to_bin('822fa41b-afd3-43a9-a248-86b0e36f3131') = uuid;\nselect * from automated_report_results order by id desc;\nSELECT * FROM automated_report_results WHERE id = 1919;\n\nselect * from automated_report_results WHERE report_id = 54;\n\nselect * from opportunities where id = 7594349;\n\nSELECT * FROM teams WHERE name LIKE '%Les%'; # 711, 692, 16067 - jiminnyintegration@lesmills.com\nselect * from playbooks where team_id = 711; # event 226147\nSELECT * FROM playbook_categories WHERE playbook_id = 5515;\nSELECT * FROM crm_fields WHERE crm_configuration_id = 692 and object_type = 'event';\nSELECT * FROM crm_fields WHERE id = 226147;\nSELECT * FROM crm_field_values WHERE crm_field_id = 226147;\n\nSELECT * FROM crm_configurations WHERE id = 692;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 711 and sa.provider = 'salesforce';\n\nSELECT * FROM crm_profiles cp JOIN users u on u.id = cp.user_id WHERE u.team_id = 711;\n\nselect * from leads;\n\nselect * from calendars;\n\nSELECT\n t.id AS team_id,\n t.name,\n LOWER(SUBSTRING_INDEX(c.calendar_provider_id, '@', -1)) AS calendar_domain\nFROM teams t\nJOIN users u ON u.team_id = t.id\nJOIN calendars c ON c.user_id = u.id AND c.status = 'active' AND c.calendar_provider_id LIKE '%@%'\nLEFT JOIN team_domains td\n ON td.team_id = t.id\n AND td.deleted_at IS NULL\n AND td.domain = LOWER(SUBSTRING_INDEX(c.calendar_provider_id, '@', -1))\nGROUP BY t.id, t.name, calendar_domain\nORDER BY t.name, calendar_domain;\n\nselect * from users u join calendars c on c.user_id = u.id\nwhere u.team_id = 882;\n\n\nselect * from activities where id = 74049485; # team 563 crm 537\nselect * from activities where id = 73272382; # team 563 crm 537\nselect * from activities where id = 64400389; # team 563 crm 537\nselect * from activities where id = 58081273; # team 563 crm 537\nselect * from activities where id = 54520297; # team 563 crm 537\nselect * from participants where activity_id = 58081273;\n\nselect * from activities where crm_configuration_id = 537 and provider = 'aircall'\nand account_id = 19003658 order by updated_at desc;\n\nselect * from contacts where crm_configuration_id = 537 and id = 35957759;\nselect * from accounts where crm_configuration_id = 537 and id = 19003658;\n\nselect * from automated_report_results where id = 1976;\nselect * from automated_reports where id = 583;\nselect * from activity_searches where id = 87714;\nselect * from activity_search_filters where activity_search_id = 87714;\n\nSELECT * FROM activities WHERE uuid_to_bin('8827f672-202d-4162-9d04-73ff5f0566a9') = uuid\nor uuid_to_bin('47842446-af51-4bcb-854f-cc6560290101') = uuid;\n\nSELECT * FROM crm_configurations WHERE provider = 'hubspot';\nselect * from rate_limits;\n\nselect * from automated_report_results where media_type = 'pdf' and status = 2\nand id IN (18, 1872);\nselect * from automated_reports where id = 54;\nSELECT * FROM users WHERE id IN (24623,29443,29613);\n\nSELECT * FROM automated_report_results WHERE uuid_to_bin('822fa41b-afd3-43a9-a248-86b0e36f3131') = uuid;\n\nselect * from text_relays where created_at > '2026-05-01';\nand id IN (32415, 32416);\n# and id = 32412;\n\nselect * from users where team_id = 2 and email like '%scott%' and id = 29510;\n\nSELECT * FROM activities WHERE uuid_to_bin('67cebfc2-ed56-44a2-8c68-7a0286ed8618') = uuid; # 79763436\n\nSELECT email_provider_id, COUNT(*) as count, GROUP_CONCAT(id) as ids, GROUP_CONCAT(status) as statuses\nFROM text_relays\nWHERE email_provider_id IN ('19e2027868a64b42', '19e2033ed8ea6b10')\nGROUP BY email_provider_id;\nSELECT id, status, telephony_provider_id, created_at\nFROM activities\nWHERE id IN (80028719, 80028846);\nSELECT id, status, code, email_sent_at, created_at, updated_at\nFROM text_relays\nWHERE id IN (32415, 32416);\nSELECT id, status, code, sender, recipient, created_at\nFROM text_relays\nWHERE sender LIKE '%mario.georgiev%' OR sender LIKE '%stoyan.tomov%'\nORDER BY created_at DESC\nLIMIT 10;\n\nSELECT id, uuid, status, code, sender, recipient, created_at, updated_at\nFROM text_relays\nWHERE uuid = uuid_to_bin('0626141c-27a6-4d8c-aff8-7c8020a2c656');\n\n# ***************\nSELECT DISTINCT u.id, u.email, u.name, u.softphone_number, COUNT(a.id) as sms_count\nFROM users u\nINNER JOIN activities a ON u.id = .user_id\nWHERE a.type LIKE 'sms%'\nAND a.created_at > DATE_SUB(NOW(), INTERVAL 30 DAY)\nGROUP BY u.id, u.email, u.name, u.softphone_number\nORDER BY sms_count DESC;\n\nselect * from teams where id = 1;\n\nselect * from roles;\n\n\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1117 and sa.provider = 'hubspot';\nSELECT * FROM activities WHERE uuid_to_bin('8024fffb-2df7-4017-91f4-d9f896050248') = uuid; # 79933459 YES\nSELECT * FROM activities WHERE uuid_to_bin('25529043-8094-4781-927f-4f4da2a8185c') = uuid; # 80186192 NO\nSELECT * FROM crm_configurations WHERE id = 1053;\nSELECT * FROM teams WHERE id = 1117;\nselect * from users where id = 30249;\nselect * from playbooks where id = 5473;\nselect * from playbook_categories where id = 43783;\nselect * from playbook_categories where playbook_id = 5473;\nselect * from crm_fields where id = 659242;\nselect * from crm_field_values where crm_field_id = 659242;\n\nSELECT * FROM crm_field_data fd\n# JOIN crm_fields f ON fd.crm_field_id = f.id\n# JOIN activities a ON fd.activity_id = a.id\nWHERE activity_id = 79933459\n# AND f.crm_provider_id = 'hs_activity_type';\n\n\nSELECT * FROM activity_messages;\nselect * from text_relays where created_at > '2026-05-01';\nselect * from activities where user_id IN (7160, 18608) and created_at > '2026-05-22' order by id desc;\nselect * from users where team_id = 1 and id IN (18608, 13934, 7160);\nselect * from activities where user_id = 7160 order by id desc limit 10;\n\nselect * from accounts where team_id = 1 and name = 'Column5';\n\nselect * from users where name like '%Subra%'; # 31054, 1117\nselect * from teams where id = 1117;\nselect * from activity_searches where user_id = 31054;\nselect * from activity_search_filters where activity_search_id IN (88882, 88902);","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
6586266914579292006
|
2218652951799805519
|
click
|
accessibility
|
NULL
|
Project: faVsco.js, menu
#12121 on JY-20963-fix-im Project: faVsco.js, menu
#12121 on JY-20963-fix-import-on-deleted-entity, menu
Start Listening for PHP Debug Connections
ServiceTest
Run 'ServiceTest'
Debug 'ServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
22
5
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Crm;
use Illuminate\Support\Collection;
use Jiminny\Contracts\Repositories\TeamRepository;
use Jiminny\Contracts\Services\Crm\ServiceInterface;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Models\Account;
use Jiminny\Models\Activity;
use Jiminny\Models\Contact;
use Jiminny\Models\Lead;
use Jiminny\Models\Opportunity;
use Jiminny\Models\Participant;
use Jiminny\Models\Stage;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Services\ResolveTeamCrmConnection;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Psr\Log\LoggerInterface;
use Exception;
use Throwable;
class CrmActivityService
{
public function __construct(
private readonly TeamRepository $teamRepository,
private readonly CachedCrmServiceDecorator $decorator,
private readonly EmailHelper $emailHelper,
private readonly ResolveTeamCrmConnection $teamCrmResolver,
private readonly LoggerInterface $logger,
) {
}
/**
* Updates CRM data for an activity and its participants.
*
* NOTE: This method performs multiple database writes and should be called
* within a transaction by the caller to ensure atomicity.
*
* @param Activity $activity
* @param bool $remoteSearch
*
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception
*/
public function updateCrmData(
Activity $activity,
bool $remoteSearch = false,
): void {
$crmService = null;
$participants = $activity->getParticipants();
$team = $activity->getTeam();
$prospectSearchStrategy = ProspectSearchStrategyFactory::match($team);
if ($prospectSearchStrategy->ignoreCrmMatchData()) {
$this->logger->info('[CrmActivityService] Ignoring crm data because of prospect strategy', [
'activity_id' => $activity->getId(),
'strategy' => get_class($prospectSearchStrategy),
]);
return;
}
if ($remoteSearch) {
try {
$crmService = $this->teamCrmResolver->resolveForTeam($team);
} catch (SocialAccountTokenInvalidException) {
$this->logger->warning('[CrmActivityService] CRM token expired, falling back to local search', [
'activity_id' => $activity->getId(),
'team_id' => $team->getId(),
]);
}
}
$records = $this->updateParticipantsCrmData(
team: $team,
activity: $activity,
participants: $participants,
crmService: $crmService,
);
if (! empty($records)) {
$activity->updateActivityCrmData($records);
}
$activity->refresh();
}
/**
* @param Collection<Participant> $participants
*
* @throws Exception
*
* @return array{
* Lead|null,
* Account|null,
* Opportunity|null,
* Contact|null,
* Stage|null,
* string|null
*}|array{}
*/
private function updateParticipantsCrmData(
Team $team,
Activity $activity,
Collection $participants,
?ServiceInterface $crmService = null,
): array {
$matchedRecords = [];
$matchedDomainRecords = [];
$this->validateCrmConfiguration($activity);
$this->decorator->setConfiguration($activity->getCrm());
$this->decorator->setCrmService($crmService);
foreach ($participants as $participant) {
if ($this->shouldSkipParticipant($participant)) {
continue;
}
if (! $this->shouldPerformLookup($participant, $team)) {
$this->logger->info('[CrmActivityService] Email domain belongs to the team, skipping crm lookup', [
'activity_id' => $activity->getId(),
'team_id' => $team->getId(),
'email' => $participant->getEmailAddress(),
]);
$this->attachUserIfExists($participant, $team);
continue;
}
$records = $this->findCrmRecords($participant, $activity);
if (! empty($records)) {
$matchedRecords[] = $records;
} else {
$records = $this->findCrmDomainRecords(
crmService: $crmService,
participant: $participant,
activity: $activity,
);
if (! empty($records)) {
$matchedDomainRecords[] = $records;
}
}
if (empty($records)) {
continue;
}
try {
$activity->updateParticipantCrmData($records, $participant);
} catch (Throwable $ex) {
$this->logger->error('[CrmActivityService] Failed to update participant CRM data', [
'activity_id' => $activity->getId(),
'participant_id' => $participant->getId(),
'exception' => $ex->getMessage(),
]);
continue;
}
}
$bestMatch = $this->getBestMatch(
matchedRecords : $matchedRecords,
matchedDomainRecords: $matchedDomainRecords,
);
$this->logger->info('[CrmActivityService] CRM matching completed', [
'activity_id' => $activity->getId(),
'participants_processed' => $participants->count(),
'exact_matches' => count($matchedRecords),
'domain_matches' => count($matchedDomainRecords),
'best_match_found' => ! empty($bestMatch),
]);
return $bestMatch;
}
private function shouldPerformLookup(Participant $participant, Team $team): bool
{
if ($participant->hasEmailAddress()) {
return $this->emailHelper->shouldPerformLookup($team, $participant->getEmailAddress());
}
return true;
}
private function validateCrmConfiguration(Activity $activity): void
{
if ($activity->getCrm() === null) {
throw new InvalidArgumentException('Cannot find CRM configuration');
}
}
private function getBestMatch(?array $matchedRecords, ?array $matchedDomainRecords): array
{
return RecordSelector::pickBestFromLists($matchedRecords, $matchedDomainRecords);
}
private function findCrmRecords(Participant $participant, Activity $activity): ?array
{
$records = null;
if ($participant->hasEmailAddress()) {
$records = $this->decorator->matchExactlyByEmail(
email: $participant->getEmailAddress(),
userId: $activity->getUser()->getId()
);
}
if (empty($records) && $participant->getPhoneNumber() !== null) {
$records = $this->decorator->matchByPhone(
phone: $participant->getPhoneNumber(),
userId: $activity->getUser()->getId(),
);
}
if (empty($records) && $participant->getName() !== null) {
$records = $this->decorator->matchByName(
name: $participant->getName(),
userId: $activity->getUser()->getId(),
);
}
return $records;
}
private function shouldSkipParticipant(Participant $participant): bool
{
return $participant->hasUser();
}
private function attachUserIfExists(Participant $participant, Team $team): void
{
if ($participant->hasEmailAddress() === false) {
return;
}
$user = $this->teamRepository->findActiveTeamMemberByEmail($team, $participant->getEmailAddress());
if ($user instanceof User) {
$participant->user_id = $user->getId();
$participant->save();
}
}
private function findCrmDomainRecords(
?ServiceInterface $crmService,
Participant $participant,
Activity $activity,
): array {
if ($participant->hasEmailAddress()) {
$this->decorator->setConfiguration($activity->getCrm());
$this->decorator->setCrmService($crmService);
$records = $this->decorator->matchByDomain(
email: $participant->getEmailAddress(),
userId: $activity->getUser()->getId()
);
if (! empty($records)) {
return $records;
}
}
return [];
}
}
Execute
Explain Plan
Browse Query History
View Parameters
Open Query Execution Settings…
In-Editor Results
Tx: Auto
Cancel Running Statements
Playground
jiminny
Sync Changes
Hide This Notification
Code changed:
Hide
45
1
41
66
Previous Highlighted Error
Next Highlighted Error
SELECT * FROM teams WHERE name LIKE '%litify%'; # 1069, 994, 24993
SELECT * FROM users WHERE id = 25061;
SELECT * FROM crm_profiles WHERE crm_configuration_id = 994;
SELECT * FROM crm_profiles WHERE user_id = 25061;
select * from crm_configurations where id = 834;
SELECT * FROM teams WHERE id = 882;
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 882 and sa.provider = 'hubspot';
SELECT * FROM crm_configurations where id = 882; # 933 - GoGlobal
SELECT * FROM opportunities WHERE team_id = 933 order by updated_at desc;
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 933 and sa.provider = 'hubspot';
SELECT * FROM crm_configurations WHERE provider = 'hubspot' and crm_provider_id = 7270388;
SELECT * FROM contacts where crm_configuration_id = 834;
SELECT * FROM opportunities WHERE team_id = 933
# AND crm_provider_id IN ('20131586060','46017317898','52543911090','53451356564','54101251892','54323768459');
AND id IN (8482561,18352941,19042734,19232139,19445140,19472541);
SELECT * FROM opportunity_contacts
WHERE opportunity_id IN (8482561,18352941,19042734,19232139,19445140,19472541);
# [PASSWORD_DOTS]
SELECT * FROM crm_configurations where id = 485; #
SELECT * FROM opportunities WHERE team_id = 933 order by updated_at desc;
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 933 and sa.provider = 'hubspot';
select crm.provider, l.* from leads l join crm_configurations crm on l.crm_configuration_id = crm.id
where crm.provider NOT IN ('salesforce', 'integration-app', 'bullhorn', 'copper')
# and l.converted_at IS NOT NULL
;
# [PASSWORD_DOTS]
SELECT * FROM activities a WHERE type IN ('email-inbound', 'email-outbound')
and opportunity_id IS NULL
order by id desc;
SELECT * FROM teams WHERE id = 604; # 598
SELECT * FROM activities WHERE id = 74410828; # [EMAIL]
SELECT * FROM accounts WHERE id = 20068382;
SELECT * FROM accounts WHERE id = 35186038;
SELECT * FROM contacts WHERE team_id = 852 and updated_at > '2026-01-23 12:30:00' order by updated_at desc;
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 559 and sa.provider = 'hubspot';
SELECT * FROM activities WHERE uuid_to_bin('cb6342b6-a183-401c-b0af-ede92b2ae763') = uuid;
select * from sidekick_settings where team_id = 781;
SELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id = 26651871; # Teya
SELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id = 7562435;
SELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id = 8420347; # opflit 2100
SELECT * FROM crm_layouts WHERE crm_configuration_id = 711;
SELECT * FROM activities where crm_configuration_id = 711 and crm_provider_id IS NULL
and is_internal = 0 and status = 'completed'
order by id desc;
SELECT * FROM crm_layout_entities
WHERE crm_layout_id IN (2352, 2353);
;
SELECT * FROM crm_configurations where provider = 'hubspot' and id = 530;
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 556 and sa.provider = 'hubspot';
SELECT * FROM activities WHERE uuid_to_bin('c6ca4b22-7738-4563-a95d-b8a9598924ae') = uuid;
SELECT * FROM activities WHERE uuid_to_bin('442abb2b-28bd-4be8-9c25-19e9bf02766d') = uuid;
select * from contacts
where crm_configuration_id = 530
and crm_provider_id = 872252;
select * from activities where crm_configuration_id = 530
and user_id = 14343 and type like '%softphone%'
and created_at between '2026-01-28 15:00:00' and '2026-01-28 15:10:00';
SELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id = 25666868; # Teya
SELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id = 8646335; # Teya
SELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id IN (5933397);
SELECT t.name, t.id, t.owner_id, c.id, c.provider, c.crm_base_url FROM teams t
JOIN crm_configurations c ON t.id = c.team_id
WHERE t.status = 'active';
SELECT * FROM teams where id = 1091;
SELECT * FROM crm_configurations where team_id = 1091;
SELECT * FROM activity_providers where team_id = 1091;
SELECT * FROM activities where crm_configuration_id = 1024 and type IN ('softphone', 'softphone-outbound')
and provider NOT IN ('hubspot', 'aircall')
# and telephony_provider_id = '019c1131-a22f-4792-b9ea-20adf6a02ed0'
order by id desc;
SELECT * FROM teams WHERE name LIKE '%Leadventure%';
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 1091 and sa.provider = 'salesforce';
SELECT * FROM teams WHERE name LIKE '%Wilson%'; # 862, 812
SELECT * FROM teams where id = 862;
SELECT * FROM crm_configurations where team_id = 862;
SELECT * FROM activity_providers where team_id = 862;
SELECT * FROM activities where crm_configuration_id = 812 and type IN ('softphone', 'softphone-outbound')
and provider NOT IN ('hubspot', 'aircall')
# and telephony_provider_id = '019c1131-a22f-4792-b9ea-20adf6a02ed0'
order by id desc;
SELECT t.id, crm.id, crm.provider, ap.* FROM teams t
join crm_configurations crm on t.id = crm.team_id
join activity_providers ap on t.id = ap.team_id
where t.status = 'active' and ap.is_enabled = 1
and crm.provider = 'hubspot'
and ap.provider NOT IN ('hubspot', 'aircall', 'uploader', 'gong', 'twilio', 'zoom-bot', 'google-meet', 'ms-teams',
'outreach', 'close', 'ringcentral', 'dialpad', 'zoom-phone');
SELECT * FROM teams where id = 1068;
SELECT * FROM crm_configurations where team_id = 1068;
SELECT * FROM activity_providers where team_id = 1068;
SELECT * FROM activities a
where crm_configuration_id = 993 and type IN ('softphone', 'softphone-outbound')
and a.provider NOT IN ('hubspot', 'uploader', 'gong', 'twilio', 'google-meet', 'ms-teams','close'
)
# and telephony_provider_id = '019c1131-a22f-4792-b9ea-20adf6a02ed0'
order by a.id desc;
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 1068 and sa.provider = 'hubspot';
# [PASSWORD_DOTS]
# [PASSWORD_DOTS]
# [PASSWORD_DOTS]
SELECT * FROM crm_configurations where id = 882; # 933 - GoGlobal , portalId: 6017093
SELECT * FROM opportunities WHERE team_id = 933 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 933 and updated_at > '2026-02-06 00:00:00' order by updated_at desc;
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 933 and sa.provider = 'hubspot';
# [PASSWORD_DOTS]
SELECT * FROM crm_configurations where id = 834; # 882 - AnyVan , portalId: 5468262
SELECT * FROM contacts WHERE crm_configuration_id = 834 and updated_at > '2026-03-30 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE crm_configuration_id = 834 and updated_at > '2026-03-04 08:00:00' order by updated_at desc;
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 882 and sa.provider = 'hubspot';
select * from crm_layouts where crm_configuration_id = 834;
select * from crm_layout_entities where crm_layout_id = 2780;
select * from crm_fields where id IN (321153,321192,321193,321194);
SELECT * FROM opportunities WHERE crm_configuration_id = 834 and id = 10993426;
# [PASSWORD_DOTS]
SELECT * FROM crm_configurations where id = 988; # 1057 - Teya (543ce4f4-168c-4571-91ea-5b35c253f06f) , portalId: 26651871
SELECT * FROM opportunities WHERE team_id = 1057 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 1057 and updated_at > '2026-02-04 00:00:00' order by updated_at desc;
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 1057 and sa.provider = 'hubspot';
SELECT * FROM crm_configurations where id = 533; # 559 - Connectd , portalId: 6710988
SELECT * FROM opportunities WHERE team_id = 559 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 559 and updated_at > '2026-02-04 00:00:00' order by updated_at desc;
SELECT * FROM crm_configurations where id = 801; # 852 - Rise Vision , portalId: 2700250
SELECT * FROM opportunities WHERE team_id = 852 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 852 and updated_at > '2026-02-04 00:00:00' order by updated_at desc; # 6th last
SELECT * FROM crm_configurations where id = 962; # 1034 - evergrowth.io , portalId: 143180990
SELECT * FROM opportunities WHERE team_id = 1034 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 1034 and updated_at > '2026-02-04 00:00:00' order by updated_at desc;
# [PASSWORD_DOTS]
SELECT * FROM crm_configurations where id = 1037; # 1102 - Jibble , portalId: 6649755
SELECT * FROM opportunities WHERE team_id = 1102 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 1102 and updated_at > '2026-02-04 12:30:00' order by updated_at desc; # 8
SELECT * FROM crm_configurations where id = 1015; # 1049 - Travefy , portalId: 48904401
SELECT * FROM opportunities WHERE team_id = 1049 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 1049 and updated_at > '2026-02-04 12:30:00' order by updated_at desc; # 20
SELECT * FROM crm_configurations where id = 64; # 70 - SalaryFinance , portalId: 3404115
SELECT * FROM opportunities WHERE team_id = 70 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 70 and updated_at > '2026-02-04 12:30:00' order by updated_at desc; # 6th last
SELECT * FROM crm_configurations where id = 802; # 853 - Street Group , portalId: 7658438
SELECT * FROM opportunities WHERE team_id = 853 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 853 and updated_at > '2026-02-04 12:30:00' order by updated_at desc; # 10
SELECT * FROM crm_configurations where id = 872; # 921 - In Professional Development , portalId: 9238273
SELECT * FROM opportunities WHERE team_id = 921 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 921 and updated_at > '2026-02-04 12:30:00' order by updated_at desc; # 2
# [PASSWORD_DOTS]
SELECT * FROM crm_configurations where id = 550; # 576 - SeedLegals , portalId: 3028661
SELECT * FROM opportunities WHERE team_id = 576 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 576 and updated_at > '2026-02-05 14:00:00' order by updated_at desc;
SELECT * FROM crm_configurations where id = 989; # 1058 - rtaoutdoor.com , portalId: 22371204
SELECT * FROM opportunities WHERE team_id = 1058 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 1058 and updated_at > '2026-02-05 14:00:00' order by updated_at desc;
SELECT * FROM crm_configurations where id = 896; # 946 - Mintago , portalId: 6621281
SELECT * FROM opportunities WHERE team_id = 946 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 946 and updated_at > '2026-02-05 14:00:00' order by updated_at desc;
SELECT * FROM crm_configurations where id = 617; # 641 - PCS , portalId: 5244937
SELECT * FROM opportunities WHERE team_id = 641 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 641 and updated_at > '2026-02-05 14:00:00' order by updated_at desc; # 7th
# [PASSWORD_DOTS]
SELECT * FROM crm_configurations where id = 649; # 670 - Eventeny , portalId: 4492849
SELECT * FROM opportunities WHERE team_id = 670 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 670 and updated_at > '2026-02-09 08:00:00' order by updated_at desc; #
SELECT * FROM crm_configurations where id = 48; # 51 - CleanCloud , portalId: 4373137
SELECT * FROM opportunities WHERE team_id = 51 and updated_at > '2026-03-04 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 51 and updated_at > '2026-02-09 08:00:00' order by updated_at desc;
select * from users where team_id = 51; # 7783
SELECT * FROM groups WHERE uuid_to_bin('8a8d2cb6-8b55-4fa3-8b5c-5f0e3d8de59a') = uuid; # 1130
select * from activity_searches where user_id = 7783;
select * from activity_search_filters where activity_search_id IN (32291, 32292);
SELECT asf.activity_search_id, asf.id, asf.value
FROM activity_search_filters asf
WHERE asf.filter = 'group_id'
AND asf.value IN (
SELECT CONCAT(
HEX(SUBSTR(uuid, 5, 4)), '-',
HEX(SUBSTR(uuid, 3, 2)), '-',
HEX(SUBSTR(uuid, 1, 2)), '-',
HEX(SUBSTR(uuid, 9, 2)), '-',
HEX(SUBSTR(uuid, 11))
)
FROM groups
WHERE deleted_at IS NOT NULL
);
SELECT * FROM crm_configurations where id = 272; # 290 - Bonham & Brook , portalId: 5705856
SELECT * FROM opportunities WHERE team_id = 290 and updated_at > '2026-02-05 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 290 and updated_at > '2026-02-09 08:00:00' order by updated_at desc; # 6th
# [PASSWORD_DOTS]
SELECT * FROM crm_configurations where provider = 'hubspot';
SELECT * FROM crm_configurations where id = 1056; # 1119 - Chromatic , portalId: 45602133
SELECT * FROM opportunities WHERE team_id = 1119 and remotely_created_at > '2026-02-01 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 1119 and updated_at > '2026-02-09 09:00:00' order by updated_at desc; # null
# [PASSWORD_DOTS]
select * from contacts where crm_provider_id = '003Uu00000ojD4NIAU';
select
cp.*
# DISTINCT t.id
# cp.id, cp.user_id, t.id, cp.crm_configuration_id, cp.contact_fields
FROM crm_profiles cp
JOIN crm_configurations crm on crm.id = cp.crm_configuration_id
JOIN users u on u.id = cp.user_id
JOIN teams t ON t.id = crm.team_id
WHERE crm.provider = 'salesforce' and t.status = 'active'
and cp.archived_at IS NULL and u.deleted_at IS NULL
and t.id NOT IN (1093)
and t.id = 2
and cp.contact_fields IS NULL;
# and c.crm_provider_id = '003Uu00000ojD4NIAU';
SELECT * FROM users WHERE id = 26484;
SELECT * FROM crm_profiles WHERE user_id = 26484;
SELECT * FROM social_accounts WHERE sociable_id = 26484;
SELECT * FROM crm_configurations where provider = 'salesforce';
select * from users where id IN (10022, 10403);
select * from users where team_id IN (526);
select * from teams where id IN (526, 532);
select * from crm_configurations where id IN (500, 516);
select * from crm_profiles where crm_configuration_id IN (500, 516) and user_id IN (10022, 10403);
select * from contacts where crm_configuration_id IN (500, 516) and crm_provider_id = '003Uu00000ojD4NIAU';
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 526 and sa.provider = 'salesforce';
select * from team_settings where team_id IN (526, 532);
select * from users where id IN (22824);
select * from crm_profiles where crm_configuration_id IN (1026);
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 1093 and sa.provider = 'salesforce';
select * from teams where id = 1099;
select * from users where id = 29643
select * from activity_processing_states;
SELECT * FROM teams where name LIKE '%Fare%'; # 233
SELECT * FROM opportunities where crm_configuration_id = 215
# and crm_provider_id = 'oppo_ogESZf2P50nDrd1nGPvKDXeA6sSaTN5v51Lp4ayVzKR'
;
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 1088 and sa.provider = 'hubspot';
SELECT * FROM teams order by updated_at DESC
SELECT * FROM crm_configurations WHERE id = 1019; # SimpleConsign 1088 - no social account
select * from crm_configurations where provider = 'pipedrive';
select * from teams where id = 957;
select * from crm_configurations where id = 957;
SELECT * FROM teams WHERE name LIKE '%Prolific%'; # 544, 518, 10743
SELECT * FROM opportunities where crm_configuration_id = 518 order by id desc;
select * from users where team_id = 1; # 26726 - Gabriela Dureva
SELECT * FROM opportunities where user_id = 26726; # 16834447 - Prolific
select * from activities where user_id = 26726 order by id desc;
select * from contacts where crm_configuration_id = 1
and email IN ('[EMAIL]', '[EMAIL]'); # 2094416, 2093620
SELECT * FROM contacts WHERE id = 6284931;
SELECT p.* FROM activities a JOIN participants p ON a.id = p.activity_id
WHERE a.user_id = 26726 and p.lead_id IN (2094416, 2093620) and a.created_at > '2026-01-01 00:00:00' order by p.email;
select * from activities where id IN (75509259,75509261,75509261,75511034,75026464,75517602,75517605);
select * from crm_configurations where id = 1;
43801692-1aeb-32ce-acba-5b80a479701a
44c3c9cf-6f5e-75f3-8179-bc9f75dd2b1b
405975c0-b3d0-7aaa-821f-09d59cae6dd1
4caf848d-4bed-2299-b248-7788d41f9fca
49bedc3f-f196-eef3-89c3-dea6a3b4aa63
43420989-a09d-b8f8-9806-c8bbf7a02aac
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 1 and sa.provider = 'salesforce';
SELECT * FROM activities WHERE id = 75461988;
SELECT * FROM activities WHERE uuid_to_bin('d6c5052e-e972-49e9-8912-26f2f7d6c5f6') = uuid;
select * from contacts where id = 17900517;
select * from contact_roles cr join crm_configurations crm on cr.crm_configuration_id = crm.id
where crm.provider != 'salesforce';
select * from users where id = 21047;
SELECT * FROM crm_configurations WHERE id = 892;
SELECT * FROM teams WHERE id = 942;
select * from opportunities where team_id = 942 order by updated_at desc;
select * from contacts where team_id = 942 order by updated_at desc;
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 942 and sa.provider = 'hubspot';
SELECT * FROM opportunities where team_id = 1 and crm_provider_id IN ('006Pq00000NeH6XIAV', '006Pq000007z8kdIAA'); # 10697889, 6621430
SELECT * FROM crm_configurations WHERE id = 1;
SELECT * FROM teams WHERE crm_id = 1;
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 1 and sa.provider = 'salesforce';
select id, user_id, opportunity_fields from crm_profiles where crm_configuration_id = 1
SELECT * FROM opportunities where team_id = 1 order by updated_at desc; # 10697889, 6621430
select * from teams where id = 852;
select * from groups where id = 2286;
select * from sidekick_settings where team_id = 852;
select * from default_activity_types where team_id = 852;
SELECT cc.provider, cc.id, p.id, u.*
FROM users u
LEFT JOIN crm_profiles p ON u.id = p.user_id AND p.id IS NULL -- no profile
INNER JOIN teams t ON u.team_id = t.id AND t.status = 'active' -- team is active
INNER JOIN crm_configurations cc ON t.crm_id = cc.id
WHERE u.status = 1 AND u.deleted_at IS NULL
AND u.crm_required = 1
AND u.team_id = 1
ORDER BY u.team_id;
SELECT * FROM crm_profiles cp where cp.crm_configuration_id = 1 and cp.user_id IN (
18481
);
SELECT cc.provider, cc.id, p.id, u.*
FROM users u
LEFT JOIN crm_profiles p ON u.id = p.user_id
INNER JOIN teams t ON u.team_id = t.id AND t.status = 'active'
INNER JOIN crm_configurations cc ON t.crm_id = cc.id
WHERE u.status = 1
AND u.deleted_at IS NULL
AND u.crm_required = 1
# AND u.team_id = 1
AND p.id IS NULL -- Move this condition to WHERE clause
ORDER BY u.team_id;
SELECT * FROM opportunities WHERE id = 20002609;
select * from teams where id = 1122; # Velatir, 29953 - [EMAIL]
select * from crm_configurations where id = 1060;
select * from crm_layouts where crm_configuration_id = 1060;
SELECT * FROM crm_layout_entities WHERE crm_layout_id = 3596;
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 1122 and sa.provider = 'hubspot';
select * from opportunities where team_id = 1122 order by updated_at desc;
select * from crm_field_data where object_type = 'contact';
SELECT * FROM activities WHERE uuid_to_bin('374fc8ed-3315-4c9f-9b25-318b7fd2928f') = uuid; # 76584262
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 248 and sa.provider = 'salesforce';
SELECT * FROM crm_profiles where user_id = 24115; # 005QF000002CswMYAS
SELECT * FROM users where id = 24115;
SELECT * FROM accounts where id = 4002896;
SELECT * FROM teams WHERE name LIKE '%adswerve%';
SELECT * FROM opportunities where crm_configuration_id = 230 AND crm_provider_id IN ("0069N000003GIQ9QAO","0061r000019yGP9AAM","0066900001S2KWlAAN","0066900001TDpj2AAD","0066900001b8uEwAAI","0069N000001rQi0QAE","006QF00000KD40mYAD","006QF00000LzpRJYAZ","0069N000002uomtQAA","0069N000002xlMLQAY","0066900001NV6ubAAD","0061r00001HJp45AAD","006QF00000uTlUoYAK","006QF00000v0bZqYAI");
SELECT * FROM opportunities WHERE crm_configuration_id = 230 AND crm_provider_id = '0069N000003GIQ9QAO'; # 6272203
SELECT u.id, u.email, ac.name, a.* FROM activities a
JOIN users u ON a.user_id = u.id
JOIN accounts ac ON a.account_id = ac.id
WHERE
uuid_to_bin('e3269598-b562-44fb-b5e9-9d2694dc63e0') = a.uuid or
uuid_to_bin('66ddc3ab-4e15-45aa-af0c-248c1eece593') = a.uuid or
uuid_to_bin('826bd328-e1cc-4213-b8d8-572454cacc07') = a.uuid;
select * from users where id = 5825;
SELECT * FROM activities WHERE uuid_to_bin('e56aa2e8-231a-421b-ab1f-cb38ed2bf573') = uuid;
select * from activities where uuid_to_bin('91e13b2f-2d1b-45f8-b1fd-1141b6563782') = uuid;
19594, 862
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 862 and sa.provider = 'salesforce';
select * from automated_reports where id = 36;
select ar.frequency, r.*, ar.* from automated_report_results r
join automated_reports ar on r.report_id = ar.id
where ar.frequency != 'one_off';
select s.* from activity_searches s join users u ON s.user_id = u.id where u.team_id = 882;
select * from nudges n where n.activity_search_id
select * from teams where created_at > '2026-03-09';
SELECT * FROM crm_layouts WHERE crm_configuration_id = 1065; # 1065
SELECT * FROM crm_layout_entities WHERE crm_layout_id = 3617;
select * from users where team_id = 1 and name like '%Lukas%'; # 7160
SELECT * FROM teams WHERE id = 575;
select * from opportunities where team_id = 575;
SELECT * FROM teams WHERE name LIKE '%Integrum ESG%'; # 1126, 1065,
select * from opportunities where team_id = 1126;
SELECT * FROM teams WHERE name LIKE '%Base%'; # 1125, 1063,
select * from opportunities where team_id = 1125;
select * from contacts c
where c.team_id = 882;
SELECT * FROM activities WHERE id = 76822967;
SELECT * FROM crm_profiles WHERE user_id = 15440;
SELECT * FROM crm_profiles WHERE crm_configuration_id = 555;
SELECT * FROM crm_configurations WHERE id = 555;
SELECT * FROM users WHERE id = 15440; # team. 581, gr. 15440, pl. 3911, act. field 162182
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 581 and sa.provider = 'salesforce';
SELECT * FROM automated_report_results order by id desc;
select * from features;
select * from team_features where feature_id = 40;
select * from teams where id = 556;
select * from automated_reports;
where id = 54; # 4fdd41f6-dcf0-30d0-b339-7345381b6044 , ["pdf","podcast"]
SELECT * FROM automated_report_results WHERE uuid_to_bin('822fa41b-afd3-43a9-a248-86b0e36f3131') = uuid;
select * from automated_report_results order by id desc;
SELECT * FROM automated_report_results WHERE id = 1919;
select * from automated_report_results WHERE report_id = 54;
select * from opportunities where id = 7594349;
SELECT * FROM teams WHERE name LIKE '%Les%'; # 711, 692, 16067 - [EMAIL]
select * from playbooks where team_id = 711; # event 226147
SELECT * FROM playbook_categories WHERE playbook_id = 5515;
SELECT * FROM crm_fields WHERE crm_configuration_id = 692 and object_type = 'event';
SELECT * FROM crm_fields WHERE id = 226147;
SELECT * FROM crm_field_values WHERE crm_field_id = 226147;
SELECT * FROM crm_configurations WHERE id = 692;
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 711 and sa.provider = 'salesforce';
SELECT * FROM crm_profiles cp JOIN users u on u.id = cp.user_id WHERE u.team_id = 711;
select * from leads;
select * from calendars;
SELECT
t.id AS team_id,
t.name,
LOWER(SUBSTRING_INDEX(c.calendar_provider_id, '@', -1)) AS calendar_domain
FROM teams t
JOIN users u ON u.team_id = t.id
JOIN calendars c ON c.user_id = u.id AND c.status = 'active' AND c.calendar_provider_id LIKE '%@%'
LEFT JOIN team_domains td
ON td.team_id = t.id
AND td.deleted_at IS NULL
AND td.domain = LOWER(SUBSTRING_INDEX(c.calendar_provider_id, '@', -1))
GROUP BY t.id, t.name, calendar_domain
ORDER BY t.name, calendar_domain;
select * from users u join calendars c on c.user_id = u.id
where u.team_id = 882;
select * from activities where id = 74049485; # team 563 crm 537
select * from activities where id = 73272382; # team 563 crm 537
select * from activities where id = 64400389; # team 563 crm 537
select * from activities where id = 58081273; # team 563 crm 537
select * from activities where id = 54520297; # team 563 crm 537
select * from participants where activity_id = 58081273;
select * from activities where crm_configuration_id = 537 and provider = 'aircall'
and account_id = 19003658 order by updated_at desc;
select * from contacts where crm_configuration_id = 537 and id = 35957759;
select * from accounts where crm_configuration_id = 537 and id = 19003658;
select * from automated_report_results where id = 1976;
select * from automated_reports where id = 583;
select * from activity_searches where id = 87714;
select * from activity_search_filters where activity_search_id = 87714;
SELECT * FROM activities WHERE uuid_to_bin('8827f672-202d-4162-9d04-73ff5f0566a9') = uuid
or uuid_to_bin('47842446-af51-4bcb-854f-cc6560290101') = uuid;
SELECT * FROM crm_configurations WHERE provider = 'hubspot';
select * from rate_limits;
select * from automated_report_results where media_type = 'pdf' and status = 2
and id IN (18, 1872);
select * from automated_reports where id = 54;
SELECT * FROM users WHERE id IN (24623,29443,29613);
SELECT * FROM automated_report_results WHERE uuid_to_bin('822fa41b-afd3-43a9-a248-86b0e36f3131') = uuid;
select * from text_relays where created_at > '2026-05-01';
and id IN (32415, 32416);
# and id = 32412;
select * from users where team_id = 2 and email like '%scott%' and id = 29510;
SELECT * FROM activities WHERE uuid_to_bin('67cebfc2-ed56-44a2-8c68-7a0286ed8618') = uuid; # 79763436
SELECT email_provider_id, COUNT(*) as count, GROUP_CONCAT(id) as ids, GROUP_CONCAT(status) as statuses
FROM text_relays
WHERE email_provider_id IN ('19e2027868a64b42', '19e2033ed8ea6b10')
GROUP BY email_provider_id;
SELECT id, status, telephony_provider_id, created_at
FROM activities
WHERE id IN (80028719, 80028846);
SELECT id, status, code, email_sent_at, created_at, updated_at
FROM text_relays
WHERE id IN (32415, 32416);
SELECT id, status, code, sender, recipient, created_at
FROM text_relays
WHERE sender LIKE '%mario.georgiev%' OR sender LIKE '%stoyan.tomov%'
ORDER BY created_at DESC
LIMIT 10;
SELECT id, uuid, status, code, sender, recipient, created_at, updated_at
FROM text_relays
WHERE uuid = uuid_to_bin('0626141c-27a6-4d8c-aff8-7c8020a2c656');
# [PASSWORD_DOTS]
SELECT DISTINCT u.id, u.email, u.name, u.softphone_number, COUNT(a.id) as sms_count
FROM users u
INNER JOIN activities a ON u.id = .user_id
WHERE a.type LIKE 'sms%'
AND a.created_at > DATE_SUB(NOW(), INTERVAL 30 DAY)
GROUP BY u.id, u.email, u.name, u.softphone_number
ORDER BY sms_count DESC;
select * from teams where id = 1;
select * from roles;
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 1117 and sa.provider = 'hubspot';
SELECT * FROM activities WHERE uuid_to_bin('8024fffb-2df7-4017-91f4-d9f896050248') = uuid; # 79933459 YES
SELECT * FROM activities WHERE uuid_to_bin('[CREDIT_CARD]-927f-4f4da2a8185c') = uuid; # 80186192 NO
SELECT * FROM crm_configurations WHERE id = 1053;
SELECT * FROM teams WHERE id = 1117;
select * from users where id = 30249;
select * from playbooks where id = 5473;
select * from playbook_categories where id = 43783;
select * from playbook_categories where playbook_id = 5473;
select * from crm_fields where id = 659242;
select * from crm_field_values where crm_field_id = 659242;
SELECT * FROM crm_field_data fd
# JOIN crm_fields f ON fd.crm_field_id = f.id
# JOIN activities a ON fd.activity_id = a.id
WHERE activity_id = 79933459
# AND f.crm_provider_id = 'hs_activity_type';
SELECT * FROM activity_messages;
select * from text_relays where created_at > '2026-05-01';
select * from activities where user_id IN (7160, 18608) and created_at > '2026-05-22' order by id desc;
select * from users where team_id = 1 and id IN (18608, 13934, 7160);
select * from activities where user_id = 7160 order by id desc limit 10;
select * from accounts where team_id = 1 and name = 'Column5';
select * from users where name like '%Subra%'; # 31054, 1117
select * from teams where id = 1117;
select * from activity_searches where user_id = 31054;
select * from activity_search_filters where activity_search_id IN (88882, 88902);...
|
86398
|
NULL
|
NULL
|
NULL
|
|
86400
|
2962
|
14
|
2026-05-28T14:07:38.772533+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-28/1779 /Users/lukas/.screenpipe/data/data/2026-05-28/1779977258772_m1.jpg...
|
PhpStorm
|
faVsco.js – CrmActivityService.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
#12121 on JY-20963-fix-im Project: faVsco.js, menu
#12121 on JY-20963-fix-import-on-deleted-entity, menu
Start Listening for PHP Debug Connections
ServiceTest
Run 'ServiceTest'
Debug 'ServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"#12121 on JY-20963-fix-import-on-deleted-entity, menu","depth":5,"on_screen":true,"help_text":"Pull request #12121 exists for current branch JY-20963-fix-import-on-deleted-entity, but pull request details loading failed","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"ServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'ServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'ServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
5582423643801155883
|
-8994321436789994556
|
click
|
hybrid
|
NULL
|
Project: faVsco.js, menu
#12121 on JY-20963-fix-im Project: faVsco.js, menu
#12121 on JY-20963-fix-import-on-deleted-entity, menu
Start Listening for PHP Debug Connections
ServiceTest
Run 'ServiceTest'
Debug 'ServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
JAlActivity MonitorFileEditViewWindowHelpActivity MonitorAll ProcessesProcess Name% CPUkernel_taskPhpStormVirtual Machine Service for DockerscreenpipeWindowServerreplaydFirefoxCP Isolated Web ContentClaudeFirefoxcoreaudiodiTerm2Slack HelperActivity MonitorSlack Helper (Renderer)language_server_macos_armFirefoxCP Isolated Web ContentWispr Flow Helper (Renderer)FirefoxCP Isolated Web ContentclouddFirefoxCP Isolated Web ContentSlackWispr FlowidleassetsdKarabiner-Core-ServiceFirefoxCP Isolated Web ContentBitwardenNotion Helper (Renderer)backupd180,5172,595,758,353,749,310,56,25,55,34,94,73,93,93,73,13,13,02,72,52,42,22,02,01,91,81,61,6CPU Time22:48:08,472:33:19,151:52:42,623:34:54,887:50:24,495:42:16,3933:41,1528:22,061:46:45,0057:02,291:03:29,7714:25,526:47,2357:25,497:29,2415:13,643:45,5929:46,5427,1317:15,9213:14,552:36,402,4119:49,792:03,932:03,5623:57,0754,26System:User:Idle:Threads CPUMemoryEnergyDiskNetworkIdle Wake-UpsKind % GPUAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleApple43.58%56,42%0,00%0,07,70,00,043,00,00,00,00,00,00,54,80,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,0CPU LOADThreads:Processes:GPU Time0,007:47,720,005:03,903:20:06,990,000,000,000,040,0019,386:06,690,000,000,000,000,000,000,000,000,050,000,000,000,000,000,000,00‹ >0 (hlPID Userrootlukaslukaslukas_windowserverlukaslukaslukaslukas_coreaudiodlukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukasrootrootlukaslukaslukasroot100% C8• Thu 28 May 17:07:38Search...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
86398
|
2963
|
10
|
2026-05-28T14:07:31.721771+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-28/1779 /Users/lukas/.screenpipe/data/data/2026-05-28/1779977251721_m2.jpg...
|
PhpStorm
|
faVsco.js – CrmActivityService.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
rapstomvewNeweNNCCoocKetucioWindowFV faVsco.s ~#12 rapstomvewNeweNNCCoocKetucioWindowFV faVsco.s ~#12121 on JY-20963-fx-inAdtwtyontroller.onga service.phiasanyiceteston>mutkewolcnnedvilycimbato.ongIa Wethookc Batchsynceo ecror.orrcRatchSuncRedeCanrce.o© Clent.phoc eoseddeaistarcosermieuDealFieldsService.php© DecorateActivity.phge feldaetinitions.ono© FieldTypeConverter.pho(© HubspotClientinterface.ph@ HubspotTokenManager.ot =7© PayloadBuilder.phoc) Remotcermocrec.wan .oulResponseNormalize.phoc) service.ono© SyncFieidAction.phocSuncrelateohcwiywanx17© WebhookSvncBatchProce> IntegrationApp› @ Listeners→lweradatal18Motaten→p nedriveMh Salesforce• FaldeMi Onnortun tMatchenOpportunitySyncStrategyProspectSearchStrategyIh Sennicetraird© Clent.phg© DecorateActivity.phpDeleteObiectsTrait.phpe Faldnafoitione mho© PayloadBuilder.pho(©) Profile.pho207© QueryBuilder.php© QueryHandler.pho208© Querviterator.ohc© QueryResuits.pho(c) Service,oho@ SvncBatchRedisService.of 24€la TraitsC BaseClient.php© BaseService.ohe© CachedCrmServiceDecoratoc countrvcoderesolver ohdCrmActivitvProviderintegrateCTermcv tsarioe.oodclermeontautatoncet doccerclermoniac shaes wero@ nelanttDrosneetSaarchStrate 27:© CrmActivityService.php xC Activity.php©) Team.phpclass cruacevzcyserviceprivate function updateParticipantsCrnData(EooersestratChiiRecords: SmatchedDomainRecords,Fo("[ConAct.v.tySenvice) CRH matching conpleted', LSactus tyosaettdlol._processed' => Sparticipants->count.s' => count(SmatchedRecords)es" => count (SmatchedDomainRecords),ound" =>! empty($bestMatch).› LdPerfornLookup(Particinant Sparticipant. Team Steam): bool ...=jateCrnConfiguration(Activity Sactivity): voidi.…astMatch(2array SmatchedRecords, ?array SaatchedDonainRecords): arravf...;> ConRecords(Particioant Sparticinant. Actávity Sactivity): 2arravá...}sekopart erinentpanta einant ShantscsnantebooittaashuserIfExists(Partieipant Sparticipant, Tean Stean): voidf..CardonsfnRecondewwwttw/wiew oureoneetsnmioutas.nedinu Lo moy twursServiceTestA2ASAY= custom.loglSF [minny@localhost)A console (EUTA console [STAGING"imiamyAND a.created at > DATE SUB(NOWO, INTERVAL 38 DAY)GROUP RY u.sd urenaitl, u.nane. M. softahone nunberORDER BY sms count DESC045 A1 A41 Y 66 Aselect * from teans where 1d = 1select * fron rolesCONCAT(U.1d, CASE WHEN U.1d = t.owner_id THEN • (ouner)' ELSE ** END) AS user_1d.t.oaner id FROM social_accounts saJoiN users u on u.1d = sa.soczablc1d1.n<->1: on taid = u.tean_idWUIO D7OSASASS YESneeweles HerE wisd to mnwssyoeuosecuuieotucomasseeuusor2891864197W10hereoie shSes where 5d= 43783select * fron playbook_categories where playbook_id = 5473Seld values where com 6ield d = 659262SSLEN + FROM CA 4e1d datar+IOTM cos Erelde & OM Edl eo €reld3d= €.30•IOTN ACtvtes A OM £dl sctsustu 3a =93dRERTuserid IN (7168, 18688) and created at > *2826-85-22' order by id descaselect * fron users where tean_id = 1 and id IN (18688. 13934, 7160)=select * fron activities where user_id = 7168 order by id desc linit 18:select * fron accountsselect * fron usenswhere nane Like '%Subrax': # 31054. 1117aetuity searches where lusen oe syisasparch &ilters where actiuity seanch ia TN (9998), BROR)):111|Cecsdales Orcnnworceoeionh1/ 0rosers/ tuxos/2Toreach (Sparticiponts as spariicipant)Srecords = Sthis-s1indCreRecords(Sparticipant, Sactivity):coly piccororritonter tiit w::wwinrecorddlierarnch. kerserth/l & This nay call Salesforce::matchByDomain()Step 7: Domain Search tnggers opportunity synoboin senrh eirs necount ens drinee" withi relaired oncorunt/l To get opportunity details, it calls:Scraservicessyndopponenstylscirtovzoerto):# couts aaporcopportunztyl)Step 8: The bua - importo pportunity creates temporary Recoro/Users/lukas/1m1nny/app/app/Services/cra/Salestorce/Servicc.php:1430-1448oriivate tunctionimoort@sportunt tylScr iatal: 20000rtuntvVxino early chelete chack8a. Restore fron trash (tine 1430)SthcesrestorAcyTrachwenttylsthiisescontossomortntlesol. SerDataloToebuny na sests wich valbld0io.l0ones34305833Sopportunity = Sthsooruaeolnoalasosaat RosborunitweseVADio la.or7hosy5yiumoor edusin thesid edine42sthices coort DocortuntverFelData (Ser Datar SorBtelds, Conoortunt tyesd)eM8ar Noh celeteit ansin itine 1444Sthis-shandle0bjectDeletion(Sopportunity, ScrnData):Sopportuntty=trashedIl Methad returs null, ButAat anuthientuat4 space...
|
NULL
|
3023331138368611587
|
NULL
|
click
|
ocr
|
NULL
|
rapstomvewNeweNNCCoocKetucioWindowFV faVsco.s ~#12 rapstomvewNeweNNCCoocKetucioWindowFV faVsco.s ~#12121 on JY-20963-fx-inAdtwtyontroller.onga service.phiasanyiceteston>mutkewolcnnedvilycimbato.ongIa Wethookc Batchsynceo ecror.orrcRatchSuncRedeCanrce.o© Clent.phoc eoseddeaistarcosermieuDealFieldsService.php© DecorateActivity.phge feldaetinitions.ono© FieldTypeConverter.pho(© HubspotClientinterface.ph@ HubspotTokenManager.ot =7© PayloadBuilder.phoc) Remotcermocrec.wan .oulResponseNormalize.phoc) service.ono© SyncFieidAction.phocSuncrelateohcwiywanx17© WebhookSvncBatchProce> IntegrationApp› @ Listeners→lweradatal18Motaten→p nedriveMh Salesforce• FaldeMi Onnortun tMatchenOpportunitySyncStrategyProspectSearchStrategyIh Sennicetraird© Clent.phg© DecorateActivity.phpDeleteObiectsTrait.phpe Faldnafoitione mho© PayloadBuilder.pho(©) Profile.pho207© QueryBuilder.php© QueryHandler.pho208© Querviterator.ohc© QueryResuits.pho(c) Service,oho@ SvncBatchRedisService.of 24€la TraitsC BaseClient.php© BaseService.ohe© CachedCrmServiceDecoratoc countrvcoderesolver ohdCrmActivitvProviderintegrateCTermcv tsarioe.oodclermeontautatoncet doccerclermoniac shaes wero@ nelanttDrosneetSaarchStrate 27:© CrmActivityService.php xC Activity.php©) Team.phpclass cruacevzcyserviceprivate function updateParticipantsCrnData(EooersestratChiiRecords: SmatchedDomainRecords,Fo("[ConAct.v.tySenvice) CRH matching conpleted', LSactus tyosaettdlol._processed' => Sparticipants->count.s' => count(SmatchedRecords)es" => count (SmatchedDomainRecords),ound" =>! empty($bestMatch).› LdPerfornLookup(Particinant Sparticipant. Team Steam): bool ...=jateCrnConfiguration(Activity Sactivity): voidi.…astMatch(2array SmatchedRecords, ?array SaatchedDonainRecords): arravf...;> ConRecords(Particioant Sparticinant. Actávity Sactivity): 2arravá...}sekopart erinentpanta einant ShantscsnantebooittaashuserIfExists(Partieipant Sparticipant, Tean Stean): voidf..CardonsfnRecondewwwttw/wiew oureoneetsnmioutas.nedinu Lo moy twursServiceTestA2ASAY= custom.loglSF [minny@localhost)A console (EUTA console [STAGING"imiamyAND a.created at > DATE SUB(NOWO, INTERVAL 38 DAY)GROUP RY u.sd urenaitl, u.nane. M. softahone nunberORDER BY sms count DESC045 A1 A41 Y 66 Aselect * from teans where 1d = 1select * fron rolesCONCAT(U.1d, CASE WHEN U.1d = t.owner_id THEN • (ouner)' ELSE ** END) AS user_1d.t.oaner id FROM social_accounts saJoiN users u on u.1d = sa.soczablc1d1.n<->1: on taid = u.tean_idWUIO D7OSASASS YESneeweles HerE wisd to mnwssyoeuosecuuieotucomasseeuusor2891864197W10hereoie shSes where 5d= 43783select * fron playbook_categories where playbook_id = 5473Seld values where com 6ield d = 659262SSLEN + FROM CA 4e1d datar+IOTM cos Erelde & OM Edl eo €reld3d= €.30•IOTN ACtvtes A OM £dl sctsustu 3a =93dRERTuserid IN (7168, 18688) and created at > *2826-85-22' order by id descaselect * fron users where tean_id = 1 and id IN (18688. 13934, 7160)=select * fron activities where user_id = 7168 order by id desc linit 18:select * fron accountsselect * fron usenswhere nane Like '%Subrax': # 31054. 1117aetuity searches where lusen oe syisasparch &ilters where actiuity seanch ia TN (9998), BROR)):111|Cecsdales Orcnnworceoeionh1/ 0rosers/ tuxos/2Toreach (Sparticiponts as spariicipant)Srecords = Sthis-s1indCreRecords(Sparticipant, Sactivity):coly piccororritonter tiit w::wwinrecorddlierarnch. kerserth/l & This nay call Salesforce::matchByDomain()Step 7: Domain Search tnggers opportunity synoboin senrh eirs necount ens drinee" withi relaired oncorunt/l To get opportunity details, it calls:Scraservicessyndopponenstylscirtovzoerto):# couts aaporcopportunztyl)Step 8: The bua - importo pportunity creates temporary Recoro/Users/lukas/1m1nny/app/app/Services/cra/Salestorce/Servicc.php:1430-1448oriivate tunctionimoort@sportunt tylScr iatal: 20000rtuntvVxino early chelete chack8a. Restore fron trash (tine 1430)SthcesrestorAcyTrachwenttylsthiisescontossomortntlesol. SerDataloToebuny na sests wich valbld0io.l0ones34305833Sopportunity = Sthsooruaeolnoalasosaat RosborunitweseVADio la.or7hosy5yiumoor edusin thesid edine42sthices coort DocortuntverFelData (Ser Datar SorBtelds, Conoortunt tyesd)eM8ar Noh celeteit ansin itine 1444Sthis-shandle0bjectDeletion(Sopportunity, ScrnData):Sopportuntty=trashedIl Methad returs null, ButAat anuthientuat4 space...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
86397
|
2962
|
13
|
2026-05-28T14:07:31.618643+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-28/1779 /Users/lukas/.screenpipe/data/data/2026-05-28/1779977251618_m1.jpg...
|
PhpStorm
|
faVsco.js – CrmActivityService.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
IAlActivity MonitorFileEditViewWindowHelpActivity IAlActivity MonitorFileEditViewWindowHelpActivity MonitorAll ProcessesProcess Name% CPUPhpStormkernel_taskVirtual Machine Service for DockerreplaydWindowServerSlack Helper (Renderer)screenpipeFirefoxCP Isolated Web ContentlaunchservicesdcoreaudiodclouddiTerm2tccdActivity MonitorSlack HelperClaudelanguage_server_macos_armcef_server Helper (Renderer)syspolicydWispr Flow Helper (Renderer)FirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentWispr FlowFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContenttccdKarabiner-Core-ServiceFirefoxCP Isolated Web Content178,4161,185,558,147,724,723,09,86,65,95,35,14,94,84,73,73,63,53,12,92,32,22,12,01,71,71,61,4CPU Time2:33:09,9622:47:58,841:52:37,515:42:13,767:50:21,6357:25,293:34:51,7733:40,591:00:05,2057:02,0126,991:03:29,518:36,346:47,0214:25,2728:21,737:29,048:12,1616:32,453:45,4229:46,382:03,822:36,2817:15,7923:21,516:14,1519:49,6915:13,48System:User:Idle:Threads lahl100% C8• Thu 28 May 17:07:31CPUMemoryEnergyDiskNetworkIdle Wake-UpsKind% GPU AppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleApple36,66%59,35%4,00%10,50,00,00,053,10,00,00,00,00,00,00,60,00,05,80,00,00,00,00,00,00,00,00,00,00,00,00,0CPU LOADThreads:Processes:SearchGPU Time7:47,620,000,000,003:20:06,220,005:03,900,000,000,000,0019,370,000,006:06,600,000,000,000,000,000,000,000,000,000,000,000,000,00PID Userlukasrootlukaslukas_windowserverlukaslukaslukasroot_coreaudiodlukaslukaslukaslukaslukaslukaslukaslukasrootlukaslukaslukaslukaslukaslukasrootrootlukas...
|
NULL
|
-1501416439682967965
|
NULL
|
click
|
ocr
|
NULL
|
IAlActivity MonitorFileEditViewWindowHelpActivity IAlActivity MonitorFileEditViewWindowHelpActivity MonitorAll ProcessesProcess Name% CPUPhpStormkernel_taskVirtual Machine Service for DockerreplaydWindowServerSlack Helper (Renderer)screenpipeFirefoxCP Isolated Web ContentlaunchservicesdcoreaudiodclouddiTerm2tccdActivity MonitorSlack HelperClaudelanguage_server_macos_armcef_server Helper (Renderer)syspolicydWispr Flow Helper (Renderer)FirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentWispr FlowFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContenttccdKarabiner-Core-ServiceFirefoxCP Isolated Web Content178,4161,185,558,147,724,723,09,86,65,95,35,14,94,84,73,73,63,53,12,92,32,22,12,01,71,71,61,4CPU Time2:33:09,9622:47:58,841:52:37,515:42:13,767:50:21,6357:25,293:34:51,7733:40,591:00:05,2057:02,0126,991:03:29,518:36,346:47,0214:25,2728:21,737:29,048:12,1616:32,453:45,4229:46,382:03,822:36,2817:15,7923:21,516:14,1519:49,6915:13,48System:User:Idle:Threads lahl100% C8• Thu 28 May 17:07:31CPUMemoryEnergyDiskNetworkIdle Wake-UpsKind% GPU AppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleApple36,66%59,35%4,00%10,50,00,00,053,10,00,00,00,00,00,00,60,00,05,80,00,00,00,00,00,00,00,00,00,00,00,00,0CPU LOADThreads:Processes:SearchGPU Time7:47,620,000,000,003:20:06,220,005:03,900,000,000,000,0019,370,000,006:06,600,000,000,000,000,000,000,000,000,000,000,000,000,00PID Userlukasrootlukaslukas_windowserverlukaslukaslukasroot_coreaudiodlukaslukaslukaslukaslukaslukaslukaslukasrootlukaslukaslukaslukaslukaslukasrootrootlukas...
|
86396
|
NULL
|
NULL
|
NULL
|
|
86395
|
2963
|
9
|
2026-05-28T14:07:27.974602+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-28/1779 /Users/lukas/.screenpipe/data/data/2026-05-28/1779977247974_m2.jpg...
|
PhpStorm
|
faVsco.js – CrmActivityService.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
rapstomViewNeweNNCCoocKetucioWindowFV faVsco.|s ~# rapstomViewNeweNNCCoocKetucioWindowFV faVsco.|s ~#12121 on JY-20963-fx-lProinet va Service.pri>mutkewolcnnedvilycimbato.ong© CrmActivityService.php x© Activity.pho©) Team.phpIa Wethookc) RatchSunceo ector.chocRatchSuncRedeCanrce.o© Clent.phoc eoseddeaistarcosermieuDealFieldsService.pho© DecorateActivity.phgoreaochinidons.one© FieldTypeConverter.pho(© HubspotClientinterface.ph© HubspotTokenManager.pt170171© PayloadBuilder.pho©) RemoteCrmObiectManioul173ResponseNormalize.pho175c) service.ono© SyncFieidAction.pho176177c Suncrelateohcwiywanx© WebhookSvncBatchProce178>M intearationAno@ listeners→lweradatalMotaten→p nedriveii Salesforce• FaldeHANE18sMi Onnortun tMatchenOpportunitySyncStrategyProspectSearchStrateg)>Ih SemicetrairdeCant nhr© DecorateActivity.phpnelateObiecteTrait.phpe Faldnafoitione mho© PayloadBuilder.pho(©) Profile.pho© QueryBuilder.php© QueryHandler.pho208© Querviterator.ohc© QueryResuits.pho© Service.php@ SvncBatchRedisService.of 24€la TraitsA pasoclient.php© BaseService.ohe© CachedCrmServiceDecoratoc countrvcoderesolver ohdCrmActivitvProviderinteorateCTermcv tsarioe.ood© [EMAIL](@ nelauttDrosnantSearchStrateclass cruacevzcyserviceprivate function updateParticipantsCrnData(contnue.SbestMatch = Sthis->getBestMatch(nantaciinants nracassed: = Soantacinantsoscount/el'donain_natches' => count(SnatchedDonainRecords),"best_natch_found' => ! empty(SbestMatch),1):return SbestMatchaprivate function shouldPerforalookup(Participant Spanticipant. Tean Steam): boolf....private function validateCreConfiquration(Activity Sactivity): voidi..."private function getßestMatch(?array SnatchedRecords, ?array SnatchedDonainRecords): arr&736)private function findCrmRecords(Panticipant Sparticipant, Activity Sactivity): ?array(....uesaaprivate function shouldSkipParticipant(Participant Sparticipant): boolf...}736ursasprivate function attachUserIfExists(Participant Sparticipant, Ieen Stean): voidf...nniuata functtion findtcehoenfn0scandelpantfefnant CoanticinantActivity Sactivity): array {...= custom.logSF [minny@localhost)Service.phpA console (EUTA console [STAGING"imiamyA22ySAV=701AND a.created at > DATE SUB(NOWO, INTERVAL 38 DAY)GROUP RY u.sd urenatl u.nane. M. sostahone nunberORDER BY sms count DESC045 A1 A41 У 66 ^select * from teans where 1d = 1:select * fron rolesCONCATu.id, CASE WHEN u.id = t.owner id THEN " (onner' ELSE ** END) AS user idt.oanerid FROM social accounts s.JOIN users u on u.id = sa.sociable.idJOIN teans t1.n<->1: on taid = u.tean_idTHERE u,tean sid = 1117 and saroroviden = "hubspot*.SELECT * FROM activities WHERE uuid_to_bin('8024fffb-2df7-4017-91f4-d9f896850248') = uuid; # 79933459 YESSELECT + FPOM activitios THERE unid to binf+[CREDIT_CARD]-9276-464d22-8185e*) = uusde # 80186192 NGSELECT * FROM crn_configurations WHERE id = 1053:select * from playbooks where 1a = s47x:playbook_categories where id = 43783;select + Eron dlavbook cateoonses whene nilavhook5d 5673)eon &eld values where com Sield 3d = 6502/%SSIEN + FROM Con 6eld data 4oJOIN cra fields + ON fd.crn field id = f.idAOTN setvtee A OM Edl sctkustu 3a =a3dPAlRAtA ChAe WN ANAUS ThARA AMMAIAA 10004-06011select * fron activities whereuserid IN (7168, 18688) and created at > *2826-85-22' order by id descaPlthaeneheh hAnh dAiACAAndAAYM CAeRde 47071 m/01eselect * fron activities where user id = 7169 order by id desc Linit 10:select * fron accounts wheretean_id = 1 and nane = 'Colunns":select * fron usens where nane Like 'ySubrax": # 31954. 1117select + fron activity sparches where usen 1d = 319541select + fron actiwity sparch &iltens where actiuity seanch ia TN (9998). BROR)) :inu Lo wey tworsServiceTest+0.Cecsdales Orcnnworceoeionh1/ 0rosers/ tuxos/2Toreach (Spartzciponts as sparcicipant)Srecords = Sthis-s1indCreRecords(Sparticipant, Sactivity):coly piccororritonter tiit w::wwinrecorddlierarnch. kerserth/l & This nay call Salesforce::matchByDomain()Step 7: Domain Search tnggers opportunity synoboin senrh eirs necount ens drinee" withi relaired oncorunt/l To get opportunity details, it calls:Scraservicesyncopponontylscrrrovzoerso):# couts aaporcopportunztyl)Step 8: The bua - importo pportunity creates temporary Recoroaond/Users/luxas/1m1nny//app/app/Services/crm/Salestorce/Serv1cc.php:1430-1448oriivate tunctionimoort@sportunt tylScr iatal: 20000rtuntvVxino early chelete chack8a. Restore fron trash (tine 1430)SthcesrestorAcyTrachwenttylsthicescontoesomoortuntlecol. Ser Dataletoenruny na sests wich valbld0io.l0ones34305833Sopportunity = SthswoWrtreucreonowloer1ooruaeolnoalasosaar Coobortuntwese Nabio arorosysyiumoor edusin thesid edine42sthices roort DocortuntverFlel bata SerDatar SerBields, Conoortunt tyesto)eM8ar Noh celeteit ansin itine 1444Sthis-shandle0bjectDeletion(Sopportunity, ScrnData):Sopportuntty=trashedIl Methad returs null, ButAat anuthientua*4 space...
|
NULL
|
-1766861973227769218
|
NULL
|
click
|
ocr
|
NULL
|
rapstomViewNeweNNCCoocKetucioWindowFV faVsco.|s ~# rapstomViewNeweNNCCoocKetucioWindowFV faVsco.|s ~#12121 on JY-20963-fx-lProinet va Service.pri>mutkewolcnnedvilycimbato.ong© CrmActivityService.php x© Activity.pho©) Team.phpIa Wethookc) RatchSunceo ector.chocRatchSuncRedeCanrce.o© Clent.phoc eoseddeaistarcosermieuDealFieldsService.pho© DecorateActivity.phgoreaochinidons.one© FieldTypeConverter.pho(© HubspotClientinterface.ph© HubspotTokenManager.pt170171© PayloadBuilder.pho©) RemoteCrmObiectManioul173ResponseNormalize.pho175c) service.ono© SyncFieidAction.pho176177c Suncrelateohcwiywanx© WebhookSvncBatchProce178>M intearationAno@ listeners→lweradatalMotaten→p nedriveii Salesforce• FaldeHANE18sMi Onnortun tMatchenOpportunitySyncStrategyProspectSearchStrateg)>Ih SemicetrairdeCant nhr© DecorateActivity.phpnelateObiecteTrait.phpe Faldnafoitione mho© PayloadBuilder.pho(©) Profile.pho© QueryBuilder.php© QueryHandler.pho208© Querviterator.ohc© QueryResuits.pho© Service.php@ SvncBatchRedisService.of 24€la TraitsA pasoclient.php© BaseService.ohe© CachedCrmServiceDecoratoc countrvcoderesolver ohdCrmActivitvProviderinteorateCTermcv tsarioe.ood© [EMAIL](@ nelauttDrosnantSearchStrateclass cruacevzcyserviceprivate function updateParticipantsCrnData(contnue.SbestMatch = Sthis->getBestMatch(nantaciinants nracassed: = Soantacinantsoscount/el'donain_natches' => count(SnatchedDonainRecords),"best_natch_found' => ! empty(SbestMatch),1):return SbestMatchaprivate function shouldPerforalookup(Participant Spanticipant. Tean Steam): boolf....private function validateCreConfiquration(Activity Sactivity): voidi..."private function getßestMatch(?array SnatchedRecords, ?array SnatchedDonainRecords): arr&736)private function findCrmRecords(Panticipant Sparticipant, Activity Sactivity): ?array(....uesaaprivate function shouldSkipParticipant(Participant Sparticipant): boolf...}736ursasprivate function attachUserIfExists(Participant Sparticipant, Ieen Stean): voidf...nniuata functtion findtcehoenfn0scandelpantfefnant CoanticinantActivity Sactivity): array {...= custom.logSF [minny@localhost)Service.phpA console (EUTA console [STAGING"imiamyA22ySAV=701AND a.created at > DATE SUB(NOWO, INTERVAL 38 DAY)GROUP RY u.sd urenatl u.nane. M. sostahone nunberORDER BY sms count DESC045 A1 A41 У 66 ^select * from teans where 1d = 1:select * fron rolesCONCATu.id, CASE WHEN u.id = t.owner id THEN " (onner' ELSE ** END) AS user idt.oanerid FROM social accounts s.JOIN users u on u.id = sa.sociable.idJOIN teans t1.n<->1: on taid = u.tean_idTHERE u,tean sid = 1117 and saroroviden = "hubspot*.SELECT * FROM activities WHERE uuid_to_bin('8024fffb-2df7-4017-91f4-d9f896850248') = uuid; # 79933459 YESSELECT + FPOM activitios THERE unid to binf+[CREDIT_CARD]-9276-464d22-8185e*) = uusde # 80186192 NGSELECT * FROM crn_configurations WHERE id = 1053:select * from playbooks where 1a = s47x:playbook_categories where id = 43783;select + Eron dlavbook cateoonses whene nilavhook5d 5673)eon &eld values where com Sield 3d = 6502/%SSIEN + FROM Con 6eld data 4oJOIN cra fields + ON fd.crn field id = f.idAOTN setvtee A OM Edl sctkustu 3a =a3dPAlRAtA ChAe WN ANAUS ThARA AMMAIAA 10004-06011select * fron activities whereuserid IN (7168, 18688) and created at > *2826-85-22' order by id descaPlthaeneheh hAnh dAiACAAndAAYM CAeRde 47071 m/01eselect * fron activities where user id = 7169 order by id desc Linit 10:select * fron accounts wheretean_id = 1 and nane = 'Colunns":select * fron usens where nane Like 'ySubrax": # 31954. 1117select + fron activity sparches where usen 1d = 319541select + fron actiwity sparch &iltens where actiuity seanch ia TN (9998). BROR)) :inu Lo wey tworsServiceTest+0.Cecsdales Orcnnworceoeionh1/ 0rosers/ tuxos/2Toreach (Spartzciponts as sparcicipant)Srecords = Sthis-s1indCreRecords(Sparticipant, Sactivity):coly piccororritonter tiit w::wwinrecorddlierarnch. kerserth/l & This nay call Salesforce::matchByDomain()Step 7: Domain Search tnggers opportunity synoboin senrh eirs necount ens drinee" withi relaired oncorunt/l To get opportunity details, it calls:Scraservicesyncopponontylscrrrovzoerso):# couts aaporcopportunztyl)Step 8: The bua - importo pportunity creates temporary Recoroaond/Users/luxas/1m1nny//app/app/Services/crm/Salestorce/Serv1cc.php:1430-1448oriivate tunctionimoort@sportunt tylScr iatal: 20000rtuntvVxino early chelete chack8a. Restore fron trash (tine 1430)SthcesrestorAcyTrachwenttylsthicescontoesomoortuntlecol. Ser Dataletoenruny na sests wich valbld0io.l0ones34305833Sopportunity = SthswoWrtreucreonowloer1ooruaeolnoalasosaar Coobortuntwese Nabio arorosysyiumoor edusin thesid edine42sthices roort DocortuntverFlel bata SerDatar SerBields, Conoortunt tyesto)eM8ar Noh celeteit ansin itine 1444Sthis-shandle0bjectDeletion(Sopportunity, ScrnData):Sopportuntty=trashedIl Methad returs null, ButAat anuthientua*4 space...
|
86392
|
NULL
|
NULL
|
NULL
|
|
86396
|
2962
|
12
|
2026-05-28T14:07:27.847895+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-28/1779 /Users/lukas/.screenpipe/data/data/2026-05-28/1779977247847_m1.jpg...
|
PhpStorm
|
faVsco.js – CrmActivityService.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
JAlActivity MonitorFileEditViewWindowHelpActivity JAlActivity MonitorFileEditViewWindowHelpActivity MonitorAll ProcessesProcess Name% CPUkernel_taskPhpStormVirtual Machine Service for DockerreplaydWindowServerlanguage_server_macos_armscreenpipeSlack Helper (Renderer)FirefoxCP Isolated Web ContentiTerm2clouddcoreaudiodActivity MonitorSlack HelperFirefoxCP Isolated Web ContentFirefoxFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentWispr Flow Helper (Renderer)io.kandji.KandjiAgent.ESF-ExtensionFirefoxCP Isolated Web ContentWispr FlowSlackClaudetccdFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentFirefoxCP Isolated Web Content174,7147,3136,236,733,613,511,48,98,86,16,05,14,74,63,63,03,03,02,92,82,52,22,12,01,91,91,91,8CPU Time22:47:50,262:33:00,451:52:32,955:42:10,667:50:19,097:28,853:34:50,5457:23,9733:40,061:03:29,2326,7057:01,696:46,7714:25,0229:46,261:46:44,6517:15,6915:13,403:45,2731:20,9223:21,412:36,1713:14,4028:21,538:36,084:15,7340:33,532:03,71System:User:Idle:Threads % <7Thu 28 May 17:07:27CPUMemoryEnergyDiskNetworkIdle Wake-UpsKind % GPUAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleApple43,89%50,70%5,41%0,08,70,00,047,40,00,00,00,00,50,00,00,05,30,00,00,00,00,00,00,00,00,00,00,00,00,00,0CPU LOADThreads:Processes:GPU Time0,007:47,280,000,003:20:05,150,005:03,900,000,0019,360,000,000,006:06,520,000,040,000,000,000,000,000,000,050,000,000,000,000,00PID alolUserrootlukaslukaslukas_windowserverlukaslukaslukaslukaslukaslukas_coreaudiodlukaslukaslukaslukaslukaslukaslukasrootlukaslukaslukaslukaslukaslukaslukaslukasSearch...
|
NULL
|
-7842054117516564831
|
NULL
|
click
|
ocr
|
NULL
|
JAlActivity MonitorFileEditViewWindowHelpActivity JAlActivity MonitorFileEditViewWindowHelpActivity MonitorAll ProcessesProcess Name% CPUkernel_taskPhpStormVirtual Machine Service for DockerreplaydWindowServerlanguage_server_macos_armscreenpipeSlack Helper (Renderer)FirefoxCP Isolated Web ContentiTerm2clouddcoreaudiodActivity MonitorSlack HelperFirefoxCP Isolated Web ContentFirefoxFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentWispr Flow Helper (Renderer)io.kandji.KandjiAgent.ESF-ExtensionFirefoxCP Isolated Web ContentWispr FlowSlackClaudetccdFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentFirefoxCP Isolated Web Content174,7147,3136,236,733,613,511,48,98,86,16,05,14,74,63,63,03,03,02,92,82,52,22,12,01,91,91,91,8CPU Time22:47:50,262:33:00,451:52:32,955:42:10,667:50:19,097:28,853:34:50,5457:23,9733:40,061:03:29,2326,7057:01,696:46,7714:25,0229:46,261:46:44,6517:15,6915:13,403:45,2731:20,9223:21,412:36,1713:14,4028:21,538:36,084:15,7340:33,532:03,71System:User:Idle:Threads % <7Thu 28 May 17:07:27CPUMemoryEnergyDiskNetworkIdle Wake-UpsKind % GPUAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleApple43,89%50,70%5,41%0,08,70,00,047,40,00,00,00,00,50,00,00,05,30,00,00,00,00,00,00,00,00,00,00,00,00,00,0CPU LOADThreads:Processes:GPU Time0,007:47,280,000,003:20:05,150,005:03,900,000,0019,360,000,000,006:06,520,000,040,000,000,000,000,000,000,050,000,000,000,000,00PID alolUserrootlukaslukaslukas_windowserverlukaslukaslukaslukaslukaslukas_coreaudiodlukaslukaslukaslukaslukaslukaslukasrootlukaslukaslukaslukaslukaslukaslukaslukasSearch...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
86394
|
2962
|
11
|
2026-05-28T14:07:20.006764+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-28/1779 /Users/lukas/.screenpipe/data/data/2026-05-28/1779977240006_m1.jpg...
|
PhpStorm
|
faVsco.js – CrmActivityService.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
#12121 on JY-20963-fix-im Project: faVsco.js, menu
#12121 on JY-20963-fix-import-on-deleted-entity, menu
Start Listening for PHP Debug Connections
ServiceTest
Run 'ServiceTest'
Debug 'ServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
22
5
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Crm;
use Illuminate\Support\Collection;
use Jiminny\Contracts\Repositories\TeamRepository;
use Jiminny\Contracts\Services\Crm\ServiceInterface;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Models\Account;
use Jiminny\Models\Activity;
use Jiminny\Models\Contact;
use Jiminny\Models\Lead;
use Jiminny\Models\Opportunity;
use Jiminny\Models\Participant;
use Jiminny\Models\Stage;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Services\ResolveTeamCrmConnection;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Psr\Log\LoggerInterface;
use Exception;
use Throwable;
class CrmActivityService
{
public function __construct(
private readonly TeamRepository $teamRepository,
private readonly CachedCrmServiceDecorator $decorator,
private readonly EmailHelper $emailHelper,
private readonly ResolveTeamCrmConnection $teamCrmResolver,
private readonly LoggerInterface $logger,
) {
}
/**
* Updates CRM data for an activity and its participants.
*
* NOTE: This method performs multiple database writes and should be called
* within a transaction by the caller to ensure atomicity.
*
* @param Activity $activity
* @param bool $remoteSearch
*
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception
*/
public function updateCrmData(
Activity $activity,
bool $remoteSearch = false,
): void {
$crmService = null;
$participants = $activity->getParticipants();
$team = $activity->getTeam();
$prospectSearchStrategy = ProspectSearchStrategyFactory::match($team);
if ($prospectSearchStrategy->ignoreCrmMatchData()) {
$this->logger->info('[CrmActivityService] Ignoring crm data because of prospect strategy', [
'activity_id' => $activity->getId(),
'strategy' => get_class($prospectSearchStrategy),
]);
return;
}
if ($remoteSearch) {
try {
$crmService = $this->teamCrmResolver->resolveForTeam($team);
} catch (SocialAccountTokenInvalidException) {
$this->logger->warning('[CrmActivityService] CRM token expired, falling back to local search', [
'activity_id' => $activity->getId(),
'team_id' => $team->getId(),
]);
}
}
$records = $this->updateParticipantsCrmData(
team: $team,
activity: $activity,
participants: $participants,
crmService: $crmService,
);
if (! empty($records)) {
$activity->updateActivityCrmData($records);
}
$activity->refresh();
}
/**
* @param Collection<Participant> $participants
*
* @throws Exception
*
* @return array{
* Lead|null,
* Account|null,
* Opportunity|null,
* Contact|null,
* Stage|null,
* string|null
*}|array{}
*/
private function updateParticipantsCrmData(
Team $team,
Activity $activity,
Collection $participants,
?ServiceInterface $crmService = null,
): array {
$matchedRecords = [];
$matchedDomainRecords = [];
$this->validateCrmConfiguration($activity);
$this->decorator->setConfiguration($activity->getCrm());
$this->decorator->setCrmService($crmService);
foreach ($participants as $participant) {
if ($this->shouldSkipParticipant($participant)) {
continue;
}
if (! $this->shouldPerformLookup($participant, $team)) {
$this->logger->info('[CrmActivityService] Email domain belongs to the team, skipping crm lookup', [
'activity_id' => $activity->getId(),
'team_id' => $team->getId(),
'email' => $participant->getEmailAddress(),
]);
$this->attachUserIfExists($participant, $team);
continue;
}
$records = $this->findCrmRecords($participant, $activity);
if (! empty($records)) {
$matchedRecords[] = $records;
} else {
$records = $this->findCrmDomainRecords(
crmService: $crmService,
participant: $participant,
activity: $activity,
);
if (! empty($records)) {
$matchedDomainRecords[] = $records;
}
}
if (empty($records)) {
continue;
}
try {
$activity->updateParticipantCrmData($records, $participant);
} catch (Throwable $ex) {
$this->logger->error('[CrmActivityService] Failed to update participant CRM data', [
'activity_id' => $activity->getId(),
'participant_id' => $participant->getId(),
'exception' => $ex->getMessage(),
]);
continue;
}
}
$bestMatch = $this->getBestMatch(
matchedRecords : $matchedRecords,
matchedDomainRecords: $matchedDomainRecords,
);
$this->logger->info('[CrmActivityService] CRM matching completed', [
'activity_id' => $activity->getId(),
'participants_processed' => $participants->count(),
'exact_matches' => count($matchedRecords),
'domain_matches' => count($matchedDomainRecords),
'best_match_found' => ! empty($bestMatch),
]);
return $bestMatch;
}
private function shouldPerformLookup(Participant $participant, Team $team): bool
{
if ($participant->hasEmailAddress()) {
return $this->emailHelper->shouldPerformLookup($team, $participant->getEmailAddress());
}
return true;
}
private function validateCrmConfiguration(Activity $activity): void
{
if ($activity->getCrm() === null) {
throw new InvalidArgumentException('Cannot find CRM configuration');
}
}
private function getBestMatch(?array $matchedRecords, ?array $matchedDomainRecords): array
{
return RecordSelector::pickBestFromLists($matchedRecords, $matchedDomainRecords);
}
private function findCrmRecords(Participant $participant, Activity $activity): ?array
{
$records = null;
if ($participant->hasEmailAddress()) {
$records = $this->decorator->matchExactlyByEmail(
email: $participant->getEmailAddress(),
userId: $activity->getUser()->getId()
);
}
if (empty($records) && $participant->getPhoneNumber() !== null) {
$records = $this->decorator->matchByPhone(
phone: $participant->getPhoneNumber(),
userId: $activity->getUser()->getId(),
);
}
if (empty($records) && $participant->getName() !== null) {
$records = $this->decorator->matchByName(
name: $participant->getName(),
userId: $activity->getUser()->getId(),
);
}
return $records;
}
private function shouldSkipParticipant(Participant $participant): bool
{
return $participant->hasUser();
}
private function attachUserIfExists(Participant $participant, Team $team): void
{
if ($participant->hasEmailAddress() === false) {
return;
}
$user = $this->teamRepository->findActiveTeamMemberByEmail($team, $participant->getEmailAddress());
if ($user instanceof User) {
$participant->user_id = $user->getId();
$participant->save();
}
}
private function findCrmDomainRecords(
?ServiceInterface $crmService,
Participant $participant,
Activity $activity,
): array {
if ($participant->hasEmailAddress()) {
$this->decorator->setConfiguration($activity->getCrm());
$this->decorator->setCrmService($crmService);
$records = $this->decorator->matchByDomain(
email: $participant->getEmailAddress(),
userId: $activity->getUser()->getId()
);
if (! empty($records)) {
return $records;
}
}
return [];
}
}
Execute
Explain Plan
Browse Query History
View Parameters
Open Query Execution Settings…
In-Editor Results
Tx: Auto
Cancel Running Statements...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"#12121 on JY-20963-fix-import-on-deleted-entity, menu","depth":5,"on_screen":true,"help_text":"Pull request #12121 exists for current branch JY-20963-fix-import-on-deleted-entity, but pull request details loading failed","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"ServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'ServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'ServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"22","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"5","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Crm;\n\nuse Illuminate\\Support\\Collection;\nuse Jiminny\\Contracts\\Repositories\\TeamRepository;\nuse Jiminny\\Contracts\\Services\\Crm\\ServiceInterface;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Exceptions\\SocialAccountTokenInvalidException;\nuse Jiminny\\Models\\Account;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Contact;\nuse Jiminny\\Models\\Lead;\nuse Jiminny\\Models\\Opportunity;\nuse Jiminny\\Models\\Participant;\nuse Jiminny\\Models\\Stage;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\ResolveTeamCrmConnection;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Psr\\Log\\LoggerInterface;\nuse Exception;\nuse Throwable;\n\nclass CrmActivityService\n{\n public function __construct(\n private readonly TeamRepository $teamRepository,\n private readonly CachedCrmServiceDecorator $decorator,\n private readonly EmailHelper $emailHelper,\n private readonly ResolveTeamCrmConnection $teamCrmResolver,\n private readonly LoggerInterface $logger,\n ) {\n }\n\n /**\n * Updates CRM data for an activity and its participants.\n *\n * NOTE: This method performs multiple database writes and should be called\n * within a transaction by the caller to ensure atomicity.\n *\n * @param Activity $activity\n * @param bool $remoteSearch\n *\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception\n */\n public function updateCrmData(\n Activity $activity,\n bool $remoteSearch = false,\n ): void {\n $crmService = null;\n $participants = $activity->getParticipants();\n $team = $activity->getTeam();\n\n $prospectSearchStrategy = ProspectSearchStrategyFactory::match($team);\n if ($prospectSearchStrategy->ignoreCrmMatchData()) {\n $this->logger->info('[CrmActivityService] Ignoring crm data because of prospect strategy', [\n 'activity_id' => $activity->getId(),\n 'strategy' => get_class($prospectSearchStrategy),\n ]);\n\n return;\n }\n\n if ($remoteSearch) {\n try {\n $crmService = $this->teamCrmResolver->resolveForTeam($team);\n } catch (SocialAccountTokenInvalidException) {\n $this->logger->warning('[CrmActivityService] CRM token expired, falling back to local search', [\n 'activity_id' => $activity->getId(),\n 'team_id' => $team->getId(),\n ]);\n }\n }\n\n $records = $this->updateParticipantsCrmData(\n team: $team,\n activity: $activity,\n participants: $participants,\n crmService: $crmService,\n );\n\n if (! empty($records)) {\n $activity->updateActivityCrmData($records);\n }\n\n $activity->refresh();\n }\n\n /**\n * @param Collection<Participant> $participants\n *\n * @throws Exception\n *\n * @return array{\n * Lead|null,\n * Account|null,\n * Opportunity|null,\n * Contact|null,\n * Stage|null,\n * string|null\n *}|array{}\n */\n private function updateParticipantsCrmData(\n Team $team,\n Activity $activity,\n Collection $participants,\n ?ServiceInterface $crmService = null,\n ): array {\n $matchedRecords = [];\n $matchedDomainRecords = [];\n\n $this->validateCrmConfiguration($activity);\n $this->decorator->setConfiguration($activity->getCrm());\n $this->decorator->setCrmService($crmService);\n\n foreach ($participants as $participant) {\n if ($this->shouldSkipParticipant($participant)) {\n continue;\n }\n\n if (! $this->shouldPerformLookup($participant, $team)) {\n $this->logger->info('[CrmActivityService] Email domain belongs to the team, skipping crm lookup', [\n 'activity_id' => $activity->getId(),\n 'team_id' => $team->getId(),\n 'email' => $participant->getEmailAddress(),\n ]);\n\n $this->attachUserIfExists($participant, $team);\n\n continue;\n }\n\n $records = $this->findCrmRecords($participant, $activity);\n\n if (! empty($records)) {\n $matchedRecords[] = $records;\n } else {\n $records = $this->findCrmDomainRecords(\n crmService: $crmService,\n participant: $participant,\n activity: $activity,\n );\n if (! empty($records)) {\n $matchedDomainRecords[] = $records;\n }\n }\n\n if (empty($records)) {\n continue;\n }\n\n try {\n $activity->updateParticipantCrmData($records, $participant);\n } catch (Throwable $ex) {\n $this->logger->error('[CrmActivityService] Failed to update participant CRM data', [\n 'activity_id' => $activity->getId(),\n 'participant_id' => $participant->getId(),\n 'exception' => $ex->getMessage(),\n ]);\n\n continue;\n }\n }\n\n $bestMatch = $this->getBestMatch(\n matchedRecords : $matchedRecords,\n matchedDomainRecords: $matchedDomainRecords,\n );\n\n $this->logger->info('[CrmActivityService] CRM matching completed', [\n 'activity_id' => $activity->getId(),\n 'participants_processed' => $participants->count(),\n 'exact_matches' => count($matchedRecords),\n 'domain_matches' => count($matchedDomainRecords),\n 'best_match_found' => ! empty($bestMatch),\n ]);\n\n return $bestMatch;\n }\n\n private function shouldPerformLookup(Participant $participant, Team $team): bool\n {\n if ($participant->hasEmailAddress()) {\n return $this->emailHelper->shouldPerformLookup($team, $participant->getEmailAddress());\n }\n\n return true;\n }\n\n private function validateCrmConfiguration(Activity $activity): void\n {\n if ($activity->getCrm() === null) {\n throw new InvalidArgumentException('Cannot find CRM configuration');\n }\n }\n\n private function getBestMatch(?array $matchedRecords, ?array $matchedDomainRecords): array\n {\n return RecordSelector::pickBestFromLists($matchedRecords, $matchedDomainRecords);\n }\n\n private function findCrmRecords(Participant $participant, Activity $activity): ?array\n {\n $records = null;\n\n if ($participant->hasEmailAddress()) {\n $records = $this->decorator->matchExactlyByEmail(\n email: $participant->getEmailAddress(),\n userId: $activity->getUser()->getId()\n );\n }\n\n if (empty($records) && $participant->getPhoneNumber() !== null) {\n $records = $this->decorator->matchByPhone(\n phone: $participant->getPhoneNumber(),\n userId: $activity->getUser()->getId(),\n );\n }\n\n if (empty($records) && $participant->getName() !== null) {\n $records = $this->decorator->matchByName(\n name: $participant->getName(),\n userId: $activity->getUser()->getId(),\n );\n }\n\n return $records;\n }\n\n private function shouldSkipParticipant(Participant $participant): bool\n {\n return $participant->hasUser();\n }\n\n private function attachUserIfExists(Participant $participant, Team $team): void\n {\n if ($participant->hasEmailAddress() === false) {\n return;\n }\n\n $user = $this->teamRepository->findActiveTeamMemberByEmail($team, $participant->getEmailAddress());\n\n if ($user instanceof User) {\n $participant->user_id = $user->getId();\n $participant->save();\n }\n }\n\n private function findCrmDomainRecords(\n ?ServiceInterface $crmService,\n Participant $participant,\n Activity $activity,\n ): array {\n if ($participant->hasEmailAddress()) {\n $this->decorator->setConfiguration($activity->getCrm());\n $this->decorator->setCrmService($crmService);\n\n $records = $this->decorator->matchByDomain(\n email: $participant->getEmailAddress(),\n userId: $activity->getUser()->getId()\n );\n if (! empty($records)) {\n return $records;\n }\n }\n\n return [];\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Crm;\n\nuse Illuminate\\Support\\Collection;\nuse Jiminny\\Contracts\\Repositories\\TeamRepository;\nuse Jiminny\\Contracts\\Services\\Crm\\ServiceInterface;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Exceptions\\SocialAccountTokenInvalidException;\nuse Jiminny\\Models\\Account;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Contact;\nuse Jiminny\\Models\\Lead;\nuse Jiminny\\Models\\Opportunity;\nuse Jiminny\\Models\\Participant;\nuse Jiminny\\Models\\Stage;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\ResolveTeamCrmConnection;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Psr\\Log\\LoggerInterface;\nuse Exception;\nuse Throwable;\n\nclass CrmActivityService\n{\n public function __construct(\n private readonly TeamRepository $teamRepository,\n private readonly CachedCrmServiceDecorator $decorator,\n private readonly EmailHelper $emailHelper,\n private readonly ResolveTeamCrmConnection $teamCrmResolver,\n private readonly LoggerInterface $logger,\n ) {\n }\n\n /**\n * Updates CRM data for an activity and its participants.\n *\n * NOTE: This method performs multiple database writes and should be called\n * within a transaction by the caller to ensure atomicity.\n *\n * @param Activity $activity\n * @param bool $remoteSearch\n *\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception\n */\n public function updateCrmData(\n Activity $activity,\n bool $remoteSearch = false,\n ): void {\n $crmService = null;\n $participants = $activity->getParticipants();\n $team = $activity->getTeam();\n\n $prospectSearchStrategy = ProspectSearchStrategyFactory::match($team);\n if ($prospectSearchStrategy->ignoreCrmMatchData()) {\n $this->logger->info('[CrmActivityService] Ignoring crm data because of prospect strategy', [\n 'activity_id' => $activity->getId(),\n 'strategy' => get_class($prospectSearchStrategy),\n ]);\n\n return;\n }\n\n if ($remoteSearch) {\n try {\n $crmService = $this->teamCrmResolver->resolveForTeam($team);\n } catch (SocialAccountTokenInvalidException) {\n $this->logger->warning('[CrmActivityService] CRM token expired, falling back to local search', [\n 'activity_id' => $activity->getId(),\n 'team_id' => $team->getId(),\n ]);\n }\n }\n\n $records = $this->updateParticipantsCrmData(\n team: $team,\n activity: $activity,\n participants: $participants,\n crmService: $crmService,\n );\n\n if (! empty($records)) {\n $activity->updateActivityCrmData($records);\n }\n\n $activity->refresh();\n }\n\n /**\n * @param Collection<Participant> $participants\n *\n * @throws Exception\n *\n * @return array{\n * Lead|null,\n * Account|null,\n * Opportunity|null,\n * Contact|null,\n * Stage|null,\n * string|null\n *}|array{}\n */\n private function updateParticipantsCrmData(\n Team $team,\n Activity $activity,\n Collection $participants,\n ?ServiceInterface $crmService = null,\n ): array {\n $matchedRecords = [];\n $matchedDomainRecords = [];\n\n $this->validateCrmConfiguration($activity);\n $this->decorator->setConfiguration($activity->getCrm());\n $this->decorator->setCrmService($crmService);\n\n foreach ($participants as $participant) {\n if ($this->shouldSkipParticipant($participant)) {\n continue;\n }\n\n if (! $this->shouldPerformLookup($participant, $team)) {\n $this->logger->info('[CrmActivityService] Email domain belongs to the team, skipping crm lookup', [\n 'activity_id' => $activity->getId(),\n 'team_id' => $team->getId(),\n 'email' => $participant->getEmailAddress(),\n ]);\n\n $this->attachUserIfExists($participant, $team);\n\n continue;\n }\n\n $records = $this->findCrmRecords($participant, $activity);\n\n if (! empty($records)) {\n $matchedRecords[] = $records;\n } else {\n $records = $this->findCrmDomainRecords(\n crmService: $crmService,\n participant: $participant,\n activity: $activity,\n );\n if (! empty($records)) {\n $matchedDomainRecords[] = $records;\n }\n }\n\n if (empty($records)) {\n continue;\n }\n\n try {\n $activity->updateParticipantCrmData($records, $participant);\n } catch (Throwable $ex) {\n $this->logger->error('[CrmActivityService] Failed to update participant CRM data', [\n 'activity_id' => $activity->getId(),\n 'participant_id' => $participant->getId(),\n 'exception' => $ex->getMessage(),\n ]);\n\n continue;\n }\n }\n\n $bestMatch = $this->getBestMatch(\n matchedRecords : $matchedRecords,\n matchedDomainRecords: $matchedDomainRecords,\n );\n\n $this->logger->info('[CrmActivityService] CRM matching completed', [\n 'activity_id' => $activity->getId(),\n 'participants_processed' => $participants->count(),\n 'exact_matches' => count($matchedRecords),\n 'domain_matches' => count($matchedDomainRecords),\n 'best_match_found' => ! empty($bestMatch),\n ]);\n\n return $bestMatch;\n }\n\n private function shouldPerformLookup(Participant $participant, Team $team): bool\n {\n if ($participant->hasEmailAddress()) {\n return $this->emailHelper->shouldPerformLookup($team, $participant->getEmailAddress());\n }\n\n return true;\n }\n\n private function validateCrmConfiguration(Activity $activity): void\n {\n if ($activity->getCrm() === null) {\n throw new InvalidArgumentException('Cannot find CRM configuration');\n }\n }\n\n private function getBestMatch(?array $matchedRecords, ?array $matchedDomainRecords): array\n {\n return RecordSelector::pickBestFromLists($matchedRecords, $matchedDomainRecords);\n }\n\n private function findCrmRecords(Participant $participant, Activity $activity): ?array\n {\n $records = null;\n\n if ($participant->hasEmailAddress()) {\n $records = $this->decorator->matchExactlyByEmail(\n email: $participant->getEmailAddress(),\n userId: $activity->getUser()->getId()\n );\n }\n\n if (empty($records) && $participant->getPhoneNumber() !== null) {\n $records = $this->decorator->matchByPhone(\n phone: $participant->getPhoneNumber(),\n userId: $activity->getUser()->getId(),\n );\n }\n\n if (empty($records) && $participant->getName() !== null) {\n $records = $this->decorator->matchByName(\n name: $participant->getName(),\n userId: $activity->getUser()->getId(),\n );\n }\n\n return $records;\n }\n\n private function shouldSkipParticipant(Participant $participant): bool\n {\n return $participant->hasUser();\n }\n\n private function attachUserIfExists(Participant $participant, Team $team): void\n {\n if ($participant->hasEmailAddress() === false) {\n return;\n }\n\n $user = $this->teamRepository->findActiveTeamMemberByEmail($team, $participant->getEmailAddress());\n\n if ($user instanceof User) {\n $participant->user_id = $user->getId();\n $participant->save();\n }\n }\n\n private function findCrmDomainRecords(\n ?ServiceInterface $crmService,\n Participant $participant,\n Activity $activity,\n ): array {\n if ($participant->hasEmailAddress()) {\n $this->decorator->setConfiguration($activity->getCrm());\n $this->decorator->setCrmService($crmService);\n\n $records = $this->decorator->matchByDomain(\n email: $participant->getEmailAddress(),\n userId: $activity->getUser()->getId()\n );\n if (! empty($records)) {\n return $records;\n }\n }\n\n return [];\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Execute","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Explain Plan","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Browse Query History","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"View Parameters","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Open Query Execution Settings…","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"In-Editor Results","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Tx: Auto","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Cancel Running Statements","depth":4,"on_screen":true,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-5538416147107499572
|
-8382375122341513090
|
visual_change
|
accessibility
|
NULL
|
Project: faVsco.js, menu
#12121 on JY-20963-fix-im Project: faVsco.js, menu
#12121 on JY-20963-fix-import-on-deleted-entity, menu
Start Listening for PHP Debug Connections
ServiceTest
Run 'ServiceTest'
Debug 'ServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
22
5
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Crm;
use Illuminate\Support\Collection;
use Jiminny\Contracts\Repositories\TeamRepository;
use Jiminny\Contracts\Services\Crm\ServiceInterface;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Models\Account;
use Jiminny\Models\Activity;
use Jiminny\Models\Contact;
use Jiminny\Models\Lead;
use Jiminny\Models\Opportunity;
use Jiminny\Models\Participant;
use Jiminny\Models\Stage;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Services\ResolveTeamCrmConnection;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Psr\Log\LoggerInterface;
use Exception;
use Throwable;
class CrmActivityService
{
public function __construct(
private readonly TeamRepository $teamRepository,
private readonly CachedCrmServiceDecorator $decorator,
private readonly EmailHelper $emailHelper,
private readonly ResolveTeamCrmConnection $teamCrmResolver,
private readonly LoggerInterface $logger,
) {
}
/**
* Updates CRM data for an activity and its participants.
*
* NOTE: This method performs multiple database writes and should be called
* within a transaction by the caller to ensure atomicity.
*
* @param Activity $activity
* @param bool $remoteSearch
*
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception
*/
public function updateCrmData(
Activity $activity,
bool $remoteSearch = false,
): void {
$crmService = null;
$participants = $activity->getParticipants();
$team = $activity->getTeam();
$prospectSearchStrategy = ProspectSearchStrategyFactory::match($team);
if ($prospectSearchStrategy->ignoreCrmMatchData()) {
$this->logger->info('[CrmActivityService] Ignoring crm data because of prospect strategy', [
'activity_id' => $activity->getId(),
'strategy' => get_class($prospectSearchStrategy),
]);
return;
}
if ($remoteSearch) {
try {
$crmService = $this->teamCrmResolver->resolveForTeam($team);
} catch (SocialAccountTokenInvalidException) {
$this->logger->warning('[CrmActivityService] CRM token expired, falling back to local search', [
'activity_id' => $activity->getId(),
'team_id' => $team->getId(),
]);
}
}
$records = $this->updateParticipantsCrmData(
team: $team,
activity: $activity,
participants: $participants,
crmService: $crmService,
);
if (! empty($records)) {
$activity->updateActivityCrmData($records);
}
$activity->refresh();
}
/**
* @param Collection<Participant> $participants
*
* @throws Exception
*
* @return array{
* Lead|null,
* Account|null,
* Opportunity|null,
* Contact|null,
* Stage|null,
* string|null
*}|array{}
*/
private function updateParticipantsCrmData(
Team $team,
Activity $activity,
Collection $participants,
?ServiceInterface $crmService = null,
): array {
$matchedRecords = [];
$matchedDomainRecords = [];
$this->validateCrmConfiguration($activity);
$this->decorator->setConfiguration($activity->getCrm());
$this->decorator->setCrmService($crmService);
foreach ($participants as $participant) {
if ($this->shouldSkipParticipant($participant)) {
continue;
}
if (! $this->shouldPerformLookup($participant, $team)) {
$this->logger->info('[CrmActivityService] Email domain belongs to the team, skipping crm lookup', [
'activity_id' => $activity->getId(),
'team_id' => $team->getId(),
'email' => $participant->getEmailAddress(),
]);
$this->attachUserIfExists($participant, $team);
continue;
}
$records = $this->findCrmRecords($participant, $activity);
if (! empty($records)) {
$matchedRecords[] = $records;
} else {
$records = $this->findCrmDomainRecords(
crmService: $crmService,
participant: $participant,
activity: $activity,
);
if (! empty($records)) {
$matchedDomainRecords[] = $records;
}
}
if (empty($records)) {
continue;
}
try {
$activity->updateParticipantCrmData($records, $participant);
} catch (Throwable $ex) {
$this->logger->error('[CrmActivityService] Failed to update participant CRM data', [
'activity_id' => $activity->getId(),
'participant_id' => $participant->getId(),
'exception' => $ex->getMessage(),
]);
continue;
}
}
$bestMatch = $this->getBestMatch(
matchedRecords : $matchedRecords,
matchedDomainRecords: $matchedDomainRecords,
);
$this->logger->info('[CrmActivityService] CRM matching completed', [
'activity_id' => $activity->getId(),
'participants_processed' => $participants->count(),
'exact_matches' => count($matchedRecords),
'domain_matches' => count($matchedDomainRecords),
'best_match_found' => ! empty($bestMatch),
]);
return $bestMatch;
}
private function shouldPerformLookup(Participant $participant, Team $team): bool
{
if ($participant->hasEmailAddress()) {
return $this->emailHelper->shouldPerformLookup($team, $participant->getEmailAddress());
}
return true;
}
private function validateCrmConfiguration(Activity $activity): void
{
if ($activity->getCrm() === null) {
throw new InvalidArgumentException('Cannot find CRM configuration');
}
}
private function getBestMatch(?array $matchedRecords, ?array $matchedDomainRecords): array
{
return RecordSelector::pickBestFromLists($matchedRecords, $matchedDomainRecords);
}
private function findCrmRecords(Participant $participant, Activity $activity): ?array
{
$records = null;
if ($participant->hasEmailAddress()) {
$records = $this->decorator->matchExactlyByEmail(
email: $participant->getEmailAddress(),
userId: $activity->getUser()->getId()
);
}
if (empty($records) && $participant->getPhoneNumber() !== null) {
$records = $this->decorator->matchByPhone(
phone: $participant->getPhoneNumber(),
userId: $activity->getUser()->getId(),
);
}
if (empty($records) && $participant->getName() !== null) {
$records = $this->decorator->matchByName(
name: $participant->getName(),
userId: $activity->getUser()->getId(),
);
}
return $records;
}
private function shouldSkipParticipant(Participant $participant): bool
{
return $participant->hasUser();
}
private function attachUserIfExists(Participant $participant, Team $team): void
{
if ($participant->hasEmailAddress() === false) {
return;
}
$user = $this->teamRepository->findActiveTeamMemberByEmail($team, $participant->getEmailAddress());
if ($user instanceof User) {
$participant->user_id = $user->getId();
$participant->save();
}
}
private function findCrmDomainRecords(
?ServiceInterface $crmService,
Participant $participant,
Activity $activity,
): array {
if ($participant->hasEmailAddress()) {
$this->decorator->setConfiguration($activity->getCrm());
$this->decorator->setCrmService($crmService);
$records = $this->decorator->matchByDomain(
email: $participant->getEmailAddress(),
userId: $activity->getUser()->getId()
);
if (! empty($records)) {
return $records;
}
}
return [];
}
}
Execute
Explain Plan
Browse Query History
View Parameters
Open Query Execution Settings…
In-Editor Results
Tx: Auto
Cancel Running Statements...
|
86393
|
NULL
|
NULL
|
NULL
|
|
86393
|
2962
|
10
|
2026-05-28T14:07:10.664395+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-28/1779 /Users/lukas/.screenpipe/data/data/2026-05-28/1779977230664_m1.jpg...
|
PhpStorm
|
faVsco.js – Activity.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
#12121 on JY-20963-fix-im Project: faVsco.js, menu
#12121 on JY-20963-fix-import-on-deleted-entity, menu
Start Listening for PHP Debug Connections
ServiceTest
Run 'ServiceTest'
Debug 'ServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"#12121 on JY-20963-fix-import-on-deleted-entity, menu","depth":5,"on_screen":true,"help_text":"Pull request #12121 exists for current branch JY-20963-fix-import-on-deleted-entity, but pull request details loading failed","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"ServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'ServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'ServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
6806313800532383212
|
-8129894190043395130
|
visual_change
|
hybrid
|
NULL
|
Project: faVsco.js, menu
#12121 on JY-20963-fix-im Project: faVsco.js, menu
#12121 on JY-20963-fix-import-on-deleted-entity, menu
Start Listening for PHP Debug Connections
ServiceTest
Run 'ServiceTest'
Debug 'ServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
JAlActivity MonitorFileEditViewWindowHelpActivity MonitorAll ProcessesProcess Name% CPUPhpStormkernel_taskVirtual Machine Service for DockerWindowServerreplaydscreenpipeFirefoxCP Isolated Web Contentmds_storesNotion Calendar Helper (Renderer)logdClaudecoreaudiodiTerm2Slack HelperSlack Helper (Renderer)Activity Monitorlanguage_server_macos_armWispr Flow Helper (Renderer)FirefoxCP Isolated Web ContentSlackFirefoxCP Isolated Web ContentWispr FlowauditdDockerKarabiner-Core-ServiceControl CentreNotion Helper (Renderer)237,2209,195,652,243,319,29,77,95,95,75,65,45,14,74,74,53,33,02,42,42,32,01,81,71,71,61,6io.kandji.KandjiAgent.ESF-Extension1,4CPU Time2:32:39,2522:47:21,181:52:11,747:50:15,205:42:02,913:34:46,2133:38,611:03:36,059:40,4215:28,1628:21,3257:00,891:03:28,3314:24,2757:23,016:46,057:25,603:44,8115:12,9413:14,1423:20,882:35,848:42,431:26,0319:49,4119:46,5523:56,6631:20,45System:User:Idle:Threads lahl100% <7Thu 28 May 17:07:10CPUMemoryEnergyDiskNetworkIdle Wake-UpsKind % GPUAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleApple55,16%44,02%0,82%6,90,00,049,80,00,00,00,00,00,00,00,00,55,70,00,00,00,00,00,00,00,00,00,00,00,00,00,0CPU LOADThreads:Processes:SearchGPU Time3:2PID Userlukasrootlukas_windowserverlukaslukaslukasrootlukasrootlukas_coreaudiodlukaslukaslukaslukaslukaslukaslukaslukaslukaslukasrootlukasrootlukaslukasroot...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
86392
|
2963
|
8
|
2026-05-28T14:07:05.897240+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-28/1779 /Users/lukas/.screenpipe/data/data/2026-05-28/1779977225897_m2.jpg...
|
PhpStorm
|
faVsco.js – CrmActivityService.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
oedstatee soinie poltenlorouseehuno000wne owwusn w oedstatee soinie poltenlorouseehuno000wne owwusn wip perwoouosOPOroRewaieTuas = KatunzuoddoseENSoue oooCh lh SISioG NoU WerunMPTRROwOSOSOTUN.000000000.000109485N09094iwune use cow wowweNatontoooorerowo wrenoorowoo Doren DeNroOw:wHold"osTwosc.oronceyoorwog/ce/oce/husn/oalsott/DOD9N Aehoc monConeole" NunToc omootE" one ouThe doneastunoddowwodsshonHOTISORAOTEUSCYNTIOTOCASUAREESSYMRGIUSCRrunroodo poeoduain oore suerunonde sour rodees unceoeUAS AIUnTOCCOOPoTUENCOS UIGWOOH OoNC• C+30:20:Z1 Key 82 nulJpioat•(48688 88668) 12 P4 400605 1454940e duo4m 508+695 400605 M4249908wostl oue soroueos walkpheCOOO75OES88.[CREDIT_CARD] 93044Sosh 5o0, 4 790105эuoцa S4unosse 500, 4 790105- (691L 75621 88980) NI Py pue t= Pirueos auayn suash souy * 290185•SSOp Py AQ JOpJo 12C-S8-9282. < Ze Pazeaso pue (88981 8910) MI PEJOSEзлаци S079EAyaSe B0Jy * 190195:soбessou"Aa5A7a06 HOBJ * 103135VusHllmme ewooosu usonooronees neouleorooorporodoe noodlerouooRoe do Pounoenseobesn LAPYON •CAagAgo0Я мagaдо)0oдeоnо gyboвоgоsupften dossouny esengoePossnSSLECCwat deuu srooene oLo 00osllooa8ss soihnaroseiseaemelsmhossewiwsm uroroonn cretsooicheewenELTLО5А 65722666 # «РуПП В С187685896896Р-7716-2187-С9Р6-4939768810954 01 руnи зазнИ S0TENgOE W08S + 105155oosonta dooinouoes cue herreercihte"V OAWULUSTOAMOWnPYBOOS A = PE# UO HLOOULвs s3uл9390 195908 NOE PF 39008ESOLON BOJS * 339105-T = рт алаци SWEaL BOJy * 108105CAVO IT OMNEE MAYOM ME PENERTO EMELnal ejosuooborworsno(чoneasaas) Ayoua i ce, punoy yodeurasoa.(spoosegoyemoopayonems tons me minetegttenon.e zunoэ<-squedtotaueos « ,рassозозa арето .OрIзоbс-латлтдо0S са ,рт лататдое,rowere reoupororte cewooooeteouporeseeonueuosqueoyoueos "spuosaus eregasgquedyo puedareponc-Atynpoes569 A V SAZWspuosaus = вspлорадитевоораwhwlurereewwtoondUC ANADOY O)OTSENASCMARSMUIRAYMOOUIANINensчonesioesoameoueoe ooesoeewe1ez jossou,nosuogeno,ooowoeoee woshieweeeaeosiupeposdniygpywieeNoowwoooswUNoToet1o aopsessipor peeoeseS8tY8TS8tdud'oswosooya sinseyaano eоца зозезозло 0оцазоiрuен ло 0dyа seping ano e$ 5oya sepingpeoled o9LTSLT7LTSLEZLtTEtOLT691besensupseasioedsola coKbeseлssuks/junzoddo cNeerwenboouetosopsoes499гSiaaisneeooid4ose8euAsyoouaonoouo uopyprgous oyo degeuonesuoosos eoyd sepingpeoled o:w.w0ot%ooonheATAIGUONUNaGOIaHISTwwwescabeciPaGoUSODATwOmarOASUASUOE%lOSUdPARAWI MANSYUSRO-xJ-C960Z-AF UO LZIZL O• SгOOSAe AJМОIСаTИ...
|
NULL
|
3116834422774170686
|
NULL
|
click
|
ocr
|
NULL
|
oedstatee soinie poltenlorouseehuno000wne owwusn w oedstatee soinie poltenlorouseehuno000wne owwusn wip perwoouosOPOroRewaieTuas = KatunzuoddoseENSoue oooCh lh SISioG NoU WerunMPTRROwOSOSOTUN.000000000.000109485N09094iwune use cow wowweNatontoooorerowo wrenoorowoo Doren DeNroOw:wHold"osTwosc.oronceyoorwog/ce/oce/husn/oalsott/DOD9N Aehoc monConeole" NunToc omootE" one ouThe doneastunoddowwodsshonHOTISORAOTEUSCYNTIOTOCASUAREESSYMRGIUSCRrunroodo poeoduain oore suerunonde sour rodees unceoeUAS AIUnTOCCOOPoTUENCOS UIGWOOH OoNC• C+30:20:Z1 Key 82 nulJpioat•(48688 88668) 12 P4 400605 1454940e duo4m 508+695 400605 M4249908wostl oue soroueos walkpheCOOO75OES88.[CREDIT_CARD] 93044Sosh 5o0, 4 790105эuoцa S4unosse 500, 4 790105- (691L 75621 88980) NI Py pue t= Pirueos auayn suash souy * 290185•SSOp Py AQ JOpJo 12C-S8-9282. < Ze Pazeaso pue (88981 8910) MI PEJOSEзлаци S079EAyaSe B0Jy * 190195:soбessou"Aa5A7a06 HOBJ * 103135VusHllmme ewooosu usonooronees neouleorooorporodoe noodlerouooRoe do Pounoenseobesn LAPYON •CAagAgo0Я мagaдо)0oдeоnо gyboвоgоsupften dossouny esengoePossnSSLECCwat deuu srooene oLo 00osllooa8ss soihnaroseiseaemelsmhossewiwsm uroroonn cretsooicheewenELTLО5А 65722666 # «РуПП В С187685896896Р-7716-2187-С9Р6-4939768810954 01 руnи зазнИ S0TENgOE W08S + 105155oosonta dooinouoes cue herreercihte"V OAWULUSTOAMOWnPYBOOS A = PE# UO HLOOULвs s3uл9390 195908 NOE PF 39008ESOLON BOJS * 339105-T = рт алаци SWEaL BOJy * 108105CAVO IT OMNEE MAYOM ME PENERTO EMELnal ejosuooborworsno(чoneasaas) Ayoua i ce, punoy yodeurasoa.(spoosegoyemoopayonems tons me minetegttenon.e zunoэ<-squedtotaueos « ,рassозозa арето .OрIзоbс-латлтдо0S са ,рт лататдое,rowere reoupororte cewooooeteouporeseeonueuosqueoyoueos "spuosaus eregasgquedyo puedareponc-Atynpoes569 A V SAZWspuosaus = вspлорадитевоораwhwlurereewwtoondUC ANADOY O)OTSENASCMARSMUIRAYMOOUIANINensчonesioesoameoueoe ooesoeewe1ez jossou,nosuogeno,ooowoeoee woshieweeeaeosiupeposdniygpywieeNoowwoooswUNoToet1o aopsessipor peeoeseS8tY8TS8tdud'oswosooya sinseyaano eоца зозезозло 0оцазоiрuен ло 0dyа seping ano e$ 5oya sepingpeoled o9LTSLT7LTSLEZLtTEtOLT691besensupseasioedsola coKbeseлssuks/junzoddo cNeerwenboouetosopsoes499гSiaaisneeooid4ose8euAsyoouaonoouo uopyprgous oyo degeuonesuoosos eoyd sepingpeoled o:w.w0ot%ooonheATAIGUONUNaGOIaHISTwwwescabeciPaGoUSODATwOmarOASUASUOE%lOSUdPARAWI MANSYUSRO-xJ-C960Z-AF UO LZIZL O• SгOOSAe AJМОIСаTИ...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
86391
|
2962
|
9
|
2026-05-28T14:07:05.780314+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-28/1779 /Users/lukas/.screenpipe/data/data/2026-05-28/1779977225780_m1.jpg...
|
PhpStorm
|
faVsco.js – CrmActivityService.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
IAIActivity MonitorFileEditViewWindowHelpActivity IAIActivity MonitorFileEditViewWindowHelpActivity MonitorAll ProcessesProcess Name% CPUPhpStormkernel_taskVirtual Machine Service for DockerreplaydWindowServerscreenpipeFirefoxCP Isolated Web ContentlaunchservicesdlogdcoreaudiodSlack Helpermds_storesActivity MonitoriTerm2Slack Helper (Renderer)DockerClaudeWispr Flow Helper (Renderer)language_server_macos_armFirefoxCP Isolated Web ContentWispr FlowFirefoxCP Isolated Web ContentNotion Helper (Renderer)Control CentreKarabiner-Core-ServiceWindowManagersmdFirefoxCP Isolated Web Content260,3194,387,666,942,721,79,68,35,65,45,04,74,54,54,04,03,63,03,02,62,11,91,81,61,51,31,31,2CPU Time2:32:26,5622:47:09,991:52:06,625:42:00,597:50:12,413:34:45,1833:38,091:00:04,0915:27,8657:00,6114:24,021:03:35,626:45,811:03:28,0557:22,761:25,9428:21,023:44,657:25,4215:12,812:35,7323:20,7523:56,5819:46,4619:49,323:19,8010:52,2817:15,27System:User:Idle:Threads CPUMemoryEnergyDiskNetworkIdle Wake-UpsKind % GPUAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleApple48,06%51,47%0,47%4,80,00,00,040,50,00,00,00,00,04,80,00,00,50,00,00,00,00,00,00,00,00,00,00,00,00,00,0CPU LOADThreads:Processes:GPU Time7:46,480,000,000,003:20:01,585:03,900,000,000,000,006:06,130,000,0019,330,000,000,000,000,000,000,000,000,000,040,000,000,000,00‹ >0alslPIDUser2468lukasroot13612lukas9826lukas410_windowserver48327lukas21027lukas383root340root477_coreaudiod % C8• Thu 28 May 17:07:05Search...
|
NULL
|
-3589381717880627057
|
NULL
|
click
|
ocr
|
NULL
|
IAIActivity MonitorFileEditViewWindowHelpActivity IAIActivity MonitorFileEditViewWindowHelpActivity MonitorAll ProcessesProcess Name% CPUPhpStormkernel_taskVirtual Machine Service for DockerreplaydWindowServerscreenpipeFirefoxCP Isolated Web ContentlaunchservicesdlogdcoreaudiodSlack Helpermds_storesActivity MonitoriTerm2Slack Helper (Renderer)DockerClaudeWispr Flow Helper (Renderer)language_server_macos_armFirefoxCP Isolated Web ContentWispr FlowFirefoxCP Isolated Web ContentNotion Helper (Renderer)Control CentreKarabiner-Core-ServiceWindowManagersmdFirefoxCP Isolated Web Content260,3194,387,666,942,721,79,68,35,65,45,04,74,54,54,04,03,63,03,02,62,11,91,81,61,51,31,31,2CPU Time2:32:26,5622:47:09,991:52:06,625:42:00,597:50:12,413:34:45,1833:38,091:00:04,0915:27,8657:00,6114:24,021:03:35,626:45,811:03:28,0557:22,761:25,9428:21,023:44,657:25,4215:12,812:35,7323:20,7523:56,5819:46,4619:49,323:19,8010:52,2817:15,27System:User:Idle:Threads CPUMemoryEnergyDiskNetworkIdle Wake-UpsKind % GPUAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleApple48,06%51,47%0,47%4,80,00,00,040,50,00,00,00,00,04,80,00,00,50,00,00,00,00,00,00,00,00,00,00,00,00,00,0CPU LOADThreads:Processes:GPU Time7:46,480,000,000,003:20:01,585:03,900,000,000,000,006:06,130,000,0019,330,000,000,000,000,000,000,000,000,000,040,000,000,000,00‹ >0alslPIDUser2468lukasroot13612lukas9826lukas410_windowserver48327lukas21027lukas383root340root477_coreaudiod % C8• Thu 28 May 17:07:05Search...
|
86390
|
NULL
|
NULL
|
NULL
|
|
86390
|
2962
|
8
|
2026-05-28T14:07:04.418893+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-28/1779 /Users/lukas/.screenpipe/data/data/2026-05-28/1779977224418_m1.jpg...
|
PhpStorm
|
faVsco.js – CrmActivityService.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
#12121 on JY-20963-fix-im Project: faVsco.js, menu
#12121 on JY-20963-fix-import-on-deleted-entity, menu
Start Listening for PHP Debug Connections
ServiceTest
Run 'ServiceTest'
Debug 'ServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
22
5
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Crm;
use Illuminate\Support\Collection;
use Jiminny\Contracts\Repositories\TeamRepository;
use Jiminny\Contracts\Services\Crm\ServiceInterface;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Models\Account;
use Jiminny\Models\Activity;
use Jiminny\Models\Contact;
use Jiminny\Models\Lead;
use Jiminny\Models\Opportunity;
use Jiminny\Models\Participant;
use Jiminny\Models\Stage;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Services\ResolveTeamCrmConnection;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Psr\Log\LoggerInterface;
use Exception;
use Throwable;
class CrmActivityService
{
public function __construct(
private readonly TeamRepository $teamRepository,
private readonly CachedCrmServiceDecorator $decorator,
private readonly EmailHelper $emailHelper,
private readonly ResolveTeamCrmConnection $teamCrmResolver,
private readonly LoggerInterface $logger,
) {
}
/**
* Updates CRM data for an activity and its participants.
*
* NOTE: This method performs multiple database writes and should be called
* within a transaction by the caller to ensure atomicity.
*
* @param Activity $activity
* @param bool $remoteSearch
*
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception
*/
public function updateCrmData(
Activity $activity,
bool $remoteSearch = false,
): void {
$crmService = null;
$participants = $activity->getParticipants();
$team = $activity->getTeam();
$prospectSearchStrategy = ProspectSearchStrategyFactory::match($team);
if ($prospectSearchStrategy->ignoreCrmMatchData()) {
$this->logger->info('[CrmActivityService] Ignoring crm data because of prospect strategy', [
'activity_id' => $activity->getId(),
'strategy' => get_class($prospectSearchStrategy),
]);
return;
}
if ($remoteSearch) {
try {
$crmService = $this->teamCrmResolver->resolveForTeam($team);
} catch (SocialAccountTokenInvalidException) {
$this->logger->warning('[CrmActivityService] CRM token expired, falling back to local search', [
'activity_id' => $activity->getId(),
'team_id' => $team->getId(),
]);
}
}
$records = $this->updateParticipantsCrmData(
team: $team,
activity: $activity,
participants: $participants,
crmService: $crmService,
);
if (! empty($records)) {
$activity->updateActivityCrmData($records);
}
$activity->refresh();
}
/**
* @param Collection<Participant> $participants
*
* @throws Exception
*
* @return array{
* Lead|null,
* Account|null,
* Opportunity|null,
* Contact|null,
* Stage|null,
* string|null
*}|array{}
*/
private function updateParticipantsCrmData(
Team $team,
Activity $activity,
Collection $participants,
?ServiceInterface $crmService = null,
): array {
$matchedRecords = [];
$matchedDomainRecords = [];
$this->validateCrmConfiguration($activity);
$this->decorator->setConfiguration($activity->getCrm());
$this->decorator->setCrmService($crmService);
foreach ($participants as $participant) {
if ($this->shouldSkipParticipant($participant)) {
continue;
}
if (! $this->shouldPerformLookup($participant, $team)) {
$this->logger->info('[CrmActivityService] Email domain belongs to the team, skipping crm lookup', [
'activity_id' => $activity->getId(),
'team_id' => $team->getId(),
'email' => $participant->getEmailAddress(),
]);
$this->attachUserIfExists($participant, $team);
continue;
}
$records = $this->findCrmRecords($participant, $activity);
if (! empty($records)) {
$matchedRecords[] = $records;
} else {
$records = $this->findCrmDomainRecords(
crmService: $crmService,
participant: $participant,
activity: $activity,
);
if (! empty($records)) {
$matchedDomainRecords[] = $records;
}
}
if (empty($records)) {
continue;
}
try {
$activity->updateParticipantCrmData($records, $participant);
} catch (Throwable $ex) {
$this->logger->error('[CrmActivityService] Failed to update participant CRM data', [
'activity_id' => $activity->getId(),
'participant_id' => $participant->getId(),
'exception' => $ex->getMessage(),
]);
continue;
}
}
$bestMatch = $this->getBestMatch(
matchedRecords : $matchedRecords,
matchedDomainRecords: $matchedDomainRecords,
);
$this->logger->info('[CrmActivityService] CRM matching completed', [
'activity_id' => $activity->getId(),
'participants_processed' => $participants->count(),
'exact_matches' => count($matchedRecords),
'domain_matches' => count($matchedDomainRecords),
'best_match_found' => ! empty($bestMatch),
]);
return $bestMatch;
}
private function shouldPerformLookup(Participant $participant, Team $team): bool
{
if ($participant->hasEmailAddress()) {
return $this->emailHelper->shouldPerformLookup($team, $participant->getEmailAddress());
}
return true;
}
private function validateCrmConfiguration(Activity $activity): void
{
if ($activity->getCrm() === null) {
throw new InvalidArgumentException('Cannot find CRM configuration');
}
}
private function getBestMatch(?array $matchedRecords, ?array $matchedDomainRecords): array
{
return RecordSelector::pickBestFromLists($matchedRecords, $matchedDomainRecords);
}
private function findCrmRecords(Participant $participant, Activity $activity): ?array
{
$records = null;
if ($participant->hasEmailAddress()) {
$records = $this->decorator->matchExactlyByEmail(
email: $participant->getEmailAddress(),
userId: $activity->getUser()->getId()
);
}
if (empty($records) && $participant->getPhoneNumber() !== null) {
$records = $this->decorator->matchByPhone(
phone: $participant->getPhoneNumber(),
userId: $activity->getUser()->getId(),
);
}
if (empty($records) && $participant->getName() !== null) {
$records = $this->decorator->matchByName(
name: $participant->getName(),
userId: $activity->getUser()->getId(),
);
}
return $records;
}
private function shouldSkipParticipant(Participant $participant): bool
{
return $participant->hasUser();
}
private function attachUserIfExists(Participant $participant, Team $team): void
{
if ($participant->hasEmailAddress() === false) {
return;
}
$user = $this->teamRepository->findActiveTeamMemberByEmail($team, $participant->getEmailAddress());
if ($user instanceof User) {
$participant->user_id = $user->getId();
$participant->save();
}
}
private function findCrmDomainRecords(
?ServiceInterface $crmService,
Participant $participant,
Activity $activity,
): array {
if ($participant->hasEmailAddress()) {
$this->decorator->setConfiguration($activity->getCrm());
$this->decorator->setCrmService($crmService);
$records = $this->decorator->matchByDomain(
email: $participant->getEmailAddress(),
userId: $activity->getUser()->getId()
);
if (! empty($records)) {
return $records;
}
}
return [];
}
}
Execute
Explain Plan
Browse Query History
View Parameters
Open Query Execution Settings…
In-Editor Results
Tx: Auto
Cancel Running Statements
Playground
jiminny
Sync Changes
Hide This Notification
Code changed:
Hide
45
1
41...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"#12121 on JY-20963-fix-import-on-deleted-entity, menu","depth":5,"on_screen":true,"help_text":"Pull request #12121 exists for current branch JY-20963-fix-import-on-deleted-entity, but pull request details loading failed","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"ServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'ServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'ServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"22","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"5","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Crm;\n\nuse Illuminate\\Support\\Collection;\nuse Jiminny\\Contracts\\Repositories\\TeamRepository;\nuse Jiminny\\Contracts\\Services\\Crm\\ServiceInterface;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Exceptions\\SocialAccountTokenInvalidException;\nuse Jiminny\\Models\\Account;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Contact;\nuse Jiminny\\Models\\Lead;\nuse Jiminny\\Models\\Opportunity;\nuse Jiminny\\Models\\Participant;\nuse Jiminny\\Models\\Stage;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\ResolveTeamCrmConnection;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Psr\\Log\\LoggerInterface;\nuse Exception;\nuse Throwable;\n\nclass CrmActivityService\n{\n public function __construct(\n private readonly TeamRepository $teamRepository,\n private readonly CachedCrmServiceDecorator $decorator,\n private readonly EmailHelper $emailHelper,\n private readonly ResolveTeamCrmConnection $teamCrmResolver,\n private readonly LoggerInterface $logger,\n ) {\n }\n\n /**\n * Updates CRM data for an activity and its participants.\n *\n * NOTE: This method performs multiple database writes and should be called\n * within a transaction by the caller to ensure atomicity.\n *\n * @param Activity $activity\n * @param bool $remoteSearch\n *\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception\n */\n public function updateCrmData(\n Activity $activity,\n bool $remoteSearch = false,\n ): void {\n $crmService = null;\n $participants = $activity->getParticipants();\n $team = $activity->getTeam();\n\n $prospectSearchStrategy = ProspectSearchStrategyFactory::match($team);\n if ($prospectSearchStrategy->ignoreCrmMatchData()) {\n $this->logger->info('[CrmActivityService] Ignoring crm data because of prospect strategy', [\n 'activity_id' => $activity->getId(),\n 'strategy' => get_class($prospectSearchStrategy),\n ]);\n\n return;\n }\n\n if ($remoteSearch) {\n try {\n $crmService = $this->teamCrmResolver->resolveForTeam($team);\n } catch (SocialAccountTokenInvalidException) {\n $this->logger->warning('[CrmActivityService] CRM token expired, falling back to local search', [\n 'activity_id' => $activity->getId(),\n 'team_id' => $team->getId(),\n ]);\n }\n }\n\n $records = $this->updateParticipantsCrmData(\n team: $team,\n activity: $activity,\n participants: $participants,\n crmService: $crmService,\n );\n\n if (! empty($records)) {\n $activity->updateActivityCrmData($records);\n }\n\n $activity->refresh();\n }\n\n /**\n * @param Collection<Participant> $participants\n *\n * @throws Exception\n *\n * @return array{\n * Lead|null,\n * Account|null,\n * Opportunity|null,\n * Contact|null,\n * Stage|null,\n * string|null\n *}|array{}\n */\n private function updateParticipantsCrmData(\n Team $team,\n Activity $activity,\n Collection $participants,\n ?ServiceInterface $crmService = null,\n ): array {\n $matchedRecords = [];\n $matchedDomainRecords = [];\n\n $this->validateCrmConfiguration($activity);\n $this->decorator->setConfiguration($activity->getCrm());\n $this->decorator->setCrmService($crmService);\n\n foreach ($participants as $participant) {\n if ($this->shouldSkipParticipant($participant)) {\n continue;\n }\n\n if (! $this->shouldPerformLookup($participant, $team)) {\n $this->logger->info('[CrmActivityService] Email domain belongs to the team, skipping crm lookup', [\n 'activity_id' => $activity->getId(),\n 'team_id' => $team->getId(),\n 'email' => $participant->getEmailAddress(),\n ]);\n\n $this->attachUserIfExists($participant, $team);\n\n continue;\n }\n\n $records = $this->findCrmRecords($participant, $activity);\n\n if (! empty($records)) {\n $matchedRecords[] = $records;\n } else {\n $records = $this->findCrmDomainRecords(\n crmService: $crmService,\n participant: $participant,\n activity: $activity,\n );\n if (! empty($records)) {\n $matchedDomainRecords[] = $records;\n }\n }\n\n if (empty($records)) {\n continue;\n }\n\n try {\n $activity->updateParticipantCrmData($records, $participant);\n } catch (Throwable $ex) {\n $this->logger->error('[CrmActivityService] Failed to update participant CRM data', [\n 'activity_id' => $activity->getId(),\n 'participant_id' => $participant->getId(),\n 'exception' => $ex->getMessage(),\n ]);\n\n continue;\n }\n }\n\n $bestMatch = $this->getBestMatch(\n matchedRecords : $matchedRecords,\n matchedDomainRecords: $matchedDomainRecords,\n );\n\n $this->logger->info('[CrmActivityService] CRM matching completed', [\n 'activity_id' => $activity->getId(),\n 'participants_processed' => $participants->count(),\n 'exact_matches' => count($matchedRecords),\n 'domain_matches' => count($matchedDomainRecords),\n 'best_match_found' => ! empty($bestMatch),\n ]);\n\n return $bestMatch;\n }\n\n private function shouldPerformLookup(Participant $participant, Team $team): bool\n {\n if ($participant->hasEmailAddress()) {\n return $this->emailHelper->shouldPerformLookup($team, $participant->getEmailAddress());\n }\n\n return true;\n }\n\n private function validateCrmConfiguration(Activity $activity): void\n {\n if ($activity->getCrm() === null) {\n throw new InvalidArgumentException('Cannot find CRM configuration');\n }\n }\n\n private function getBestMatch(?array $matchedRecords, ?array $matchedDomainRecords): array\n {\n return RecordSelector::pickBestFromLists($matchedRecords, $matchedDomainRecords);\n }\n\n private function findCrmRecords(Participant $participant, Activity $activity): ?array\n {\n $records = null;\n\n if ($participant->hasEmailAddress()) {\n $records = $this->decorator->matchExactlyByEmail(\n email: $participant->getEmailAddress(),\n userId: $activity->getUser()->getId()\n );\n }\n\n if (empty($records) && $participant->getPhoneNumber() !== null) {\n $records = $this->decorator->matchByPhone(\n phone: $participant->getPhoneNumber(),\n userId: $activity->getUser()->getId(),\n );\n }\n\n if (empty($records) && $participant->getName() !== null) {\n $records = $this->decorator->matchByName(\n name: $participant->getName(),\n userId: $activity->getUser()->getId(),\n );\n }\n\n return $records;\n }\n\n private function shouldSkipParticipant(Participant $participant): bool\n {\n return $participant->hasUser();\n }\n\n private function attachUserIfExists(Participant $participant, Team $team): void\n {\n if ($participant->hasEmailAddress() === false) {\n return;\n }\n\n $user = $this->teamRepository->findActiveTeamMemberByEmail($team, $participant->getEmailAddress());\n\n if ($user instanceof User) {\n $participant->user_id = $user->getId();\n $participant->save();\n }\n }\n\n private function findCrmDomainRecords(\n ?ServiceInterface $crmService,\n Participant $participant,\n Activity $activity,\n ): array {\n if ($participant->hasEmailAddress()) {\n $this->decorator->setConfiguration($activity->getCrm());\n $this->decorator->setCrmService($crmService);\n\n $records = $this->decorator->matchByDomain(\n email: $participant->getEmailAddress(),\n userId: $activity->getUser()->getId()\n );\n if (! empty($records)) {\n return $records;\n }\n }\n\n return [];\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Crm;\n\nuse Illuminate\\Support\\Collection;\nuse Jiminny\\Contracts\\Repositories\\TeamRepository;\nuse Jiminny\\Contracts\\Services\\Crm\\ServiceInterface;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Exceptions\\SocialAccountTokenInvalidException;\nuse Jiminny\\Models\\Account;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Contact;\nuse Jiminny\\Models\\Lead;\nuse Jiminny\\Models\\Opportunity;\nuse Jiminny\\Models\\Participant;\nuse Jiminny\\Models\\Stage;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\ResolveTeamCrmConnection;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Psr\\Log\\LoggerInterface;\nuse Exception;\nuse Throwable;\n\nclass CrmActivityService\n{\n public function __construct(\n private readonly TeamRepository $teamRepository,\n private readonly CachedCrmServiceDecorator $decorator,\n private readonly EmailHelper $emailHelper,\n private readonly ResolveTeamCrmConnection $teamCrmResolver,\n private readonly LoggerInterface $logger,\n ) {\n }\n\n /**\n * Updates CRM data for an activity and its participants.\n *\n * NOTE: This method performs multiple database writes and should be called\n * within a transaction by the caller to ensure atomicity.\n *\n * @param Activity $activity\n * @param bool $remoteSearch\n *\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception\n */\n public function updateCrmData(\n Activity $activity,\n bool $remoteSearch = false,\n ): void {\n $crmService = null;\n $participants = $activity->getParticipants();\n $team = $activity->getTeam();\n\n $prospectSearchStrategy = ProspectSearchStrategyFactory::match($team);\n if ($prospectSearchStrategy->ignoreCrmMatchData()) {\n $this->logger->info('[CrmActivityService] Ignoring crm data because of prospect strategy', [\n 'activity_id' => $activity->getId(),\n 'strategy' => get_class($prospectSearchStrategy),\n ]);\n\n return;\n }\n\n if ($remoteSearch) {\n try {\n $crmService = $this->teamCrmResolver->resolveForTeam($team);\n } catch (SocialAccountTokenInvalidException) {\n $this->logger->warning('[CrmActivityService] CRM token expired, falling back to local search', [\n 'activity_id' => $activity->getId(),\n 'team_id' => $team->getId(),\n ]);\n }\n }\n\n $records = $this->updateParticipantsCrmData(\n team: $team,\n activity: $activity,\n participants: $participants,\n crmService: $crmService,\n );\n\n if (! empty($records)) {\n $activity->updateActivityCrmData($records);\n }\n\n $activity->refresh();\n }\n\n /**\n * @param Collection<Participant> $participants\n *\n * @throws Exception\n *\n * @return array{\n * Lead|null,\n * Account|null,\n * Opportunity|null,\n * Contact|null,\n * Stage|null,\n * string|null\n *}|array{}\n */\n private function updateParticipantsCrmData(\n Team $team,\n Activity $activity,\n Collection $participants,\n ?ServiceInterface $crmService = null,\n ): array {\n $matchedRecords = [];\n $matchedDomainRecords = [];\n\n $this->validateCrmConfiguration($activity);\n $this->decorator->setConfiguration($activity->getCrm());\n $this->decorator->setCrmService($crmService);\n\n foreach ($participants as $participant) {\n if ($this->shouldSkipParticipant($participant)) {\n continue;\n }\n\n if (! $this->shouldPerformLookup($participant, $team)) {\n $this->logger->info('[CrmActivityService] Email domain belongs to the team, skipping crm lookup', [\n 'activity_id' => $activity->getId(),\n 'team_id' => $team->getId(),\n 'email' => $participant->getEmailAddress(),\n ]);\n\n $this->attachUserIfExists($participant, $team);\n\n continue;\n }\n\n $records = $this->findCrmRecords($participant, $activity);\n\n if (! empty($records)) {\n $matchedRecords[] = $records;\n } else {\n $records = $this->findCrmDomainRecords(\n crmService: $crmService,\n participant: $participant,\n activity: $activity,\n );\n if (! empty($records)) {\n $matchedDomainRecords[] = $records;\n }\n }\n\n if (empty($records)) {\n continue;\n }\n\n try {\n $activity->updateParticipantCrmData($records, $participant);\n } catch (Throwable $ex) {\n $this->logger->error('[CrmActivityService] Failed to update participant CRM data', [\n 'activity_id' => $activity->getId(),\n 'participant_id' => $participant->getId(),\n 'exception' => $ex->getMessage(),\n ]);\n\n continue;\n }\n }\n\n $bestMatch = $this->getBestMatch(\n matchedRecords : $matchedRecords,\n matchedDomainRecords: $matchedDomainRecords,\n );\n\n $this->logger->info('[CrmActivityService] CRM matching completed', [\n 'activity_id' => $activity->getId(),\n 'participants_processed' => $participants->count(),\n 'exact_matches' => count($matchedRecords),\n 'domain_matches' => count($matchedDomainRecords),\n 'best_match_found' => ! empty($bestMatch),\n ]);\n\n return $bestMatch;\n }\n\n private function shouldPerformLookup(Participant $participant, Team $team): bool\n {\n if ($participant->hasEmailAddress()) {\n return $this->emailHelper->shouldPerformLookup($team, $participant->getEmailAddress());\n }\n\n return true;\n }\n\n private function validateCrmConfiguration(Activity $activity): void\n {\n if ($activity->getCrm() === null) {\n throw new InvalidArgumentException('Cannot find CRM configuration');\n }\n }\n\n private function getBestMatch(?array $matchedRecords, ?array $matchedDomainRecords): array\n {\n return RecordSelector::pickBestFromLists($matchedRecords, $matchedDomainRecords);\n }\n\n private function findCrmRecords(Participant $participant, Activity $activity): ?array\n {\n $records = null;\n\n if ($participant->hasEmailAddress()) {\n $records = $this->decorator->matchExactlyByEmail(\n email: $participant->getEmailAddress(),\n userId: $activity->getUser()->getId()\n );\n }\n\n if (empty($records) && $participant->getPhoneNumber() !== null) {\n $records = $this->decorator->matchByPhone(\n phone: $participant->getPhoneNumber(),\n userId: $activity->getUser()->getId(),\n );\n }\n\n if (empty($records) && $participant->getName() !== null) {\n $records = $this->decorator->matchByName(\n name: $participant->getName(),\n userId: $activity->getUser()->getId(),\n );\n }\n\n return $records;\n }\n\n private function shouldSkipParticipant(Participant $participant): bool\n {\n return $participant->hasUser();\n }\n\n private function attachUserIfExists(Participant $participant, Team $team): void\n {\n if ($participant->hasEmailAddress() === false) {\n return;\n }\n\n $user = $this->teamRepository->findActiveTeamMemberByEmail($team, $participant->getEmailAddress());\n\n if ($user instanceof User) {\n $participant->user_id = $user->getId();\n $participant->save();\n }\n }\n\n private function findCrmDomainRecords(\n ?ServiceInterface $crmService,\n Participant $participant,\n Activity $activity,\n ): array {\n if ($participant->hasEmailAddress()) {\n $this->decorator->setConfiguration($activity->getCrm());\n $this->decorator->setCrmService($crmService);\n\n $records = $this->decorator->matchByDomain(\n email: $participant->getEmailAddress(),\n userId: $activity->getUser()->getId()\n );\n if (! empty($records)) {\n return $records;\n }\n }\n\n return [];\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Execute","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Explain Plan","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Browse Query History","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"View Parameters","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Open Query Execution Settings…","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"In-Editor Results","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Tx: Auto","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Cancel Running Statements","depth":4,"on_screen":true,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Playground","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"jiminny","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"45","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"1","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"41","depth":4,"on_screen":true,"role_description":"text"}]...
|
-5725748941061649983
|
-8454432716379441058
|
visual_change
|
accessibility
|
NULL
|
Project: faVsco.js, menu
#12121 on JY-20963-fix-im Project: faVsco.js, menu
#12121 on JY-20963-fix-import-on-deleted-entity, menu
Start Listening for PHP Debug Connections
ServiceTest
Run 'ServiceTest'
Debug 'ServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
22
5
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Crm;
use Illuminate\Support\Collection;
use Jiminny\Contracts\Repositories\TeamRepository;
use Jiminny\Contracts\Services\Crm\ServiceInterface;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Models\Account;
use Jiminny\Models\Activity;
use Jiminny\Models\Contact;
use Jiminny\Models\Lead;
use Jiminny\Models\Opportunity;
use Jiminny\Models\Participant;
use Jiminny\Models\Stage;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Services\ResolveTeamCrmConnection;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Psr\Log\LoggerInterface;
use Exception;
use Throwable;
class CrmActivityService
{
public function __construct(
private readonly TeamRepository $teamRepository,
private readonly CachedCrmServiceDecorator $decorator,
private readonly EmailHelper $emailHelper,
private readonly ResolveTeamCrmConnection $teamCrmResolver,
private readonly LoggerInterface $logger,
) {
}
/**
* Updates CRM data for an activity and its participants.
*
* NOTE: This method performs multiple database writes and should be called
* within a transaction by the caller to ensure atomicity.
*
* @param Activity $activity
* @param bool $remoteSearch
*
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception
*/
public function updateCrmData(
Activity $activity,
bool $remoteSearch = false,
): void {
$crmService = null;
$participants = $activity->getParticipants();
$team = $activity->getTeam();
$prospectSearchStrategy = ProspectSearchStrategyFactory::match($team);
if ($prospectSearchStrategy->ignoreCrmMatchData()) {
$this->logger->info('[CrmActivityService] Ignoring crm data because of prospect strategy', [
'activity_id' => $activity->getId(),
'strategy' => get_class($prospectSearchStrategy),
]);
return;
}
if ($remoteSearch) {
try {
$crmService = $this->teamCrmResolver->resolveForTeam($team);
} catch (SocialAccountTokenInvalidException) {
$this->logger->warning('[CrmActivityService] CRM token expired, falling back to local search', [
'activity_id' => $activity->getId(),
'team_id' => $team->getId(),
]);
}
}
$records = $this->updateParticipantsCrmData(
team: $team,
activity: $activity,
participants: $participants,
crmService: $crmService,
);
if (! empty($records)) {
$activity->updateActivityCrmData($records);
}
$activity->refresh();
}
/**
* @param Collection<Participant> $participants
*
* @throws Exception
*
* @return array{
* Lead|null,
* Account|null,
* Opportunity|null,
* Contact|null,
* Stage|null,
* string|null
*}|array{}
*/
private function updateParticipantsCrmData(
Team $team,
Activity $activity,
Collection $participants,
?ServiceInterface $crmService = null,
): array {
$matchedRecords = [];
$matchedDomainRecords = [];
$this->validateCrmConfiguration($activity);
$this->decorator->setConfiguration($activity->getCrm());
$this->decorator->setCrmService($crmService);
foreach ($participants as $participant) {
if ($this->shouldSkipParticipant($participant)) {
continue;
}
if (! $this->shouldPerformLookup($participant, $team)) {
$this->logger->info('[CrmActivityService] Email domain belongs to the team, skipping crm lookup', [
'activity_id' => $activity->getId(),
'team_id' => $team->getId(),
'email' => $participant->getEmailAddress(),
]);
$this->attachUserIfExists($participant, $team);
continue;
}
$records = $this->findCrmRecords($participant, $activity);
if (! empty($records)) {
$matchedRecords[] = $records;
} else {
$records = $this->findCrmDomainRecords(
crmService: $crmService,
participant: $participant,
activity: $activity,
);
if (! empty($records)) {
$matchedDomainRecords[] = $records;
}
}
if (empty($records)) {
continue;
}
try {
$activity->updateParticipantCrmData($records, $participant);
} catch (Throwable $ex) {
$this->logger->error('[CrmActivityService] Failed to update participant CRM data', [
'activity_id' => $activity->getId(),
'participant_id' => $participant->getId(),
'exception' => $ex->getMessage(),
]);
continue;
}
}
$bestMatch = $this->getBestMatch(
matchedRecords : $matchedRecords,
matchedDomainRecords: $matchedDomainRecords,
);
$this->logger->info('[CrmActivityService] CRM matching completed', [
'activity_id' => $activity->getId(),
'participants_processed' => $participants->count(),
'exact_matches' => count($matchedRecords),
'domain_matches' => count($matchedDomainRecords),
'best_match_found' => ! empty($bestMatch),
]);
return $bestMatch;
}
private function shouldPerformLookup(Participant $participant, Team $team): bool
{
if ($participant->hasEmailAddress()) {
return $this->emailHelper->shouldPerformLookup($team, $participant->getEmailAddress());
}
return true;
}
private function validateCrmConfiguration(Activity $activity): void
{
if ($activity->getCrm() === null) {
throw new InvalidArgumentException('Cannot find CRM configuration');
}
}
private function getBestMatch(?array $matchedRecords, ?array $matchedDomainRecords): array
{
return RecordSelector::pickBestFromLists($matchedRecords, $matchedDomainRecords);
}
private function findCrmRecords(Participant $participant, Activity $activity): ?array
{
$records = null;
if ($participant->hasEmailAddress()) {
$records = $this->decorator->matchExactlyByEmail(
email: $participant->getEmailAddress(),
userId: $activity->getUser()->getId()
);
}
if (empty($records) && $participant->getPhoneNumber() !== null) {
$records = $this->decorator->matchByPhone(
phone: $participant->getPhoneNumber(),
userId: $activity->getUser()->getId(),
);
}
if (empty($records) && $participant->getName() !== null) {
$records = $this->decorator->matchByName(
name: $participant->getName(),
userId: $activity->getUser()->getId(),
);
}
return $records;
}
private function shouldSkipParticipant(Participant $participant): bool
{
return $participant->hasUser();
}
private function attachUserIfExists(Participant $participant, Team $team): void
{
if ($participant->hasEmailAddress() === false) {
return;
}
$user = $this->teamRepository->findActiveTeamMemberByEmail($team, $participant->getEmailAddress());
if ($user instanceof User) {
$participant->user_id = $user->getId();
$participant->save();
}
}
private function findCrmDomainRecords(
?ServiceInterface $crmService,
Participant $participant,
Activity $activity,
): array {
if ($participant->hasEmailAddress()) {
$this->decorator->setConfiguration($activity->getCrm());
$this->decorator->setCrmService($crmService);
$records = $this->decorator->matchByDomain(
email: $participant->getEmailAddress(),
userId: $activity->getUser()->getId()
);
if (! empty($records)) {
return $records;
}
}
return [];
}
}
Execute
Explain Plan
Browse Query History
View Parameters
Open Query Execution Settings…
In-Editor Results
Tx: Auto
Cancel Running Statements
Playground
jiminny
Sync Changes
Hide This Notification
Code changed:
Hide
45
1
41...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
86389
|
2963
|
7
|
2026-05-28T14:07:03.588493+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-28/1779 /Users/lukas/.screenpipe/data/data/2026-05-28/1779977223588_m2.jpg...
|
PhpStorm
|
faVsco.js – CrmActivityService.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
#12121 on JY-20963-fix-im Project: faVsco.js, menu
#12121 on JY-20963-fix-import-on-deleted-entity, menu
Start Listening for PHP Debug Connections
ServiceTest
Run 'ServiceTest'
Debug 'ServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
22
5
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Crm;
use Illuminate\Support\Collection;
use Jiminny\Contracts\Repositories\TeamRepository;
use Jiminny\Contracts\Services\Crm\ServiceInterface;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Models\Account;
use Jiminny\Models\Activity;
use Jiminny\Models\Contact;
use Jiminny\Models\Lead;
use Jiminny\Models\Opportunity;
use Jiminny\Models\Participant;
use Jiminny\Models\Stage;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Services\ResolveTeamCrmConnection;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Psr\Log\LoggerInterface;
use Exception;
use Throwable;
class CrmActivityService
{
public function __construct(
private readonly TeamRepository $teamRepository,
private readonly CachedCrmServiceDecorator $decorator,
private readonly EmailHelper $emailHelper,
private readonly ResolveTeamCrmConnection $teamCrmResolver,
private readonly LoggerInterface $logger,
) {
}
/**
* Updates CRM data for an activity and its participants.
*
* NOTE: This method performs multiple database writes and should be called
* within a transaction by the caller to ensure atomicity.
*
* @param Activity $activity
* @param bool $remoteSearch
*
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception
*/
public function updateCrmData(
Activity $activity,
bool $remoteSearch = false,
): void {
$crmService = null;
$participants = $activity->getParticipants();
$team = $activity->getTeam();
$prospectSearchStrategy = ProspectSearchStrategyFactory::match($team);
if ($prospectSearchStrategy->ignoreCrmMatchData()) {
$this->logger->info('[CrmActivityService] Ignoring crm data because of prospect strategy', [
'activity_id' => $activity->getId(),
'strategy' => get_class($prospectSearchStrategy),
]);
return;
}
if ($remoteSearch) {
try {
$crmService = $this->teamCrmResolver->resolveForTeam($team);
} catch (SocialAccountTokenInvalidException) {
$this->logger->warning('[CrmActivityService] CRM token expired, falling back to local search', [
'activity_id' => $activity->getId(),
'team_id' => $team->getId(),
]);
}
}
$records = $this->updateParticipantsCrmData(
team: $team,
activity: $activity,
participants: $participants,
crmService: $crmService,
);
if (! empty($records)) {
$activity->updateActivityCrmData($records);
}
$activity->refresh();
}
/**
* @param Collection<Participant> $participants
*
* @throws Exception
*
* @return array{
* Lead|null,
* Account|null,
* Opportunity|null,
* Contact|null,
* Stage|null,
* string|null
*}|array{}
*/
private function updateParticipantsCrmData(
Team $team,
Activity $activity,
Collection $participants,
?ServiceInterface $crmService = null,
): array {
$matchedRecords = [];
$matchedDomainRecords = [];
$this->validateCrmConfiguration($activity);
$this->decorator->setConfiguration($activity->getCrm());
$this->decorator->setCrmService($crmService);
foreach ($participants as $participant) {
if ($this->shouldSkipParticipant($participant)) {
continue;
}
if (! $this->shouldPerformLookup($participant, $team)) {
$this->logger->info('[CrmActivityService] Email domain belongs to the team, skipping crm lookup', [
'activity_id' => $activity->getId(),
'team_id' => $team->getId(),
'email' => $participant->getEmailAddress(),
]);
$this->attachUserIfExists($participant, $team);
continue;
}
$records = $this->findCrmRecords($participant, $activity);
if (! empty($records)) {
$matchedRecords[] = $records;
} else {
$records = $this->findCrmDomainRecords(
crmService: $crmService,
participant: $participant,
activity: $activity,
);
if (! empty($records)) {
$matchedDomainRecords[] = $records;
}
}
if (empty($records)) {
continue;
}
try {
$activity->updateParticipantCrmData($records, $participant);
} catch (Throwable $ex) {
$this->logger->error('[CrmActivityService] Failed to update participant CRM data', [
'activity_id' => $activity->getId(),
'participant_id' => $participant->getId(),
'exception' => $ex->getMessage(),
]);
continue;
}
}
$bestMatch = $this->getBestMatch(
matchedRecords : $matchedRecords,
matchedDomainRecords: $matchedDomainRecords,
);
$this->logger->info('[CrmActivityService] CRM matching completed', [
'activity_id' => $activity->getId(),
'participants_processed' => $participants->count(),
'exact_matches' => count($matchedRecords),
'domain_matches' => count($matchedDomainRecords),
'best_match_found' => ! empty($bestMatch),
]);
return $bestMatch;
}
private function shouldPerformLookup(Participant $participant, Team $team): bool
{
if ($participant->hasEmailAddress()) {
return $this->emailHelper->shouldPerformLookup($team, $participant->getEmailAddress());
}
return true;
}
private function validateCrmConfiguration(Activity $activity): void
{
if ($activity->getCrm() === null) {
throw new InvalidArgumentException('Cannot find CRM configuration');
}
}
private function getBestMatch(?array $matchedRecords, ?array $matchedDomainRecords): array
{
return RecordSelector::pickBestFromLists($matchedRecords, $matchedDomainRecords);
}
private function findCrmRecords(Participant $participant, Activity $activity): ?array
{
$records = null;
if ($participant->hasEmailAddress()) {
$records = $this->decorator->matchExactlyByEmail(
email: $participant->getEmailAddress(),
userId: $activity->getUser()->getId()
);
}
if (empty($records) && $participant->getPhoneNumber() !== null) {
$records = $this->decorator->matchByPhone(
phone: $participant->getPhoneNumber(),
userId: $activity->getUser()->getId(),
);
}
if (empty($records) && $participant->getName() !== null) {
$records = $this->decorator->matchByName(
name: $participant->getName(),
userId: $activity->getUser()->getId(),
);
}
return $records;
}
private function shouldSkipParticipant(Participant $participant): bool
{
return $participant->hasUser();
}
private function attachUserIfExists(Participant $participant, Team $team): void
{
if ($participant->hasEmailAddress() === false) {
return;
}
$user = $this->teamRepository->findActiveTeamMemberByEmail($team, $participant->getEmailAddress());
if ($user instanceof User) {
$participant->user_id = $user->getId();
$participant->save();
}
}
private function findCrmDomainRecords(
?ServiceInterface $crmService,
Participant $participant,
Activity $activity,
): array {
if ($participant->hasEmailAddress()) {
$this->decorator->setConfiguration($activity->getCrm());
$this->decorator->setCrmService($crmService);
$records = $this->decorator->matchByDomain(
email: $participant->getEmailAddress(),
userId: $activity->getUser()->getId()
);
if (! empty($records)) {
return $records;
}
}
return [];
}
}
Execute
Explain Plan
Browse Query History
View Parameters
Open Query Execution Settings…
In-Editor Results
Tx: Auto
Cancel Running Statements
Playground
jiminny
Sync Changes
Hide This Notification
Code changed:
Hide
45
1
41
66
Previous Highlighted Error
Next Highlighted Error
SELECT * FROM teams WHERE name LIKE '%litify%'; # 1069, 994, 24993
SELECT * FROM users WHERE id = 25061;
SELECT * FROM crm_profiles WHERE crm_configuration_id = 994;
SELECT * FROM crm_profiles WHERE user_id = 25061;
select * from crm_configurations where id = 834;
SELECT * FROM teams WHERE id = 882;
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 882 and sa.provider = 'hubspot';
SELECT * FROM crm_configurations where id = 882; # 933 - GoGlobal
SELECT * FROM opportunities WHERE team_id = 933 order by updated_at desc;
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 933 and sa.provider = 'hubspot';
SELECT * FROM crm_configurations WHERE provider = 'hubspot' and crm_provider_id = 7270388;
SELECT * FROM contacts where crm_configuration_id = 834;
SELECT * FROM opportunities WHERE team_id = 933
# AND crm_provider_id IN ('20131586060','46017317898','52543911090','53451356564','54101251892','54323768459');
AND id IN (8482561,18352941,19042734,19232139,19445140,19472541);
SELECT * FROM opportunity_contacts
WHERE opportunity_id IN (8482561,18352941,19042734,19232139,19445140,19472541);
# [PASSWORD_DOTS]
SELECT * FROM crm_configurations where id = 485; #
SELECT * FROM opportunities WHERE team_id = 933 order by updated_at desc;
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 933 and sa.provider = 'hubspot';
select crm.provider, l.* from leads l join crm_configurations crm on l.crm_configuration_id = crm.id
where crm.provider NOT IN ('salesforce', 'integration-app', 'bullhorn', 'copper')
# and l.converted_at IS NOT NULL
;
# [PASSWORD_DOTS]
SELECT * FROM activities a WHERE type IN ('email-inbound', 'email-outbound')
and opportunity_id IS NULL
order by id desc;
SELECT * FROM teams WHERE id = 604; # 598
SELECT * FROM activities WHERE id = 74410828; # [EMAIL]
SELECT * FROM accounts WHERE id = 20068382;
SELECT * FROM accounts WHERE id = 35186038;
SELECT * FROM contacts WHERE team_id = 852 and updated_at > '2026-01-23 12:30:00' order by updated_at desc;
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 559 and sa.provider = 'hubspot';
SELECT * FROM activities WHERE uuid_to_bin('cb6342b6-a183-401c-b0af-ede92b2ae763') = uuid;
select * from sidekick_settings where team_id = 781;
SELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id = 26651871; # Teya
SELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id = 7562435;
SELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id = 8420347; # opflit 2100
SELECT * FROM crm_layouts WHERE crm_configuration_id = 711;
SELECT * FROM activities where crm_configuration_id = 711 and crm_provider_id IS NULL
and is_internal = 0 and status = 'completed'
order by id desc;
SELECT * FROM crm_layout_entities
WHERE crm_layout_id IN (2352, 2353);
;
SELECT * FROM crm_configurations where provider = 'hubspot' and id = 530;
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 556 and sa.provider = 'hubspot';
SELECT * FROM activities WHERE uuid_to_bin('c6ca4b22-7738-4563-a95d-b8a9598924ae') = uuid;
SELECT * FROM activities WHERE uuid_to_bin('442abb2b-28bd-4be8-9c25-19e9bf02766d') = uuid;
select * from contacts
where crm_configuration_id = 530
and crm_provider_id = 872252;
select * from activities where crm_configuration_id = 530
and user_id = 14343 and type like '%softphone%'
and created_at between '2026-01-28 15:00:00' and '2026-01-28 15:10:00';
SELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id = 25666868; # Teya
SELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id = 8646335; # Teya
SELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id IN (5933397);
SELECT t.name, t.id, t.owner_id, c.id, c.provider, c.crm_base_url FROM teams t
JOIN crm_configurations c ON t.id = c.team_id
WHERE t.status = 'active';
SELECT * FROM teams where id = 1091;
SELECT * FROM crm_configurations where team_id = 1091;
SELECT * FROM activity_providers where team_id = 1091;
SELECT * FROM activities where crm_configuration_id = 1024 and type IN ('softphone', 'softphone-outbound')
and provider NOT IN ('hubspot', 'aircall')
# and telephony_provider_id = '019c1131-a22f-4792-b9ea-20adf6a02ed0'
order by id desc;
SELECT * FROM teams WHERE name LIKE '%Leadventure%';
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 1091 and sa.provider = 'salesforce';
SELECT * FROM teams WHERE name LIKE '%Wilson%'; # 862, 812
SELECT * FROM teams where id = 862;
SELECT * FROM crm_configurations where team_id = 862;
SELECT * FROM activity_providers where team_id = 862;
SELECT * FROM activities where crm_configuration_id = 812 and type IN ('softphone', 'softphone-outbound')
and provider NOT IN ('hubspot', 'aircall')
# and telephony_provider_id = '019c1131-a22f-4792-b9ea-20adf6a02ed0'
order by id desc;
SELECT t.id, crm.id, crm.provider, ap.* FROM teams t
join crm_configurations crm on t.id = crm.team_id
join activity_providers ap on t.id = ap.team_id
where t.status = 'active' and ap.is_enabled = 1
and crm.provider = 'hubspot'
and ap.provider NOT IN ('hubspot', 'aircall', 'uploader', 'gong', 'twilio', 'zoom-bot', 'google-meet', 'ms-teams',
'outreach', 'close', 'ringcentral', 'dialpad', 'zoom-phone');
SELECT * FROM teams where id = 1068;
SELECT * FROM crm_configurations where team_id = 1068;
SELECT * FROM activity_providers where team_id = 1068;
SELECT * FROM activities a
where crm_configuration_id = 993 and type IN ('softphone', 'softphone-outbound')
and a.provider NOT IN ('hubspot', 'uploader', 'gong', 'twilio', 'google-meet', 'ms-teams','close'
)
# and telephony_provider_id = '019c1131-a22f-4792-b9ea-20adf6a02ed0'
order by a.id desc;
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 1068 and sa.provider = 'hubspot';
# [PASSWORD_DOTS]
# [PASSWORD_DOTS]
# [PASSWORD_DOTS]
SELECT * FROM crm_configurations where id = 882; # 933 - GoGlobal , portalId: 6017093
SELECT * FROM opportunities WHERE team_id = 933 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 933 and updated_at > '2026-02-06 00:00:00' order by updated_at desc;
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 933 and sa.provider = 'hubspot';
# [PASSWORD_DOTS]
SELECT * FROM crm_configurations where id = 834; # 882 - AnyVan , portalId: 5468262
SELECT * FROM contacts WHERE crm_configuration_id = 834 and updated_at > '2026-03-30 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE crm_configuration_id = 834 and updated_at > '2026-03-04 08:00:00' order by updated_at desc;
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 882 and sa.provider = 'hubspot';
select * from crm_layouts where crm_configuration_id = 834;
select * from crm_layout_entities where crm_layout_id = 2780;
select * from crm_fields where id IN (321153,321192,321193,321194);
SELECT * FROM opportunities WHERE crm_configuration_id = 834 and id = 10993426;
# [PASSWORD_DOTS]
SELECT * FROM crm_configurations where id = 988; # 1057 - Teya (543ce4f4-168c-4571-91ea-5b35c253f06f) , portalId: 26651871
SELECT * FROM opportunities WHERE team_id = 1057 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 1057 and updated_at > '2026-02-04 00:00:00' order by updated_at desc;
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 1057 and sa.provider = 'hubspot';
SELECT * FROM crm_configurations where id = 533; # 559 - Connectd , portalId: 6710988
SELECT * FROM opportunities WHERE team_id = 559 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 559 and updated_at > '2026-02-04 00:00:00' order by updated_at desc;
SELECT * FROM crm_configurations where id = 801; # 852 - Rise Vision , portalId: 2700250
SELECT * FROM opportunities WHERE team_id = 852 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 852 and updated_at > '2026-02-04 00:00:00' order by updated_at desc; # 6th last
SELECT * FROM crm_configurations where id = 962; # 1034 - evergrowth.io , portalId: 143180990
SELECT * FROM opportunities WHERE team_id = 1034 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 1034 and updated_at > '2026-02-04 00:00:00' order by updated_at desc;
# [PASSWORD_DOTS]
SELECT * FROM crm_configurations where id = 1037; # 1102 - Jibble , portalId: 6649755
SELECT * FROM opportunities WHERE team_id = 1102 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 1102 and updated_at > '2026-02-04 12:30:00' order by updated_at desc; # 8
SELECT * FROM crm_configurations where id = 1015; # 1049 - Travefy , portalId: 48904401
SELECT * FROM opportunities WHERE team_id = 1049 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 1049 and updated_at > '2026-02-04 12:30:00' order by updated_at desc; # 20
SELECT * FROM crm_configurations where id = 64; # 70 - SalaryFinance , portalId: 3404115
SELECT * FROM opportunities WHERE team_id = 70 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 70 and updated_at > '2026-02-04 12:30:00' order by updated_at desc; # 6th last
SELECT * FROM crm_configurations where id = 802; # 853 - Street Group , portalId: 7658438
SELECT * FROM opportunities WHERE team_id = 853 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 853 and updated_at > '2026-02-04 12:30:00' order by updated_at desc; # 10
SELECT * FROM crm_configurations where id = 872; # 921 - In Professional Development , portalId: 9238273
SELECT * FROM opportunities WHERE team_id = 921 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 921 and updated_at > '2026-02-04 12:30:00' order by updated_at desc; # 2
# [PASSWORD_DOTS]
SELECT * FROM crm_configurations where id = 550; # 576 - SeedLegals , portalId: 3028661
SELECT * FROM opportunities WHERE team_id = 576 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 576 and updated_at > '2026-02-05 14:00:00' order by updated_at desc;
SELECT * FROM crm_configurations where id = 989; # 1058 - rtaoutdoor.com , portalId: 22371204
SELECT * FROM opportunities WHERE team_id = 1058 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 1058 and updated_at > '2026-02-05 14:00:00' order by updated_at desc;
SELECT * FROM crm_configurations where id = 896; # 946 - Mintago , portalId: 6621281
SELECT * FROM opportunities WHERE team_id = 946 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 946 and updated_at > '2026-02-05 14:00:00' order by updated_at desc;
SELECT * FROM crm_configurations where id = 617; # 641 - PCS , portalId: 5244937
SELECT * FROM opportunities WHERE team_id = 641 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 641 and updated_at > '2026-02-05 14:00:00' order by updated_at desc; # 7th
# [PASSWORD_DOTS]
SELECT * FROM crm_configurations where id = 649; # 670 - Eventeny , portalId: 4492849
SELECT * FROM opportunities WHERE team_id = 670 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 670 and updated_at > '2026-02-09 08:00:00' order by updated_at desc; #
SELECT * FROM crm_configurations where id = 48; # 51 - CleanCloud , portalId: 4373137
SELECT * FROM opportunities WHERE team_id = 51 and updated_at > '2026-03-04 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 51 and updated_at > '2026-02-09 08:00:00' order by updated_at desc;
select * from users where team_id = 51; # 7783
SELECT * FROM groups WHERE uuid_to_bin('8a8d2cb6-8b55-4fa3-8b5c-5f0e3d8de59a') = uuid; # 1130
select * from activity_searches where user_id = 7783;
select * from activity_search_filters where activity_search_id IN (32291, 32292);
SELECT asf.activity_search_id, asf.id, asf.value
FROM activity_search_filters asf
WHERE asf.filter = 'group_id'
AND asf.value IN (
SELECT CONCAT(
HEX(SUBSTR(uuid, 5, 4)), '-',
HEX(SUBSTR(uuid, 3, 2)), '-',
HEX(SUBSTR(uuid, 1, 2)), '-',
HEX(SUBSTR(uuid, 9, 2)), '-',
HEX(SUBSTR(uuid, 11))
)
FROM groups
WHERE deleted_at IS NOT NULL
);
SELECT * FROM crm_configurations where id = 272; # 290 - Bonham & Brook , portalId: 5705856
SELECT * FROM opportunities WHERE team_id = 290 and updated_at > '2026-02-05 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 290 and updated_at > '2026-02-09 08:00:00' order by updated_at desc; # 6th
# [PASSWORD_DOTS]
SELECT * FROM crm_configurations where provider = 'hubspot';
SELECT * FROM crm_configurations where id = 1056; # 1119 - Chromatic , portalId: 45602133
SELECT * FROM opportunities WHERE team_id = 1119 and remotely_created_at > '2026-02-01 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 1119 and updated_at > '2026-02-09 09:00:00' order by updated_at desc; # null
# [PASSWORD_DOTS]
select * from contacts where crm_provider_id = '003Uu00000ojD4NIAU';
select
cp.*
# DISTINCT t.id
# cp.id, cp.user_id, t.id, cp.crm_configuration_id, cp.contact_fields
FROM crm_profiles cp
JOIN crm_configurations crm on crm.id = cp.crm_configuration_id
JOIN users u on u.id = cp.user_id
JOIN teams t ON t.id = crm.team_id
WHERE crm.provider = 'salesforce' and t.status = 'active'
and cp.archived_at IS NULL and u.deleted_at IS NULL
and t.id NOT IN (1093)
and t.id = 2
and cp.contact_fields IS NULL;
# and c.crm_provider_id = '003Uu00000ojD4NIAU';
SELECT * FROM users WHERE id = 26484;
SELECT * FROM crm_profiles WHERE user_id = 26484;
SELECT * FROM social_accounts WHERE sociable_id = 26484;
SELECT * FROM crm_configurations where provider = 'salesforce';
select * from users where id IN (10022, 10403);
select * from users where team_id IN (526);
select * from teams where id IN (526, 532);
select * from crm_configurations where id IN (500, 516);
select * from crm_profiles where crm_configuration_id IN (500, 516) and user_id IN (10022, 10403);
select * from contacts where crm_configuration_id IN (500, 516) and crm_provider_id = '003Uu00000ojD4NIAU';
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 526 and sa.provider = 'salesforce';
select * from team_settings where team_id IN (526, 532);
select * from users where id IN (22824);
select * from crm_profiles where crm_configuration_id IN (1026);
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 1093 and sa.provider = 'salesforce';
select * from teams where id = 1099;
select * from users where id = 29643
select * from activity_processing_states;
SELECT * FROM teams where name LIKE '%Fare%'; # 233
SELECT * FROM opportunities where crm_configuration_id = 215
# and crm_provider_id = 'oppo_ogESZf2P50nDrd1nGPvKDXeA6sSaTN5v51Lp4ayVzKR'
;
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 1088 and sa.provider = 'hubspot';
SELECT * FROM teams order by updated_at DESC
SELECT * FROM crm_configurations WHERE id = 1019; # SimpleConsign 1088 - no social account
select * from crm_configurations where provider = 'pipedrive';
select * from teams where id = 957;
select * from crm_configurations where id = 957;
SELECT * FROM teams WHERE name LIKE '%Prolific%'; # 544, 518, 10743
SELECT * FROM opportunities where crm_configuration_id = 518 order by id desc;
select * from users where team_id = 1; # 26726 - Gabriela Dureva
SELECT * FROM opportunities where user_id = 26726; # 16834447 - Prolific
select * from activities where user_id = 26726 order by id desc;
select * from contacts where crm_configuration_id = 1
and email IN ('[EMAIL]', '[EMAIL]'); # 2094416, 2093620
SELECT * FROM contacts WHERE id = 6284931;
SELECT p.* FROM activities a JOIN participants p ON a.id = p.activity_id
WHERE a.user_id = 26726 and p.lead_id IN (2094416, 2093620) and a.created_at > '2026-01-01 00:00:00' order by p.email;
select * from activities where id IN (75509259,75509261,75509261,75511034,75026464,75517602,75517605);
select * from crm_configurations where id = 1;
43801692-1aeb-32ce-acba-5b80a479701a
44c3c9cf-6f5e-75f3-8179-bc9f75dd2b1b
405975c0-b3d0-7aaa-821f-09d59cae6dd1
4caf848d-4bed-2299-b248-7788d41f9fca
49bedc3f-f196-eef3-89c3-dea6a3b4aa63
43420989-a09d-b8f8-9806-c8bbf7a02aac
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 1 and sa.provider = 'salesforce';
SELECT * FROM activities WHERE id = 75461988;
SELECT * FROM activities WHERE uuid_to_bin('d6c5052e-e972-49e9-8912-26f2f7d6c5f6') = uuid;
select * from contacts where id = 17900517;
select * from contact_roles cr join crm_configurations crm on cr.crm_configuration_id = crm.id
where crm.provider != 'salesforce';
select * from users where id = 21047;
SELECT * FROM crm_configurations WHERE id = 892;
SELECT * FROM teams WHERE id = 942;
select * from opportunities where team_id = 942 order by updated_at desc;
select * from contacts where team_id = 942 order by updated_at desc;
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 942 and sa.provider = 'hubspot';
SELECT * FROM opportunities where team_id = 1 and crm_provider_id IN ('006Pq00000NeH6XIAV', '006Pq000007z8kdIAA'); # 10697889, 6621430
SELECT * FROM crm_configurations WHERE id = 1;
SELECT * FROM teams WHERE crm_id = 1;
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 1 and sa.provider = 'salesforce';
select id, user_id, opportunity_fields from crm_profiles where crm_configuration_id = 1
SELECT * FROM opportunities where team_id = 1 order by updated_at desc; # 10697889, 6621430
select * from teams where id = 852;
select * from groups where id = 2286;
select * from sidekick_settings where team_id = 852;
select * from default_activity_types where team_id = 852;
SELECT cc.provider, cc.id, p.id, u.*
FROM users u
LEFT JOIN crm_profiles p ON u.id = p.user_id AND p.id IS NULL -- no profile
INNER JOIN teams t ON u.team_id = t.id AND t.status = 'active' -- team is active
INNER JOIN crm_configurations cc ON t.crm_id = cc.id
WHERE u.status = 1 AND u.deleted_at IS NULL
AND u.crm_required = 1
AND u.team_id = 1
ORDER BY u.team_id;
SELECT * FROM crm_profiles cp where cp.crm_configuration_id = 1 and cp.user_id IN (
18481
);
SELECT cc.provider, cc.id, p.id, u.*
FROM users u
LEFT JOIN crm_profiles p ON u.id = p.user_id
INNER JOIN teams t ON u.team_id = t.id AND t.status = 'active'
INNER JOIN crm_configurations cc ON t.crm_id = cc.id
WHERE u.status = 1
AND u.deleted_at IS NULL
AND u.crm_required = 1
# AND u.team_id = 1
AND p.id IS NULL -- Move this condition to WHERE clause
ORDER BY u.team_id;
SELECT * FROM opportunities WHERE id = 20002609;
select * from teams where id = 1122; # Velatir, 29953 - [EMAIL]
select * from crm_configurations where id = 1060;
select * from crm_layouts where crm_configuration_id = 1060;
SELECT * FROM crm_layout_entities WHERE crm_layout_id = 3596;
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 1122 and sa.provider = 'hubspot';
select * from opportunities where team_id = 1122 order by updated_at desc;
select * from crm_field_data where object_type = 'contact';
SELECT * FROM activities WHERE uuid_to_bin('374fc8ed-3315-4c9f-9b25-318b7fd2928f') = uuid; # 76584262
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 248 and sa.provider = 'salesforce';
SELECT * FROM crm_profiles where user_id = 24115; # 005QF000002CswMYAS
SELECT * FROM users where id = 24115;
SELECT * FROM accounts where id = 4002896;
SELECT * FROM teams WHERE name LIKE '%adswerve%';
SELECT * FROM opportunities where crm_configuration_id = 230 AND crm_provider_id IN ("0069N000003GIQ9QAO","0061r000019yGP9AAM","0066900001S2KWlAAN","0066900001TDpj2AAD","0066900001b8uEwAAI","0069N000001rQi0QAE","006QF00000KD40mYAD","006QF00000LzpRJYAZ","0069N000002uomtQAA","0069N000002xlMLQAY","0066900001NV6ubAAD","0061r00001HJp45AAD","006QF00000uTlUoYAK","006QF00000v0bZqYAI");
SELECT * FROM opportunities WHERE crm_configuration_id = 230 AND crm_provider_id = '0069N000003GIQ9QAO'; # 6272203
SELECT u.id, u.email, ac.name, a.* FROM activities a
JOIN users u ON a.user_id = u.id
JOIN accounts ac ON a.account_id = ac.id
WHERE
uuid_to_bin('e3269598-b562-44fb-b5e9-9d2694dc63e0') = a.uuid or
uuid_to_bin('66ddc3ab-4e15-45aa-af0c-248c1eece593') = a.uuid or
uuid_to_bin('826bd328-e1cc-4213-b8d8-572454cacc07') = a.uuid;
select * from users where id = 5825;
SELECT * FROM activities WHERE uuid_to_bin('e56aa2e8-231a-421b-ab1f-cb38ed2bf573') = uuid;
select * from activities where uuid_to_bin('91e13b2f-2d1b-45f8-b1fd-1141b6563782') = uuid;
19594, 862
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 862 and sa.provider = 'salesforce';
select * from automated_reports where id = 36;
select ar.frequency, r.*, ar.* from automated_report_results r
join automated_reports ar on r.report_id = ar.id
where ar.frequency != 'one_off';
select s.* from activity_searches s join users u ON s.user_id = u.id where u.team_id = 882;
select * from nudges n where n.activity_search_id
select * from teams where created_at > '2026-03-09';
SELECT * FROM crm_layouts WHERE crm_configuration_id = 1065; # 1065
SELECT * FROM crm_layout_entities WHERE crm_layout_id = 3617;
select * from users where team_id = 1 and name like '%Lukas%'; # 7160
SELECT * FROM teams WHERE id = 575;
select * from opportunities where team_id = 575;
SELECT * FROM teams WHERE name LIKE '%Integrum ESG%'; # 1126, 1065,
select * from opportunities where team_id = 1126;
SELECT * FROM teams WHERE name LIKE '%Base%'; # 1125, 1063,
select * from opportunities where team_id = 1125;
select * from contacts c
where c.team_id = 882;
SELECT * FROM activities WHERE id = 76822967;
SELECT * FROM crm_profiles WHERE user_id = 15440;
SELECT * FROM crm_profiles WHERE crm_configuration_id = 555;
SELECT * FROM crm_configurations WHERE id = 555;
SELECT * FROM users WHERE id = 15440; # team. 581, gr. 15440, pl. 3911, act. field 162182
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 581 and sa.provider = 'salesforce';
SELECT * FROM automated_report_results order by id desc;
select * from features;
select * from team_features where feature_id = 40;
select * from teams where id = 556;
select * from automated_reports;
where id = 54; # 4fdd41f6-dcf0-30d0-b339-7345381b6044 , ["pdf","podcast"]
SELECT * FROM automated_report_results WHERE uuid_to_bin('822fa41b-afd3-43a9-a248-86b0e36f3131') = uuid;
select * from automated_report_results order by id desc;
SELECT * FROM automated_report_results WHERE id = 1919;
select * from automated_report_results WHERE report_id = 54;
select * from opportunities where id = 7594349;
SELECT * FROM teams WHERE name LIKE '%Les%'; # 711, 692, 16067 - [EMAIL]
select * from playbooks where team_id = 711; # event 226147
SELECT * FROM playbook_categories WHERE playbook_id = 5515;
SELECT * FROM crm_fields WHERE crm_configuration_id = 692 and object_type = 'event';
SELECT * FROM crm_fields WHERE id = 226147;
SELECT * FROM crm_field_values WHERE crm_field_id = 226147;
SELECT * FROM crm_configurations WHERE id = 692;
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 711 and sa.provider = 'salesforce';
SELECT * FROM crm_profiles cp JOIN users u on u.id = cp.user_id WHERE u.team_id = 711;
select * from leads;
select * from calendars;
SELECT
t.id AS team_id,
t.name,
LOWER(SUBSTRING_INDEX(c.calendar_provider_id, '@', -1)) AS calendar_domain
FROM teams t
JOIN users u ON u.team_id = t.id
JOIN calendars c ON c.user_id = u.id AND c.status = 'active' AND c.calendar_provider_id LIKE '%@%'
LEFT JOIN team_domains td
ON td.team_id = t.id
AND td.deleted_at IS NULL
AND td.domain = LOWER(SUBSTRING_INDEX(c.calendar_provider_id, '@', -1))
GROUP BY t.id, t.name, calendar_domain
ORDER BY t.name, calendar_domain;
select * from users u join calendars c on c.user_id = u.id
where u.team_id = 882;
select * from activities where id = 74049485; # team 563 crm 537
select * from activities where id = 73272382; # team 563 crm 537
select * from activities where id = 64400389; # team 563 crm 537
select * from activities where id = 58081273; # team 563 crm 537
select * from activities where id = 54520297; # team 563 crm 537
select * from participants where activity_id = 58081273;
select * from activities where crm_configuration_id = 537 and provider = 'aircall'
and account_id = 19003658 order by updated_at desc;
select * from contacts where crm_configuration_id = 537 and id = 35957759;
select * from accounts where crm_configuration_id = 537 and id = 19003658;
select * from automated_report_results where id = 1976;
select * from automated_reports where id = 583;
select * from activity_searches where id = 87714;
select * from activity_search_filters where activity_search_id = 87714;
SELECT * FROM activities WHERE uuid_to_bin('8827f672-202d-4162-9d04-73ff5f0566a9') = uuid
or uuid_to_bin('47842446-af51-4bcb-854f-cc6560290101') = uuid;
SELECT * FROM crm_configurations WHERE provider = 'hubspot';
select * from rate_limits;
select * from automated_report_results where media_type = 'pdf' and status = 2
and id IN (18, 1872);
select * from automated_reports where id = 54;
SELECT * FROM users WHERE id IN (24623,29443,29613);
SELECT * FROM automated_report_results WHERE uuid_to_bin('822fa41b-afd3-43a9-a248-86b0e36f3131') = uuid;
select * from text_relays where created_at > '2026-05-01';
and id IN (32415, 32416);
# and id = 32412;
select * from users where team_id = 2 and email like '%scott%' and id = 29510;
SELECT * FROM activities WHERE uuid_to_bin('67cebfc2-ed56-44a2-8c68-7a0286ed8618') = uuid; # 79763436
SELECT email_provider_id, COUNT(*) as count, GROUP_CONCAT(id) as ids, GROUP_CONCAT(status) as statuses
FROM text_relays
WHERE email_provider_id IN ('19e2027868a64b42', '19e2033ed8ea6b10')
GROUP BY email_provider_id;
SELECT id, status, telephony_provider_id, created_at
FROM activities
WHERE id IN (80028719, 80028846);
SELECT id, status, code, email_sent_at, created_at, updated_at
FROM text_relays
WHERE id IN (32415, 32416);
SELECT id, status, code, sender, recipient, created_at
FROM text_relays
WHERE sender LIKE '%mario.georgiev%' OR sender LIKE '%stoyan.tomov%'
ORDER BY created_at DESC
LIMIT 10;
SELECT id, uuid, status, code, sender, recipient, created_at, updated_at
FROM text_relays
WHERE uuid = uuid_to_bin('0626141c-27a6-4d8c-aff8-7c8020a2c656');
# [PASSWORD_DOTS]
SELECT DISTINCT u.id, u.email, u.name, u.softphone_number, COUNT(a.id) as sms_count
FROM users u
INNER JOIN activities a ON u.id = .user_id
WHERE a.type LIKE 'sms%'
AND a.created_at > DATE_SUB(NOW(), INTERVAL 30 DAY)
GROUP BY u.id, u.email, u.name, u.softphone_number
ORDER BY sms_count DESC;
select * from teams where id = 1;
select * from roles;
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 1117 and sa.provider = 'hubspot';
SELECT * FROM activities WHERE uuid_to_bin('8024fffb-2df7-4017-91f4-d9f896050248') = uuid; # 79933459 YES
SELECT * FROM activities WHERE uuid_to_bin('[CREDIT_CARD]-927f-4f4da2a8185c') = uuid; # 80186192 NO
SELECT * FROM crm_configurations WHERE id = 1053;
SELECT * FROM teams WHERE id = 1117;
select * from users where id = 30249;
select * from playbooks where id = 5473;
select * from playbook_categories where id = 43783;
select * from playbook_categories where playbook_id = 5473;
select * from crm_fields where id = 659242;
select * from crm_field_values where crm_field_id = 659242;
SELECT * FROM crm_field_data fd
# JOIN crm_fields f ON fd.crm_field_id = f.id
# JOIN activities a ON fd.activity_id = a.id
WHERE activity_id = 79933459
# AND f.crm_provider_id = 'hs_activity_type';
SELECT * FROM activity_messages;
select * from text_relays where created_at > '2026-05-01';
select * from activities where user_id IN (7160, 18608) and created_at > '2026-05-22' order by id desc;
select * from users where team_id = 1 and id IN (18608, 13934, 7160);
select * from activities where user_id = 7160 order by id desc limit 10;
select * from accounts where team_id = 1 and name = 'Column5';
select * from users where name like '%Subra%'; # 31054, 1117
select * from teams where id = 1117;
select * from activity_searches where user_id = 31054;
select * from activity_search_filters where activity_search_id IN (88882, 88902);
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"#12121 on JY-20963-fix-import-on-deleted-entity, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.11569149,"height":0.025538707},"on_screen":true,"help_text":"Pull request #12121 exists for current branch JY-20963-fix-import-on-deleted-entity, but pull request details loading failed","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.85638297,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"ServiceTest","depth":6,"bounds":{"left":0.87167555,"top":0.019952115,"width":0.043882977,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'ServiceTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'ServiceTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"22","depth":4,"bounds":{"left":0.34042552,"top":0.12529927,"width":0.009973404,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"5","depth":4,"bounds":{"left":0.35239363,"top":0.12529927,"width":0.007978723,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.3620346,"top":0.123703115,"width":0.00731383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.3693484,"top":0.123703115,"width":0.006981383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Crm;\n\nuse Illuminate\\Support\\Collection;\nuse Jiminny\\Contracts\\Repositories\\TeamRepository;\nuse Jiminny\\Contracts\\Services\\Crm\\ServiceInterface;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Exceptions\\SocialAccountTokenInvalidException;\nuse Jiminny\\Models\\Account;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Contact;\nuse Jiminny\\Models\\Lead;\nuse Jiminny\\Models\\Opportunity;\nuse Jiminny\\Models\\Participant;\nuse Jiminny\\Models\\Stage;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\ResolveTeamCrmConnection;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Psr\\Log\\LoggerInterface;\nuse Exception;\nuse Throwable;\n\nclass CrmActivityService\n{\n public function __construct(\n private readonly TeamRepository $teamRepository,\n private readonly CachedCrmServiceDecorator $decorator,\n private readonly EmailHelper $emailHelper,\n private readonly ResolveTeamCrmConnection $teamCrmResolver,\n private readonly LoggerInterface $logger,\n ) {\n }\n\n /**\n * Updates CRM data for an activity and its participants.\n *\n * NOTE: This method performs multiple database writes and should be called\n * within a transaction by the caller to ensure atomicity.\n *\n * @param Activity $activity\n * @param bool $remoteSearch\n *\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception\n */\n public function updateCrmData(\n Activity $activity,\n bool $remoteSearch = false,\n ): void {\n $crmService = null;\n $participants = $activity->getParticipants();\n $team = $activity->getTeam();\n\n $prospectSearchStrategy = ProspectSearchStrategyFactory::match($team);\n if ($prospectSearchStrategy->ignoreCrmMatchData()) {\n $this->logger->info('[CrmActivityService] Ignoring crm data because of prospect strategy', [\n 'activity_id' => $activity->getId(),\n 'strategy' => get_class($prospectSearchStrategy),\n ]);\n\n return;\n }\n\n if ($remoteSearch) {\n try {\n $crmService = $this->teamCrmResolver->resolveForTeam($team);\n } catch (SocialAccountTokenInvalidException) {\n $this->logger->warning('[CrmActivityService] CRM token expired, falling back to local search', [\n 'activity_id' => $activity->getId(),\n 'team_id' => $team->getId(),\n ]);\n }\n }\n\n $records = $this->updateParticipantsCrmData(\n team: $team,\n activity: $activity,\n participants: $participants,\n crmService: $crmService,\n );\n\n if (! empty($records)) {\n $activity->updateActivityCrmData($records);\n }\n\n $activity->refresh();\n }\n\n /**\n * @param Collection<Participant> $participants\n *\n * @throws Exception\n *\n * @return array{\n * Lead|null,\n * Account|null,\n * Opportunity|null,\n * Contact|null,\n * Stage|null,\n * string|null\n *}|array{}\n */\n private function updateParticipantsCrmData(\n Team $team,\n Activity $activity,\n Collection $participants,\n ?ServiceInterface $crmService = null,\n ): array {\n $matchedRecords = [];\n $matchedDomainRecords = [];\n\n $this->validateCrmConfiguration($activity);\n $this->decorator->setConfiguration($activity->getCrm());\n $this->decorator->setCrmService($crmService);\n\n foreach ($participants as $participant) {\n if ($this->shouldSkipParticipant($participant)) {\n continue;\n }\n\n if (! $this->shouldPerformLookup($participant, $team)) {\n $this->logger->info('[CrmActivityService] Email domain belongs to the team, skipping crm lookup', [\n 'activity_id' => $activity->getId(),\n 'team_id' => $team->getId(),\n 'email' => $participant->getEmailAddress(),\n ]);\n\n $this->attachUserIfExists($participant, $team);\n\n continue;\n }\n\n $records = $this->findCrmRecords($participant, $activity);\n\n if (! empty($records)) {\n $matchedRecords[] = $records;\n } else {\n $records = $this->findCrmDomainRecords(\n crmService: $crmService,\n participant: $participant,\n activity: $activity,\n );\n if (! empty($records)) {\n $matchedDomainRecords[] = $records;\n }\n }\n\n if (empty($records)) {\n continue;\n }\n\n try {\n $activity->updateParticipantCrmData($records, $participant);\n } catch (Throwable $ex) {\n $this->logger->error('[CrmActivityService] Failed to update participant CRM data', [\n 'activity_id' => $activity->getId(),\n 'participant_id' => $participant->getId(),\n 'exception' => $ex->getMessage(),\n ]);\n\n continue;\n }\n }\n\n $bestMatch = $this->getBestMatch(\n matchedRecords : $matchedRecords,\n matchedDomainRecords: $matchedDomainRecords,\n );\n\n $this->logger->info('[CrmActivityService] CRM matching completed', [\n 'activity_id' => $activity->getId(),\n 'participants_processed' => $participants->count(),\n 'exact_matches' => count($matchedRecords),\n 'domain_matches' => count($matchedDomainRecords),\n 'best_match_found' => ! empty($bestMatch),\n ]);\n\n return $bestMatch;\n }\n\n private function shouldPerformLookup(Participant $participant, Team $team): bool\n {\n if ($participant->hasEmailAddress()) {\n return $this->emailHelper->shouldPerformLookup($team, $participant->getEmailAddress());\n }\n\n return true;\n }\n\n private function validateCrmConfiguration(Activity $activity): void\n {\n if ($activity->getCrm() === null) {\n throw new InvalidArgumentException('Cannot find CRM configuration');\n }\n }\n\n private function getBestMatch(?array $matchedRecords, ?array $matchedDomainRecords): array\n {\n return RecordSelector::pickBestFromLists($matchedRecords, $matchedDomainRecords);\n }\n\n private function findCrmRecords(Participant $participant, Activity $activity): ?array\n {\n $records = null;\n\n if ($participant->hasEmailAddress()) {\n $records = $this->decorator->matchExactlyByEmail(\n email: $participant->getEmailAddress(),\n userId: $activity->getUser()->getId()\n );\n }\n\n if (empty($records) && $participant->getPhoneNumber() !== null) {\n $records = $this->decorator->matchByPhone(\n phone: $participant->getPhoneNumber(),\n userId: $activity->getUser()->getId(),\n );\n }\n\n if (empty($records) && $participant->getName() !== null) {\n $records = $this->decorator->matchByName(\n name: $participant->getName(),\n userId: $activity->getUser()->getId(),\n );\n }\n\n return $records;\n }\n\n private function shouldSkipParticipant(Participant $participant): bool\n {\n return $participant->hasUser();\n }\n\n private function attachUserIfExists(Participant $participant, Team $team): void\n {\n if ($participant->hasEmailAddress() === false) {\n return;\n }\n\n $user = $this->teamRepository->findActiveTeamMemberByEmail($team, $participant->getEmailAddress());\n\n if ($user instanceof User) {\n $participant->user_id = $user->getId();\n $participant->save();\n }\n }\n\n private function findCrmDomainRecords(\n ?ServiceInterface $crmService,\n Participant $participant,\n Activity $activity,\n ): array {\n if ($participant->hasEmailAddress()) {\n $this->decorator->setConfiguration($activity->getCrm());\n $this->decorator->setCrmService($crmService);\n\n $records = $this->decorator->matchByDomain(\n email: $participant->getEmailAddress(),\n userId: $activity->getUser()->getId()\n );\n if (! empty($records)) {\n return $records;\n }\n }\n\n return [];\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Crm;\n\nuse Illuminate\\Support\\Collection;\nuse Jiminny\\Contracts\\Repositories\\TeamRepository;\nuse Jiminny\\Contracts\\Services\\Crm\\ServiceInterface;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Exceptions\\SocialAccountTokenInvalidException;\nuse Jiminny\\Models\\Account;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Contact;\nuse Jiminny\\Models\\Lead;\nuse Jiminny\\Models\\Opportunity;\nuse Jiminny\\Models\\Participant;\nuse Jiminny\\Models\\Stage;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\ResolveTeamCrmConnection;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Psr\\Log\\LoggerInterface;\nuse Exception;\nuse Throwable;\n\nclass CrmActivityService\n{\n public function __construct(\n private readonly TeamRepository $teamRepository,\n private readonly CachedCrmServiceDecorator $decorator,\n private readonly EmailHelper $emailHelper,\n private readonly ResolveTeamCrmConnection $teamCrmResolver,\n private readonly LoggerInterface $logger,\n ) {\n }\n\n /**\n * Updates CRM data for an activity and its participants.\n *\n * NOTE: This method performs multiple database writes and should be called\n * within a transaction by the caller to ensure atomicity.\n *\n * @param Activity $activity\n * @param bool $remoteSearch\n *\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception\n */\n public function updateCrmData(\n Activity $activity,\n bool $remoteSearch = false,\n ): void {\n $crmService = null;\n $participants = $activity->getParticipants();\n $team = $activity->getTeam();\n\n $prospectSearchStrategy = ProspectSearchStrategyFactory::match($team);\n if ($prospectSearchStrategy->ignoreCrmMatchData()) {\n $this->logger->info('[CrmActivityService] Ignoring crm data because of prospect strategy', [\n 'activity_id' => $activity->getId(),\n 'strategy' => get_class($prospectSearchStrategy),\n ]);\n\n return;\n }\n\n if ($remoteSearch) {\n try {\n $crmService = $this->teamCrmResolver->resolveForTeam($team);\n } catch (SocialAccountTokenInvalidException) {\n $this->logger->warning('[CrmActivityService] CRM token expired, falling back to local search', [\n 'activity_id' => $activity->getId(),\n 'team_id' => $team->getId(),\n ]);\n }\n }\n\n $records = $this->updateParticipantsCrmData(\n team: $team,\n activity: $activity,\n participants: $participants,\n crmService: $crmService,\n );\n\n if (! empty($records)) {\n $activity->updateActivityCrmData($records);\n }\n\n $activity->refresh();\n }\n\n /**\n * @param Collection<Participant> $participants\n *\n * @throws Exception\n *\n * @return array{\n * Lead|null,\n * Account|null,\n * Opportunity|null,\n * Contact|null,\n * Stage|null,\n * string|null\n *}|array{}\n */\n private function updateParticipantsCrmData(\n Team $team,\n Activity $activity,\n Collection $participants,\n ?ServiceInterface $crmService = null,\n ): array {\n $matchedRecords = [];\n $matchedDomainRecords = [];\n\n $this->validateCrmConfiguration($activity);\n $this->decorator->setConfiguration($activity->getCrm());\n $this->decorator->setCrmService($crmService);\n\n foreach ($participants as $participant) {\n if ($this->shouldSkipParticipant($participant)) {\n continue;\n }\n\n if (! $this->shouldPerformLookup($participant, $team)) {\n $this->logger->info('[CrmActivityService] Email domain belongs to the team, skipping crm lookup', [\n 'activity_id' => $activity->getId(),\n 'team_id' => $team->getId(),\n 'email' => $participant->getEmailAddress(),\n ]);\n\n $this->attachUserIfExists($participant, $team);\n\n continue;\n }\n\n $records = $this->findCrmRecords($participant, $activity);\n\n if (! empty($records)) {\n $matchedRecords[] = $records;\n } else {\n $records = $this->findCrmDomainRecords(\n crmService: $crmService,\n participant: $participant,\n activity: $activity,\n );\n if (! empty($records)) {\n $matchedDomainRecords[] = $records;\n }\n }\n\n if (empty($records)) {\n continue;\n }\n\n try {\n $activity->updateParticipantCrmData($records, $participant);\n } catch (Throwable $ex) {\n $this->logger->error('[CrmActivityService] Failed to update participant CRM data', [\n 'activity_id' => $activity->getId(),\n 'participant_id' => $participant->getId(),\n 'exception' => $ex->getMessage(),\n ]);\n\n continue;\n }\n }\n\n $bestMatch = $this->getBestMatch(\n matchedRecords : $matchedRecords,\n matchedDomainRecords: $matchedDomainRecords,\n );\n\n $this->logger->info('[CrmActivityService] CRM matching completed', [\n 'activity_id' => $activity->getId(),\n 'participants_processed' => $participants->count(),\n 'exact_matches' => count($matchedRecords),\n 'domain_matches' => count($matchedDomainRecords),\n 'best_match_found' => ! empty($bestMatch),\n ]);\n\n return $bestMatch;\n }\n\n private function shouldPerformLookup(Participant $participant, Team $team): bool\n {\n if ($participant->hasEmailAddress()) {\n return $this->emailHelper->shouldPerformLookup($team, $participant->getEmailAddress());\n }\n\n return true;\n }\n\n private function validateCrmConfiguration(Activity $activity): void\n {\n if ($activity->getCrm() === null) {\n throw new InvalidArgumentException('Cannot find CRM configuration');\n }\n }\n\n private function getBestMatch(?array $matchedRecords, ?array $matchedDomainRecords): array\n {\n return RecordSelector::pickBestFromLists($matchedRecords, $matchedDomainRecords);\n }\n\n private function findCrmRecords(Participant $participant, Activity $activity): ?array\n {\n $records = null;\n\n if ($participant->hasEmailAddress()) {\n $records = $this->decorator->matchExactlyByEmail(\n email: $participant->getEmailAddress(),\n userId: $activity->getUser()->getId()\n );\n }\n\n if (empty($records) && $participant->getPhoneNumber() !== null) {\n $records = $this->decorator->matchByPhone(\n phone: $participant->getPhoneNumber(),\n userId: $activity->getUser()->getId(),\n );\n }\n\n if (empty($records) && $participant->getName() !== null) {\n $records = $this->decorator->matchByName(\n name: $participant->getName(),\n userId: $activity->getUser()->getId(),\n );\n }\n\n return $records;\n }\n\n private function shouldSkipParticipant(Participant $participant): bool\n {\n return $participant->hasUser();\n }\n\n private function attachUserIfExists(Participant $participant, Team $team): void\n {\n if ($participant->hasEmailAddress() === false) {\n return;\n }\n\n $user = $this->teamRepository->findActiveTeamMemberByEmail($team, $participant->getEmailAddress());\n\n if ($user instanceof User) {\n $participant->user_id = $user->getId();\n $participant->save();\n }\n }\n\n private function findCrmDomainRecords(\n ?ServiceInterface $crmService,\n Participant $participant,\n Activity $activity,\n ): array {\n if ($participant->hasEmailAddress()) {\n $this->decorator->setConfiguration($activity->getCrm());\n $this->decorator->setCrmService($crmService);\n\n $records = $this->decorator->matchByDomain(\n email: $participant->getEmailAddress(),\n userId: $activity->getUser()->getId()\n );\n if (! empty($records)) {\n return $records;\n }\n }\n\n return [];\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Execute","depth":4,"bounds":{"left":0.37799203,"top":0.09896249,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Explain Plan","depth":4,"bounds":{"left":0.38663563,"top":0.09896249,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Browse Query History","depth":4,"bounds":{"left":0.39760637,"top":0.09896249,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"View Parameters","depth":4,"bounds":{"left":0.40625,"top":0.09896249,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Open Query Execution Settings…","depth":4,"bounds":{"left":0.41489363,"top":0.09896249,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"In-Editor Results","depth":4,"bounds":{"left":0.42586437,"top":0.09896249,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Tx: Auto","depth":4,"bounds":{"left":0.4368351,"top":0.09896249,"width":0.024268618,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Cancel Running Statements","depth":4,"bounds":{"left":0.46343085,"top":0.09896249,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Playground","depth":4,"bounds":{"left":0.4744016,"top":0.09896249,"width":0.029587766,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"jiminny","depth":4,"bounds":{"left":0.64261967,"top":0.09896249,"width":0.02825798,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"45","depth":4,"bounds":{"left":0.61269945,"top":0.123703115,"width":0.010305851,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"1","depth":4,"bounds":{"left":0.625,"top":0.123703115,"width":0.00731383,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"41","depth":4,"bounds":{"left":0.6343085,"top":0.123703115,"width":0.009640957,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"66","depth":4,"bounds":{"left":0.6459442,"top":0.123703115,"width":0.010305851,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.65791225,"top":0.12210695,"width":0.00731383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.66522604,"top":0.12210695,"width":0.006981383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"SELECT * FROM teams WHERE name LIKE '%litify%'; # 1069, 994, 24993\nSELECT * FROM users WHERE id = 25061;\nSELECT * FROM crm_profiles WHERE crm_configuration_id = 994;\nSELECT * FROM crm_profiles WHERE user_id = 25061;\n\nselect * from crm_configurations where id = 834;\nSELECT * FROM teams WHERE id = 882;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 882 and sa.provider = 'hubspot';\n\nSELECT * FROM crm_configurations where id = 882; # 933 - GoGlobal\nSELECT * FROM opportunities WHERE team_id = 933 order by updated_at desc;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 933 and sa.provider = 'hubspot';\n\nSELECT * FROM crm_configurations WHERE provider = 'hubspot' and crm_provider_id = 7270388;\n\nSELECT * FROM contacts where crm_configuration_id = 834;\nSELECT * FROM opportunities WHERE team_id = 933\n# AND crm_provider_id IN ('20131586060','46017317898','52543911090','53451356564','54101251892','54323768459');\nAND id IN (8482561,18352941,19042734,19232139,19445140,19472541);\nSELECT * FROM opportunity_contacts\nWHERE opportunity_id IN (8482561,18352941,19042734,19232139,19445140,19472541);\n\n# ********************************************************************\nSELECT * FROM crm_configurations where id = 485; #\nSELECT * FROM opportunities WHERE team_id = 933 order by updated_at desc;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 933 and sa.provider = 'hubspot';\n\nselect crm.provider, l.* from leads l join crm_configurations crm on l.crm_configuration_id = crm.id\nwhere crm.provider NOT IN ('salesforce', 'integration-app', 'bullhorn', 'copper')\n# and l.converted_at IS NOT NULL\n;\n\n# ********************************************************************\nSELECT * FROM activities a WHERE type IN ('email-inbound', 'email-outbound')\nand opportunity_id IS NULL\norder by id desc;\n\nSELECT * FROM teams WHERE id = 604; # 598\nSELECT * FROM activities WHERE id = 74410828; # chelseaw@allvoices.co\nSELECT * FROM accounts WHERE id = 20068382;\nSELECT * FROM accounts WHERE id = 35186038;\n\nSELECT * FROM contacts WHERE team_id = 852 and updated_at > '2026-01-23 12:30:00' order by updated_at desc;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 559 and sa.provider = 'hubspot';\n\nSELECT * FROM activities WHERE uuid_to_bin('cb6342b6-a183-401c-b0af-ede92b2ae763') = uuid;\nselect * from sidekick_settings where team_id = 781;\n\nSELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id = 26651871; # Teya\nSELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id = 7562435;\nSELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id = 8420347; # opflit 2100\n\nSELECT * FROM crm_layouts WHERE crm_configuration_id = 711;\nSELECT * FROM activities where crm_configuration_id = 711 and crm_provider_id IS NULL\nand is_internal = 0 and status = 'completed'\norder by id desc;\n\nSELECT * FROM crm_layout_entities\nWHERE crm_layout_id IN (2352, 2353);\n;\n\nSELECT * FROM crm_configurations where provider = 'hubspot' and id = 530;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 556 and sa.provider = 'hubspot';\n\nSELECT * FROM activities WHERE uuid_to_bin('c6ca4b22-7738-4563-a95d-b8a9598924ae') = uuid;\nSELECT * FROM activities WHERE uuid_to_bin('442abb2b-28bd-4be8-9c25-19e9bf02766d') = uuid;\nselect * from contacts\nwhere crm_configuration_id = 530\nand crm_provider_id = 872252;\n\nselect * from activities where crm_configuration_id = 530\nand user_id = 14343 and type like '%softphone%'\nand created_at between '2026-01-28 15:00:00' and '2026-01-28 15:10:00';\n\nSELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id = 25666868; # Teya\nSELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id = 8646335; # Teya\nSELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id IN (5933397);\n\n\nSELECT t.name, t.id, t.owner_id, c.id, c.provider, c.crm_base_url FROM teams t\nJOIN crm_configurations c ON t.id = c.team_id\nWHERE t.status = 'active';\n\nSELECT * FROM teams where id = 1091;\nSELECT * FROM crm_configurations where team_id = 1091;\nSELECT * FROM activity_providers where team_id = 1091;\nSELECT * FROM activities where crm_configuration_id = 1024 and type IN ('softphone', 'softphone-outbound')\nand provider NOT IN ('hubspot', 'aircall')\n# and telephony_provider_id = '019c1131-a22f-4792-b9ea-20adf6a02ed0'\norder by id desc;\n\n\nSELECT * FROM teams WHERE name LIKE '%Leadventure%';\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1091 and sa.provider = 'salesforce';\n\nSELECT * FROM teams WHERE name LIKE '%Wilson%'; # 862, 812\nSELECT * FROM teams where id = 862;\nSELECT * FROM crm_configurations where team_id = 862;\nSELECT * FROM activity_providers where team_id = 862;\nSELECT * FROM activities where crm_configuration_id = 812 and type IN ('softphone', 'softphone-outbound')\nand provider NOT IN ('hubspot', 'aircall')\n# and telephony_provider_id = '019c1131-a22f-4792-b9ea-20adf6a02ed0'\norder by id desc;\n\n\nSELECT t.id, crm.id, crm.provider, ap.* FROM teams t\njoin crm_configurations crm on t.id = crm.team_id\njoin activity_providers ap on t.id = ap.team_id\nwhere t.status = 'active' and ap.is_enabled = 1\nand crm.provider = 'hubspot'\nand ap.provider NOT IN ('hubspot', 'aircall', 'uploader', 'gong', 'twilio', 'zoom-bot', 'google-meet', 'ms-teams',\n 'outreach', 'close', 'ringcentral', 'dialpad', 'zoom-phone');\n\nSELECT * FROM teams where id = 1068;\nSELECT * FROM crm_configurations where team_id = 1068;\nSELECT * FROM activity_providers where team_id = 1068;\n\nSELECT * FROM activities a\nwhere crm_configuration_id = 993 and type IN ('softphone', 'softphone-outbound')\nand a.provider NOT IN ('hubspot', 'uploader', 'gong', 'twilio', 'google-meet', 'ms-teams','close'\n )\n# and telephony_provider_id = '019c1131-a22f-4792-b9ea-20adf6a02ed0'\norder by a.id desc;\n\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1068 and sa.provider = 'hubspot';\n\n# ********************************************************************\n# ********************************************************************\n# ********************************************************************\nSELECT * FROM crm_configurations where id = 882; # 933 - GoGlobal , portalId: 6017093\nSELECT * FROM opportunities WHERE team_id = 933 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 933 and updated_at > '2026-02-06 00:00:00' order by updated_at desc;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 933 and sa.provider = 'hubspot';\n\n# ********************************************************************\nSELECT * FROM crm_configurations where id = 834; # 882 - AnyVan , portalId: 5468262\nSELECT * FROM contacts WHERE crm_configuration_id = 834 and updated_at > '2026-03-30 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE crm_configuration_id = 834 and updated_at > '2026-03-04 08:00:00' order by updated_at desc;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 882 and sa.provider = 'hubspot';\nselect * from crm_layouts where crm_configuration_id = 834;\nselect * from crm_layout_entities where crm_layout_id = 2780;\nselect * from crm_fields where id IN (321153,321192,321193,321194);\n\nSELECT * FROM opportunities WHERE crm_configuration_id = 834 and id = 10993426;\n# ********************************************************************\nSELECT * FROM crm_configurations where id = 988; # 1057 - Teya (543ce4f4-168c-4571-91ea-5b35c253f06f) , portalId: 26651871\nSELECT * FROM opportunities WHERE team_id = 1057 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 1057 and updated_at > '2026-02-04 00:00:00' order by updated_at desc;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1057 and sa.provider = 'hubspot';\n\nSELECT * FROM crm_configurations where id = 533; # 559 - Connectd , portalId: 6710988\nSELECT * FROM opportunities WHERE team_id = 559 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 559 and updated_at > '2026-02-04 00:00:00' order by updated_at desc;\n\nSELECT * FROM crm_configurations where id = 801; # 852 - Rise Vision , portalId: 2700250\nSELECT * FROM opportunities WHERE team_id = 852 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 852 and updated_at > '2026-02-04 00:00:00' order by updated_at desc; # 6th last\n\nSELECT * FROM crm_configurations where id = 962; # 1034 - evergrowth.io , portalId: 143180990\nSELECT * FROM opportunities WHERE team_id = 1034 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 1034 and updated_at > '2026-02-04 00:00:00' order by updated_at desc;\n\n# ********************************************************************\nSELECT * FROM crm_configurations where id = 1037; # 1102 - Jibble , portalId: 6649755\nSELECT * FROM opportunities WHERE team_id = 1102 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 1102 and updated_at > '2026-02-04 12:30:00' order by updated_at desc; # 8\n\nSELECT * FROM crm_configurations where id = 1015; # 1049 - Travefy , portalId: 48904401\nSELECT * FROM opportunities WHERE team_id = 1049 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 1049 and updated_at > '2026-02-04 12:30:00' order by updated_at desc; # 20\n\nSELECT * FROM crm_configurations where id = 64; # 70 - SalaryFinance , portalId: 3404115\nSELECT * FROM opportunities WHERE team_id = 70 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 70 and updated_at > '2026-02-04 12:30:00' order by updated_at desc; # 6th last\n\nSELECT * FROM crm_configurations where id = 802; # 853 - Street Group , portalId: 7658438\nSELECT * FROM opportunities WHERE team_id = 853 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 853 and updated_at > '2026-02-04 12:30:00' order by updated_at desc; # 10\n\nSELECT * FROM crm_configurations where id = 872; # 921 - In Professional Development , portalId: 9238273\nSELECT * FROM opportunities WHERE team_id = 921 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 921 and updated_at > '2026-02-04 12:30:00' order by updated_at desc; # 2\n\n# ********************************************************************\nSELECT * FROM crm_configurations where id = 550; # 576 - SeedLegals , portalId: 3028661\nSELECT * FROM opportunities WHERE team_id = 576 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 576 and updated_at > '2026-02-05 14:00:00' order by updated_at desc;\n\nSELECT * FROM crm_configurations where id = 989; # 1058 - rtaoutdoor.com , portalId: 22371204\nSELECT * FROM opportunities WHERE team_id = 1058 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 1058 and updated_at > '2026-02-05 14:00:00' order by updated_at desc;\n\nSELECT * FROM crm_configurations where id = 896; # 946 - Mintago , portalId: 6621281\nSELECT * FROM opportunities WHERE team_id = 946 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 946 and updated_at > '2026-02-05 14:00:00' order by updated_at desc;\n\nSELECT * FROM crm_configurations where id = 617; # 641 - PCS , portalId: 5244937\nSELECT * FROM opportunities WHERE team_id = 641 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 641 and updated_at > '2026-02-05 14:00:00' order by updated_at desc; # 7th\n# ********************************************************************\nSELECT * FROM crm_configurations where id = 649; # 670 - Eventeny , portalId: 4492849\nSELECT * FROM opportunities WHERE team_id = 670 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 670 and updated_at > '2026-02-09 08:00:00' order by updated_at desc; #\n\nSELECT * FROM crm_configurations where id = 48; # 51 - CleanCloud , portalId: 4373137\nSELECT * FROM opportunities WHERE team_id = 51 and updated_at > '2026-03-04 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 51 and updated_at > '2026-02-09 08:00:00' order by updated_at desc;\nselect * from users where team_id = 51; # 7783\nSELECT * FROM groups WHERE uuid_to_bin('8a8d2cb6-8b55-4fa3-8b5c-5f0e3d8de59a') = uuid; # 1130\nselect * from activity_searches where user_id = 7783;\nselect * from activity_search_filters where activity_search_id IN (32291, 32292);\n\nSELECT asf.activity_search_id, asf.id, asf.value\nFROM activity_search_filters asf\nWHERE asf.filter = 'group_id'\nAND asf.value IN (\n SELECT CONCAT(\n HEX(SUBSTR(uuid, 5, 4)), '-',\n HEX(SUBSTR(uuid, 3, 2)), '-',\n HEX(SUBSTR(uuid, 1, 2)), '-',\n HEX(SUBSTR(uuid, 9, 2)), '-',\n HEX(SUBSTR(uuid, 11))\n )\n FROM groups\n WHERE deleted_at IS NOT NULL\n);\n\nSELECT * FROM crm_configurations where id = 272; # 290 - Bonham & Brook , portalId: 5705856\nSELECT * FROM opportunities WHERE team_id = 290 and updated_at > '2026-02-05 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 290 and updated_at > '2026-02-09 08:00:00' order by updated_at desc; # 6th\n# ********************************************************************\nSELECT * FROM crm_configurations where provider = 'hubspot';\nSELECT * FROM crm_configurations where id = 1056; # 1119 - Chromatic , portalId: 45602133\nSELECT * FROM opportunities WHERE team_id = 1119 and remotely_created_at > '2026-02-01 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 1119 and updated_at > '2026-02-09 09:00:00' order by updated_at desc; # null\n# ********************************************************************\n\nselect * from contacts where crm_provider_id = '003Uu00000ojD4NIAU';\nselect\n cp.*\n# DISTINCT t.id\n# cp.id, cp.user_id, t.id, cp.crm_configuration_id, cp.contact_fields\nFROM crm_profiles cp\nJOIN crm_configurations crm on crm.id = cp.crm_configuration_id\nJOIN users u on u.id = cp.user_id\nJOIN teams t ON t.id = crm.team_id\nWHERE crm.provider = 'salesforce' and t.status = 'active'\n and cp.archived_at IS NULL and u.deleted_at IS NULL\n and t.id NOT IN (1093)\n and t.id = 2\n and cp.contact_fields IS NULL;\n# and c.crm_provider_id = '003Uu00000ojD4NIAU';\n\nSELECT * FROM users WHERE id = 26484;\nSELECT * FROM crm_profiles WHERE user_id = 26484;\nSELECT * FROM social_accounts WHERE sociable_id = 26484;\nSELECT * FROM crm_configurations where provider = 'salesforce';\nselect * from users where id IN (10022, 10403);\nselect * from users where team_id IN (526);\nselect * from teams where id IN (526, 532);\nselect * from crm_configurations where id IN (500, 516);\nselect * from crm_profiles where crm_configuration_id IN (500, 516) and user_id IN (10022, 10403);\nselect * from contacts where crm_configuration_id IN (500, 516) and crm_provider_id = '003Uu00000ojD4NIAU';\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 526 and sa.provider = 'salesforce';\nselect * from team_settings where team_id IN (526, 532);\n\nselect * from users where id IN (22824);\nselect * from crm_profiles where crm_configuration_id IN (1026);\n\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1093 and sa.provider = 'salesforce';\n\nselect * from teams where id = 1099;\nselect * from users where id = 29643\n\nselect * from activity_processing_states;\n\nSELECT * FROM teams where name LIKE '%Fare%'; # 233\nSELECT * FROM opportunities where crm_configuration_id = 215\n# and crm_provider_id = 'oppo_ogESZf2P50nDrd1nGPvKDXeA6sSaTN5v51Lp4ayVzKR'\n;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1088 and sa.provider = 'hubspot';\n\nSELECT * FROM teams order by updated_at DESC\nSELECT * FROM crm_configurations WHERE id = 1019; # SimpleConsign 1088 - no social account\n\nselect * from crm_configurations where provider = 'pipedrive';\n\nselect * from teams where id = 957;\nselect * from crm_configurations where id = 957;\n\nSELECT * FROM teams WHERE name LIKE '%Prolific%'; # 544, 518, 10743\nSELECT * FROM opportunities where crm_configuration_id = 518 order by id desc;\n\nselect * from users where team_id = 1; # 26726 - Gabriela Dureva\nSELECT * FROM opportunities where user_id = 26726; # 16834447 - Prolific\nselect * from activities where user_id = 26726 order by id desc;\nselect * from contacts where crm_configuration_id = 1\nand email IN ('charlotte.ward@prolific.com', 'frankie.bryant@prolific.com'); # 2094416, 2093620\nSELECT * FROM contacts WHERE id = 6284931;\n\nSELECT p.* FROM activities a JOIN participants p ON a.id = p.activity_id\nWHERE a.user_id = 26726 and p.lead_id IN (2094416, 2093620) and a.created_at > '2026-01-01 00:00:00' order by p.email;\n\nselect * from activities where id IN (75509259,75509261,75509261,75511034,75026464,75517602,75517605);\nselect * from crm_configurations where id = 1;\n\n43801692-1aeb-32ce-acba-5b80a479701a\n44c3c9cf-6f5e-75f3-8179-bc9f75dd2b1b\n405975c0-b3d0-7aaa-821f-09d59cae6dd1\n4caf848d-4bed-2299-b248-7788d41f9fca\n49bedc3f-f196-eef3-89c3-dea6a3b4aa63\n43420989-a09d-b8f8-9806-c8bbf7a02aac\n\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1 and sa.provider = 'salesforce';\n\nSELECT * FROM activities WHERE id = 75461988;\n\nSELECT * FROM activities WHERE uuid_to_bin('d6c5052e-e972-49e9-8912-26f2f7d6c5f6') = uuid;\n\nselect * from contacts where id = 17900517;\n\nselect * from contact_roles cr join crm_configurations crm on cr.crm_configuration_id = crm.id\nwhere crm.provider != 'salesforce';\n\nselect * from users where id = 21047;\nSELECT * FROM crm_configurations WHERE id = 892;\nSELECT * FROM teams WHERE id = 942;\nselect * from opportunities where team_id = 942 order by updated_at desc;\nselect * from contacts where team_id = 942 order by updated_at desc;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 942 and sa.provider = 'hubspot';\n\nSELECT * FROM opportunities where team_id = 1 and crm_provider_id IN ('006Pq00000NeH6XIAV', '006Pq000007z8kdIAA'); # 10697889, 6621430\nSELECT * FROM crm_configurations WHERE id = 1;\nSELECT * FROM teams WHERE crm_id = 1;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1 and sa.provider = 'salesforce';\n\nselect id, user_id, opportunity_fields from crm_profiles where crm_configuration_id = 1\nSELECT * FROM opportunities where team_id = 1 order by updated_at desc; # 10697889, 6621430\n\nselect * from teams where id = 852;\nselect * from groups where id = 2286;\nselect * from sidekick_settings where team_id = 852;\nselect * from default_activity_types where team_id = 852;\n\n\nSELECT cc.provider, cc.id, p.id, u.*\nFROM users u\nLEFT JOIN crm_profiles p ON u.id = p.user_id AND p.id IS NULL -- no profile\nINNER JOIN teams t ON u.team_id = t.id AND t.status = 'active' -- team is active\nINNER JOIN crm_configurations cc ON t.crm_id = cc.id\nWHERE u.status = 1 AND u.deleted_at IS NULL\nAND u.crm_required = 1\nAND u.team_id = 1\nORDER BY u.team_id;\n\nSELECT * FROM crm_profiles cp where cp.crm_configuration_id = 1 and cp.user_id IN (\n18481\n );\n\nSELECT cc.provider, cc.id, p.id, u.*\nFROM users u\nLEFT JOIN crm_profiles p ON u.id = p.user_id\nINNER JOIN teams t ON u.team_id = t.id AND t.status = 'active'\nINNER JOIN crm_configurations cc ON t.crm_id = cc.id\nWHERE u.status = 1\n AND u.deleted_at IS NULL\n AND u.crm_required = 1\n# AND u.team_id = 1\n AND p.id IS NULL -- Move this condition to WHERE clause\nORDER BY u.team_id;\n\nSELECT * FROM opportunities WHERE id = 20002609;\nselect * from teams where id = 1122; # Velatir, 29953 - christian@velatir.com\nselect * from crm_configurations where id = 1060;\nselect * from crm_layouts where crm_configuration_id = 1060;\nSELECT * FROM crm_layout_entities WHERE crm_layout_id = 3596;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1122 and sa.provider = 'hubspot';\nselect * from opportunities where team_id = 1122 order by updated_at desc;\n\nselect * from crm_field_data where object_type = 'contact';\n\nSELECT * FROM activities WHERE uuid_to_bin('374fc8ed-3315-4c9f-9b25-318b7fd2928f') = uuid; # 76584262\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 248 and sa.provider = 'salesforce';\n\nSELECT * FROM crm_profiles where user_id = 24115; # 005QF000002CswMYAS\nSELECT * FROM users where id = 24115;\nSELECT * FROM accounts where id = 4002896;\nSELECT * FROM teams WHERE name LIKE '%adswerve%';\nSELECT * FROM opportunities where crm_configuration_id = 230 AND crm_provider_id IN (\"0069N000003GIQ9QAO\",\"0061r000019yGP9AAM\",\"0066900001S2KWlAAN\",\"0066900001TDpj2AAD\",\"0066900001b8uEwAAI\",\"0069N000001rQi0QAE\",\"006QF00000KD40mYAD\",\"006QF00000LzpRJYAZ\",\"0069N000002uomtQAA\",\"0069N000002xlMLQAY\",\"0066900001NV6ubAAD\",\"0061r00001HJp45AAD\",\"006QF00000uTlUoYAK\",\"006QF00000v0bZqYAI\");\nSELECT * FROM opportunities WHERE crm_configuration_id = 230 AND crm_provider_id = '0069N000003GIQ9QAO'; # 6272203\n\nSELECT u.id, u.email, ac.name, a.* FROM activities a\nJOIN users u ON a.user_id = u.id\nJOIN accounts ac ON a.account_id = ac.id\nWHERE\nuuid_to_bin('e3269598-b562-44fb-b5e9-9d2694dc63e0') = a.uuid or\nuuid_to_bin('66ddc3ab-4e15-45aa-af0c-248c1eece593') = a.uuid or\nuuid_to_bin('826bd328-e1cc-4213-b8d8-572454cacc07') = a.uuid;\n\nselect * from users where id = 5825;\nSELECT * FROM activities WHERE uuid_to_bin('e56aa2e8-231a-421b-ab1f-cb38ed2bf573') = uuid;\n\nselect * from activities where uuid_to_bin('91e13b2f-2d1b-45f8-b1fd-1141b6563782') = uuid;\n19594, 862\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 862 and sa.provider = 'salesforce';\n\nselect * from automated_reports where id = 36;\nselect ar.frequency, r.*, ar.* from automated_report_results r\njoin automated_reports ar on r.report_id = ar.id\nwhere ar.frequency != 'one_off';\n\nselect s.* from activity_searches s join users u ON s.user_id = u.id where u.team_id = 882;\nselect * from nudges n where n.activity_search_id\n\nselect * from teams where created_at > '2026-03-09';\nSELECT * FROM crm_layouts WHERE crm_configuration_id = 1065; # 1065\nSELECT * FROM crm_layout_entities WHERE crm_layout_id = 3617;\n\nselect * from users where team_id = 1 and name like '%Lukas%'; # 7160\n\nSELECT * FROM teams WHERE id = 575;\nselect * from opportunities where team_id = 575;\nSELECT * FROM teams WHERE name LIKE '%Integrum ESG%'; # 1126, 1065,\nselect * from opportunities where team_id = 1126;\nSELECT * FROM teams WHERE name LIKE '%Base%'; # 1125, 1063,\nselect * from opportunities where team_id = 1125;\nselect * from contacts c\nwhere c.team_id = 882;\n\nSELECT * FROM activities WHERE id = 76822967;\nSELECT * FROM crm_profiles WHERE user_id = 15440;\nSELECT * FROM crm_profiles WHERE crm_configuration_id = 555;\nSELECT * FROM crm_configurations WHERE id = 555;\nSELECT * FROM users WHERE id = 15440; # team. 581, gr. 15440, pl. 3911, act. field 162182\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 581 and sa.provider = 'salesforce';\n\nSELECT * FROM automated_report_results order by id desc;\n\nselect * from features;\nselect * from team_features where feature_id = 40;\n\nselect * from teams where id = 556;\n\nselect * from automated_reports;\nwhere id = 54; # 4fdd41f6-dcf0-30d0-b339-7345381b6044 , [\"pdf\",\"podcast\"]\nSELECT * FROM automated_report_results WHERE uuid_to_bin('822fa41b-afd3-43a9-a248-86b0e36f3131') = uuid;\nselect * from automated_report_results order by id desc;\nSELECT * FROM automated_report_results WHERE id = 1919;\n\nselect * from automated_report_results WHERE report_id = 54;\n\nselect * from opportunities where id = 7594349;\n\nSELECT * FROM teams WHERE name LIKE '%Les%'; # 711, 692, 16067 - jiminnyintegration@lesmills.com\nselect * from playbooks where team_id = 711; # event 226147\nSELECT * FROM playbook_categories WHERE playbook_id = 5515;\nSELECT * FROM crm_fields WHERE crm_configuration_id = 692 and object_type = 'event';\nSELECT * FROM crm_fields WHERE id = 226147;\nSELECT * FROM crm_field_values WHERE crm_field_id = 226147;\n\nSELECT * FROM crm_configurations WHERE id = 692;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 711 and sa.provider = 'salesforce';\n\nSELECT * FROM crm_profiles cp JOIN users u on u.id = cp.user_id WHERE u.team_id = 711;\n\nselect * from leads;\n\nselect * from calendars;\n\nSELECT\n t.id AS team_id,\n t.name,\n LOWER(SUBSTRING_INDEX(c.calendar_provider_id, '@', -1)) AS calendar_domain\nFROM teams t\nJOIN users u ON u.team_id = t.id\nJOIN calendars c ON c.user_id = u.id AND c.status = 'active' AND c.calendar_provider_id LIKE '%@%'\nLEFT JOIN team_domains td\n ON td.team_id = t.id\n AND td.deleted_at IS NULL\n AND td.domain = LOWER(SUBSTRING_INDEX(c.calendar_provider_id, '@', -1))\nGROUP BY t.id, t.name, calendar_domain\nORDER BY t.name, calendar_domain;\n\nselect * from users u join calendars c on c.user_id = u.id\nwhere u.team_id = 882;\n\n\nselect * from activities where id = 74049485; # team 563 crm 537\nselect * from activities where id = 73272382; # team 563 crm 537\nselect * from activities where id = 64400389; # team 563 crm 537\nselect * from activities where id = 58081273; # team 563 crm 537\nselect * from activities where id = 54520297; # team 563 crm 537\nselect * from participants where activity_id = 58081273;\n\nselect * from activities where crm_configuration_id = 537 and provider = 'aircall'\nand account_id = 19003658 order by updated_at desc;\n\nselect * from contacts where crm_configuration_id = 537 and id = 35957759;\nselect * from accounts where crm_configuration_id = 537 and id = 19003658;\n\nselect * from automated_report_results where id = 1976;\nselect * from automated_reports where id = 583;\nselect * from activity_searches where id = 87714;\nselect * from activity_search_filters where activity_search_id = 87714;\n\nSELECT * FROM activities WHERE uuid_to_bin('8827f672-202d-4162-9d04-73ff5f0566a9') = uuid\nor uuid_to_bin('47842446-af51-4bcb-854f-cc6560290101') = uuid;\n\nSELECT * FROM crm_configurations WHERE provider = 'hubspot';\nselect * from rate_limits;\n\nselect * from automated_report_results where media_type = 'pdf' and status = 2\nand id IN (18, 1872);\nselect * from automated_reports where id = 54;\nSELECT * FROM users WHERE id IN (24623,29443,29613);\n\nSELECT * FROM automated_report_results WHERE uuid_to_bin('822fa41b-afd3-43a9-a248-86b0e36f3131') = uuid;\n\nselect * from text_relays where created_at > '2026-05-01';\nand id IN (32415, 32416);\n# and id = 32412;\n\nselect * from users where team_id = 2 and email like '%scott%' and id = 29510;\n\nSELECT * FROM activities WHERE uuid_to_bin('67cebfc2-ed56-44a2-8c68-7a0286ed8618') = uuid; # 79763436\n\nSELECT email_provider_id, COUNT(*) as count, GROUP_CONCAT(id) as ids, GROUP_CONCAT(status) as statuses\nFROM text_relays\nWHERE email_provider_id IN ('19e2027868a64b42', '19e2033ed8ea6b10')\nGROUP BY email_provider_id;\nSELECT id, status, telephony_provider_id, created_at\nFROM activities\nWHERE id IN (80028719, 80028846);\nSELECT id, status, code, email_sent_at, created_at, updated_at\nFROM text_relays\nWHERE id IN (32415, 32416);\nSELECT id, status, code, sender, recipient, created_at\nFROM text_relays\nWHERE sender LIKE '%mario.georgiev%' OR sender LIKE '%stoyan.tomov%'\nORDER BY created_at DESC\nLIMIT 10;\n\nSELECT id, uuid, status, code, sender, recipient, created_at, updated_at\nFROM text_relays\nWHERE uuid = uuid_to_bin('0626141c-27a6-4d8c-aff8-7c8020a2c656');\n\n# ***************\nSELECT DISTINCT u.id, u.email, u.name, u.softphone_number, COUNT(a.id) as sms_count\nFROM users u\nINNER JOIN activities a ON u.id = .user_id\nWHERE a.type LIKE 'sms%'\nAND a.created_at > DATE_SUB(NOW(), INTERVAL 30 DAY)\nGROUP BY u.id, u.email, u.name, u.softphone_number\nORDER BY sms_count DESC;\n\nselect * from teams where id = 1;\n\nselect * from roles;\n\n\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1117 and sa.provider = 'hubspot';\nSELECT * FROM activities WHERE uuid_to_bin('8024fffb-2df7-4017-91f4-d9f896050248') = uuid; # 79933459 YES\nSELECT * FROM activities WHERE uuid_to_bin('25529043-8094-4781-927f-4f4da2a8185c') = uuid; # 80186192 NO\nSELECT * FROM crm_configurations WHERE id = 1053;\nSELECT * FROM teams WHERE id = 1117;\nselect * from users where id = 30249;\nselect * from playbooks where id = 5473;\nselect * from playbook_categories where id = 43783;\nselect * from playbook_categories where playbook_id = 5473;\nselect * from crm_fields where id = 659242;\nselect * from crm_field_values where crm_field_id = 659242;\n\nSELECT * FROM crm_field_data fd\n# JOIN crm_fields f ON fd.crm_field_id = f.id\n# JOIN activities a ON fd.activity_id = a.id\nWHERE activity_id = 79933459\n# AND f.crm_provider_id = 'hs_activity_type';\n\n\nSELECT * FROM activity_messages;\nselect * from text_relays where created_at > '2026-05-01';\nselect * from activities where user_id IN (7160, 18608) and created_at > '2026-05-22' order by id desc;\nselect * from users where team_id = 1 and id IN (18608, 13934, 7160);\nselect * from activities where user_id = 7160 order by id desc limit 10;\n\nselect * from accounts where team_id = 1 and name = 'Column5';\n\nselect * from users where name like '%Subra%'; # 31054, 1117\nselect * from teams where id = 1117;\nselect * from activity_searches where user_id = 31054;\nselect * from activity_search_filters where activity_search_id IN (88882, 88902);","depth":4,"on_screen":true,"value":"SELECT * FROM teams WHERE name LIKE '%litify%'; # 1069, 994, 24993\nSELECT * FROM users WHERE id = 25061;\nSELECT * FROM crm_profiles WHERE crm_configuration_id = 994;\nSELECT * FROM crm_profiles WHERE user_id = 25061;\n\nselect * from crm_configurations where id = 834;\nSELECT * FROM teams WHERE id = 882;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 882 and sa.provider = 'hubspot';\n\nSELECT * FROM crm_configurations where id = 882; # 933 - GoGlobal\nSELECT * FROM opportunities WHERE team_id = 933 order by updated_at desc;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 933 and sa.provider = 'hubspot';\n\nSELECT * FROM crm_configurations WHERE provider = 'hubspot' and crm_provider_id = 7270388;\n\nSELECT * FROM contacts where crm_configuration_id = 834;\nSELECT * FROM opportunities WHERE team_id = 933\n# AND crm_provider_id IN ('20131586060','46017317898','52543911090','53451356564','54101251892','54323768459');\nAND id IN (8482561,18352941,19042734,19232139,19445140,19472541);\nSELECT * FROM opportunity_contacts\nWHERE opportunity_id IN (8482561,18352941,19042734,19232139,19445140,19472541);\n\n# ********************************************************************\nSELECT * FROM crm_configurations where id = 485; #\nSELECT * FROM opportunities WHERE team_id = 933 order by updated_at desc;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 933 and sa.provider = 'hubspot';\n\nselect crm.provider, l.* from leads l join crm_configurations crm on l.crm_configuration_id = crm.id\nwhere crm.provider NOT IN ('salesforce', 'integration-app', 'bullhorn', 'copper')\n# and l.converted_at IS NOT NULL\n;\n\n# ********************************************************************\nSELECT * FROM activities a WHERE type IN ('email-inbound', 'email-outbound')\nand opportunity_id IS NULL\norder by id desc;\n\nSELECT * FROM teams WHERE id = 604; # 598\nSELECT * FROM activities WHERE id = 74410828; # chelseaw@allvoices.co\nSELECT * FROM accounts WHERE id = 20068382;\nSELECT * FROM accounts WHERE id = 35186038;\n\nSELECT * FROM contacts WHERE team_id = 852 and updated_at > '2026-01-23 12:30:00' order by updated_at desc;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 559 and sa.provider = 'hubspot';\n\nSELECT * FROM activities WHERE uuid_to_bin('cb6342b6-a183-401c-b0af-ede92b2ae763') = uuid;\nselect * from sidekick_settings where team_id = 781;\n\nSELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id = 26651871; # Teya\nSELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id = 7562435;\nSELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id = 8420347; # opflit 2100\n\nSELECT * FROM crm_layouts WHERE crm_configuration_id = 711;\nSELECT * FROM activities where crm_configuration_id = 711 and crm_provider_id IS NULL\nand is_internal = 0 and status = 'completed'\norder by id desc;\n\nSELECT * FROM crm_layout_entities\nWHERE crm_layout_id IN (2352, 2353);\n;\n\nSELECT * FROM crm_configurations where provider = 'hubspot' and id = 530;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 556 and sa.provider = 'hubspot';\n\nSELECT * FROM activities WHERE uuid_to_bin('c6ca4b22-7738-4563-a95d-b8a9598924ae') = uuid;\nSELECT * FROM activities WHERE uuid_to_bin('442abb2b-28bd-4be8-9c25-19e9bf02766d') = uuid;\nselect * from contacts\nwhere crm_configuration_id = 530\nand crm_provider_id = 872252;\n\nselect * from activities where crm_configuration_id = 530\nand user_id = 14343 and type like '%softphone%'\nand created_at between '2026-01-28 15:00:00' and '2026-01-28 15:10:00';\n\nSELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id = 25666868; # Teya\nSELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id = 8646335; # Teya\nSELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id IN (5933397);\n\n\nSELECT t.name, t.id, t.owner_id, c.id, c.provider, c.crm_base_url FROM teams t\nJOIN crm_configurations c ON t.id = c.team_id\nWHERE t.status = 'active';\n\nSELECT * FROM teams where id = 1091;\nSELECT * FROM crm_configurations where team_id = 1091;\nSELECT * FROM activity_providers where team_id = 1091;\nSELECT * FROM activities where crm_configuration_id = 1024 and type IN ('softphone', 'softphone-outbound')\nand provider NOT IN ('hubspot', 'aircall')\n# and telephony_provider_id = '019c1131-a22f-4792-b9ea-20adf6a02ed0'\norder by id desc;\n\n\nSELECT * FROM teams WHERE name LIKE '%Leadventure%';\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1091 and sa.provider = 'salesforce';\n\nSELECT * FROM teams WHERE name LIKE '%Wilson%'; # 862, 812\nSELECT * FROM teams where id = 862;\nSELECT * FROM crm_configurations where team_id = 862;\nSELECT * FROM activity_providers where team_id = 862;\nSELECT * FROM activities where crm_configuration_id = 812 and type IN ('softphone', 'softphone-outbound')\nand provider NOT IN ('hubspot', 'aircall')\n# and telephony_provider_id = '019c1131-a22f-4792-b9ea-20adf6a02ed0'\norder by id desc;\n\n\nSELECT t.id, crm.id, crm.provider, ap.* FROM teams t\njoin crm_configurations crm on t.id = crm.team_id\njoin activity_providers ap on t.id = ap.team_id\nwhere t.status = 'active' and ap.is_enabled = 1\nand crm.provider = 'hubspot'\nand ap.provider NOT IN ('hubspot', 'aircall', 'uploader', 'gong', 'twilio', 'zoom-bot', 'google-meet', 'ms-teams',\n 'outreach', 'close', 'ringcentral', 'dialpad', 'zoom-phone');\n\nSELECT * FROM teams where id = 1068;\nSELECT * FROM crm_configurations where team_id = 1068;\nSELECT * FROM activity_providers where team_id = 1068;\n\nSELECT * FROM activities a\nwhere crm_configuration_id = 993 and type IN ('softphone', 'softphone-outbound')\nand a.provider NOT IN ('hubspot', 'uploader', 'gong', 'twilio', 'google-meet', 'ms-teams','close'\n )\n# and telephony_provider_id = '019c1131-a22f-4792-b9ea-20adf6a02ed0'\norder by a.id desc;\n\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1068 and sa.provider = 'hubspot';\n\n# ********************************************************************\n# ********************************************************************\n# ********************************************************************\nSELECT * FROM crm_configurations where id = 882; # 933 - GoGlobal , portalId: 6017093\nSELECT * FROM opportunities WHERE team_id = 933 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 933 and updated_at > '2026-02-06 00:00:00' order by updated_at desc;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 933 and sa.provider = 'hubspot';\n\n# ********************************************************************\nSELECT * FROM crm_configurations where id = 834; # 882 - AnyVan , portalId: 5468262\nSELECT * FROM contacts WHERE crm_configuration_id = 834 and updated_at > '2026-03-30 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE crm_configuration_id = 834 and updated_at > '2026-03-04 08:00:00' order by updated_at desc;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 882 and sa.provider = 'hubspot';\nselect * from crm_layouts where crm_configuration_id = 834;\nselect * from crm_layout_entities where crm_layout_id = 2780;\nselect * from crm_fields where id IN (321153,321192,321193,321194);\n\nSELECT * FROM opportunities WHERE crm_configuration_id = 834 and id = 10993426;\n# ********************************************************************\nSELECT * FROM crm_configurations where id = 988; # 1057 - Teya (543ce4f4-168c-4571-91ea-5b35c253f06f) , portalId: 26651871\nSELECT * FROM opportunities WHERE team_id = 1057 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 1057 and updated_at > '2026-02-04 00:00:00' order by updated_at desc;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1057 and sa.provider = 'hubspot';\n\nSELECT * FROM crm_configurations where id = 533; # 559 - Connectd , portalId: 6710988\nSELECT * FROM opportunities WHERE team_id = 559 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 559 and updated_at > '2026-02-04 00:00:00' order by updated_at desc;\n\nSELECT * FROM crm_configurations where id = 801; # 852 - Rise Vision , portalId: 2700250\nSELECT * FROM opportunities WHERE team_id = 852 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 852 and updated_at > '2026-02-04 00:00:00' order by updated_at desc; # 6th last\n\nSELECT * FROM crm_configurations where id = 962; # 1034 - evergrowth.io , portalId: 143180990\nSELECT * FROM opportunities WHERE team_id = 1034 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 1034 and updated_at > '2026-02-04 00:00:00' order by updated_at desc;\n\n# ********************************************************************\nSELECT * FROM crm_configurations where id = 1037; # 1102 - Jibble , portalId: 6649755\nSELECT * FROM opportunities WHERE team_id = 1102 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 1102 and updated_at > '2026-02-04 12:30:00' order by updated_at desc; # 8\n\nSELECT * FROM crm_configurations where id = 1015; # 1049 - Travefy , portalId: 48904401\nSELECT * FROM opportunities WHERE team_id = 1049 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 1049 and updated_at > '2026-02-04 12:30:00' order by updated_at desc; # 20\n\nSELECT * FROM crm_configurations where id = 64; # 70 - SalaryFinance , portalId: 3404115\nSELECT * FROM opportunities WHERE team_id = 70 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 70 and updated_at > '2026-02-04 12:30:00' order by updated_at desc; # 6th last\n\nSELECT * FROM crm_configurations where id = 802; # 853 - Street Group , portalId: 7658438\nSELECT * FROM opportunities WHERE team_id = 853 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 853 and updated_at > '2026-02-04 12:30:00' order by updated_at desc; # 10\n\nSELECT * FROM crm_configurations where id = 872; # 921 - In Professional Development , portalId: 9238273\nSELECT * FROM opportunities WHERE team_id = 921 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 921 and updated_at > '2026-02-04 12:30:00' order by updated_at desc; # 2\n\n# ********************************************************************\nSELECT * FROM crm_configurations where id = 550; # 576 - SeedLegals , portalId: 3028661\nSELECT * FROM opportunities WHERE team_id = 576 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 576 and updated_at > '2026-02-05 14:00:00' order by updated_at desc;\n\nSELECT * FROM crm_configurations where id = 989; # 1058 - rtaoutdoor.com , portalId: 22371204\nSELECT * FROM opportunities WHERE team_id = 1058 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 1058 and updated_at > '2026-02-05 14:00:00' order by updated_at desc;\n\nSELECT * FROM crm_configurations where id = 896; # 946 - Mintago , portalId: 6621281\nSELECT * FROM opportunities WHERE team_id = 946 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 946 and updated_at > '2026-02-05 14:00:00' order by updated_at desc;\n\nSELECT * FROM crm_configurations where id = 617; # 641 - PCS , portalId: 5244937\nSELECT * FROM opportunities WHERE team_id = 641 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 641 and updated_at > '2026-02-05 14:00:00' order by updated_at desc; # 7th\n# ********************************************************************\nSELECT * FROM crm_configurations where id = 649; # 670 - Eventeny , portalId: 4492849\nSELECT * FROM opportunities WHERE team_id = 670 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 670 and updated_at > '2026-02-09 08:00:00' order by updated_at desc; #\n\nSELECT * FROM crm_configurations where id = 48; # 51 - CleanCloud , portalId: 4373137\nSELECT * FROM opportunities WHERE team_id = 51 and updated_at > '2026-03-04 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 51 and updated_at > '2026-02-09 08:00:00' order by updated_at desc;\nselect * from users where team_id = 51; # 7783\nSELECT * FROM groups WHERE uuid_to_bin('8a8d2cb6-8b55-4fa3-8b5c-5f0e3d8de59a') = uuid; # 1130\nselect * from activity_searches where user_id = 7783;\nselect * from activity_search_filters where activity_search_id IN (32291, 32292);\n\nSELECT asf.activity_search_id, asf.id, asf.value\nFROM activity_search_filters asf\nWHERE asf.filter = 'group_id'\nAND asf.value IN (\n SELECT CONCAT(\n HEX(SUBSTR(uuid, 5, 4)), '-',\n HEX(SUBSTR(uuid, 3, 2)), '-',\n HEX(SUBSTR(uuid, 1, 2)), '-',\n HEX(SUBSTR(uuid, 9, 2)), '-',\n HEX(SUBSTR(uuid, 11))\n )\n FROM groups\n WHERE deleted_at IS NOT NULL\n);\n\nSELECT * FROM crm_configurations where id = 272; # 290 - Bonham & Brook , portalId: 5705856\nSELECT * FROM opportunities WHERE team_id = 290 and updated_at > '2026-02-05 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 290 and updated_at > '2026-02-09 08:00:00' order by updated_at desc; # 6th\n# ********************************************************************\nSELECT * FROM crm_configurations where provider = 'hubspot';\nSELECT * FROM crm_configurations where id = 1056; # 1119 - Chromatic , portalId: 45602133\nSELECT * FROM opportunities WHERE team_id = 1119 and remotely_created_at > '2026-02-01 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 1119 and updated_at > '2026-02-09 09:00:00' order by updated_at desc; # null\n# ********************************************************************\n\nselect * from contacts where crm_provider_id = '003Uu00000ojD4NIAU';\nselect\n cp.*\n# DISTINCT t.id\n# cp.id, cp.user_id, t.id, cp.crm_configuration_id, cp.contact_fields\nFROM crm_profiles cp\nJOIN crm_configurations crm on crm.id = cp.crm_configuration_id\nJOIN users u on u.id = cp.user_id\nJOIN teams t ON t.id = crm.team_id\nWHERE crm.provider = 'salesforce' and t.status = 'active'\n and cp.archived_at IS NULL and u.deleted_at IS NULL\n and t.id NOT IN (1093)\n and t.id = 2\n and cp.contact_fields IS NULL;\n# and c.crm_provider_id = '003Uu00000ojD4NIAU';\n\nSELECT * FROM users WHERE id = 26484;\nSELECT * FROM crm_profiles WHERE user_id = 26484;\nSELECT * FROM social_accounts WHERE sociable_id = 26484;\nSELECT * FROM crm_configurations where provider = 'salesforce';\nselect * from users where id IN (10022, 10403);\nselect * from users where team_id IN (526);\nselect * from teams where id IN (526, 532);\nselect * from crm_configurations where id IN (500, 516);\nselect * from crm_profiles where crm_configuration_id IN (500, 516) and user_id IN (10022, 10403);\nselect * from contacts where crm_configuration_id IN (500, 516) and crm_provider_id = '003Uu00000ojD4NIAU';\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 526 and sa.provider = 'salesforce';\nselect * from team_settings where team_id IN (526, 532);\n\nselect * from users where id IN (22824);\nselect * from crm_profiles where crm_configuration_id IN (1026);\n\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1093 and sa.provider = 'salesforce';\n\nselect * from teams where id = 1099;\nselect * from users where id = 29643\n\nselect * from activity_processing_states;\n\nSELECT * FROM teams where name LIKE '%Fare%'; # 233\nSELECT * FROM opportunities where crm_configuration_id = 215\n# and crm_provider_id = 'oppo_ogESZf2P50nDrd1nGPvKDXeA6sSaTN5v51Lp4ayVzKR'\n;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1088 and sa.provider = 'hubspot';\n\nSELECT * FROM teams order by updated_at DESC\nSELECT * FROM crm_configurations WHERE id = 1019; # SimpleConsign 1088 - no social account\n\nselect * from crm_configurations where provider = 'pipedrive';\n\nselect * from teams where id = 957;\nselect * from crm_configurations where id = 957;\n\nSELECT * FROM teams WHERE name LIKE '%Prolific%'; # 544, 518, 10743\nSELECT * FROM opportunities where crm_configuration_id = 518 order by id desc;\n\nselect * from users where team_id = 1; # 26726 - Gabriela Dureva\nSELECT * FROM opportunities where user_id = 26726; # 16834447 - Prolific\nselect * from activities where user_id = 26726 order by id desc;\nselect * from contacts where crm_configuration_id = 1\nand email IN ('charlotte.ward@prolific.com', 'frankie.bryant@prolific.com'); # 2094416, 2093620\nSELECT * FROM contacts WHERE id = 6284931;\n\nSELECT p.* FROM activities a JOIN participants p ON a.id = p.activity_id\nWHERE a.user_id = 26726 and p.lead_id IN (2094416, 2093620) and a.created_at > '2026-01-01 00:00:00' order by p.email;\n\nselect * from activities where id IN (75509259,75509261,75509261,75511034,75026464,75517602,75517605);\nselect * from crm_configurations where id = 1;\n\n43801692-1aeb-32ce-acba-5b80a479701a\n44c3c9cf-6f5e-75f3-8179-bc9f75dd2b1b\n405975c0-b3d0-7aaa-821f-09d59cae6dd1\n4caf848d-4bed-2299-b248-7788d41f9fca\n49bedc3f-f196-eef3-89c3-dea6a3b4aa63\n43420989-a09d-b8f8-9806-c8bbf7a02aac\n\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1 and sa.provider = 'salesforce';\n\nSELECT * FROM activities WHERE id = 75461988;\n\nSELECT * FROM activities WHERE uuid_to_bin('d6c5052e-e972-49e9-8912-26f2f7d6c5f6') = uuid;\n\nselect * from contacts where id = 17900517;\n\nselect * from contact_roles cr join crm_configurations crm on cr.crm_configuration_id = crm.id\nwhere crm.provider != 'salesforce';\n\nselect * from users where id = 21047;\nSELECT * FROM crm_configurations WHERE id = 892;\nSELECT * FROM teams WHERE id = 942;\nselect * from opportunities where team_id = 942 order by updated_at desc;\nselect * from contacts where team_id = 942 order by updated_at desc;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 942 and sa.provider = 'hubspot';\n\nSELECT * FROM opportunities where team_id = 1 and crm_provider_id IN ('006Pq00000NeH6XIAV', '006Pq000007z8kdIAA'); # 10697889, 6621430\nSELECT * FROM crm_configurations WHERE id = 1;\nSELECT * FROM teams WHERE crm_id = 1;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1 and sa.provider = 'salesforce';\n\nselect id, user_id, opportunity_fields from crm_profiles where crm_configuration_id = 1\nSELECT * FROM opportunities where team_id = 1 order by updated_at desc; # 10697889, 6621430\n\nselect * from teams where id = 852;\nselect * from groups where id = 2286;\nselect * from sidekick_settings where team_id = 852;\nselect * from default_activity_types where team_id = 852;\n\n\nSELECT cc.provider, cc.id, p.id, u.*\nFROM users u\nLEFT JOIN crm_profiles p ON u.id = p.user_id AND p.id IS NULL -- no profile\nINNER JOIN teams t ON u.team_id = t.id AND t.status = 'active' -- team is active\nINNER JOIN crm_configurations cc ON t.crm_id = cc.id\nWHERE u.status = 1 AND u.deleted_at IS NULL\nAND u.crm_required = 1\nAND u.team_id = 1\nORDER BY u.team_id;\n\nSELECT * FROM crm_profiles cp where cp.crm_configuration_id = 1 and cp.user_id IN (\n18481\n );\n\nSELECT cc.provider, cc.id, p.id, u.*\nFROM users u\nLEFT JOIN crm_profiles p ON u.id = p.user_id\nINNER JOIN teams t ON u.team_id = t.id AND t.status = 'active'\nINNER JOIN crm_configurations cc ON t.crm_id = cc.id\nWHERE u.status = 1\n AND u.deleted_at IS NULL\n AND u.crm_required = 1\n# AND u.team_id = 1\n AND p.id IS NULL -- Move this condition to WHERE clause\nORDER BY u.team_id;\n\nSELECT * FROM opportunities WHERE id = 20002609;\nselect * from teams where id = 1122; # Velatir, 29953 - christian@velatir.com\nselect * from crm_configurations where id = 1060;\nselect * from crm_layouts where crm_configuration_id = 1060;\nSELECT * FROM crm_layout_entities WHERE crm_layout_id = 3596;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1122 and sa.provider = 'hubspot';\nselect * from opportunities where team_id = 1122 order by updated_at desc;\n\nselect * from crm_field_data where object_type = 'contact';\n\nSELECT * FROM activities WHERE uuid_to_bin('374fc8ed-3315-4c9f-9b25-318b7fd2928f') = uuid; # 76584262\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 248 and sa.provider = 'salesforce';\n\nSELECT * FROM crm_profiles where user_id = 24115; # 005QF000002CswMYAS\nSELECT * FROM users where id = 24115;\nSELECT * FROM accounts where id = 4002896;\nSELECT * FROM teams WHERE name LIKE '%adswerve%';\nSELECT * FROM opportunities where crm_configuration_id = 230 AND crm_provider_id IN (\"0069N000003GIQ9QAO\",\"0061r000019yGP9AAM\",\"0066900001S2KWlAAN\",\"0066900001TDpj2AAD\",\"0066900001b8uEwAAI\",\"0069N000001rQi0QAE\",\"006QF00000KD40mYAD\",\"006QF00000LzpRJYAZ\",\"0069N000002uomtQAA\",\"0069N000002xlMLQAY\",\"0066900001NV6ubAAD\",\"0061r00001HJp45AAD\",\"006QF00000uTlUoYAK\",\"006QF00000v0bZqYAI\");\nSELECT * FROM opportunities WHERE crm_configuration_id = 230 AND crm_provider_id = '0069N000003GIQ9QAO'; # 6272203\n\nSELECT u.id, u.email, ac.name, a.* FROM activities a\nJOIN users u ON a.user_id = u.id\nJOIN accounts ac ON a.account_id = ac.id\nWHERE\nuuid_to_bin('e3269598-b562-44fb-b5e9-9d2694dc63e0') = a.uuid or\nuuid_to_bin('66ddc3ab-4e15-45aa-af0c-248c1eece593') = a.uuid or\nuuid_to_bin('826bd328-e1cc-4213-b8d8-572454cacc07') = a.uuid;\n\nselect * from users where id = 5825;\nSELECT * FROM activities WHERE uuid_to_bin('e56aa2e8-231a-421b-ab1f-cb38ed2bf573') = uuid;\n\nselect * from activities where uuid_to_bin('91e13b2f-2d1b-45f8-b1fd-1141b6563782') = uuid;\n19594, 862\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 862 and sa.provider = 'salesforce';\n\nselect * from automated_reports where id = 36;\nselect ar.frequency, r.*, ar.* from automated_report_results r\njoin automated_reports ar on r.report_id = ar.id\nwhere ar.frequency != 'one_off';\n\nselect s.* from activity_searches s join users u ON s.user_id = u.id where u.team_id = 882;\nselect * from nudges n where n.activity_search_id\n\nselect * from teams where created_at > '2026-03-09';\nSELECT * FROM crm_layouts WHERE crm_configuration_id = 1065; # 1065\nSELECT * FROM crm_layout_entities WHERE crm_layout_id = 3617;\n\nselect * from users where team_id = 1 and name like '%Lukas%'; # 7160\n\nSELECT * FROM teams WHERE id = 575;\nselect * from opportunities where team_id = 575;\nSELECT * FROM teams WHERE name LIKE '%Integrum ESG%'; # 1126, 1065,\nselect * from opportunities where team_id = 1126;\nSELECT * FROM teams WHERE name LIKE '%Base%'; # 1125, 1063,\nselect * from opportunities where team_id = 1125;\nselect * from contacts c\nwhere c.team_id = 882;\n\nSELECT * FROM activities WHERE id = 76822967;\nSELECT * FROM crm_profiles WHERE user_id = 15440;\nSELECT * FROM crm_profiles WHERE crm_configuration_id = 555;\nSELECT * FROM crm_configurations WHERE id = 555;\nSELECT * FROM users WHERE id = 15440; # team. 581, gr. 15440, pl. 3911, act. field 162182\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 581 and sa.provider = 'salesforce';\n\nSELECT * FROM automated_report_results order by id desc;\n\nselect * from features;\nselect * from team_features where feature_id = 40;\n\nselect * from teams where id = 556;\n\nselect * from automated_reports;\nwhere id = 54; # 4fdd41f6-dcf0-30d0-b339-7345381b6044 , [\"pdf\",\"podcast\"]\nSELECT * FROM automated_report_results WHERE uuid_to_bin('822fa41b-afd3-43a9-a248-86b0e36f3131') = uuid;\nselect * from automated_report_results order by id desc;\nSELECT * FROM automated_report_results WHERE id = 1919;\n\nselect * from automated_report_results WHERE report_id = 54;\n\nselect * from opportunities where id = 7594349;\n\nSELECT * FROM teams WHERE name LIKE '%Les%'; # 711, 692, 16067 - jiminnyintegration@lesmills.com\nselect * from playbooks where team_id = 711; # event 226147\nSELECT * FROM playbook_categories WHERE playbook_id = 5515;\nSELECT * FROM crm_fields WHERE crm_configuration_id = 692 and object_type = 'event';\nSELECT * FROM crm_fields WHERE id = 226147;\nSELECT * FROM crm_field_values WHERE crm_field_id = 226147;\n\nSELECT * FROM crm_configurations WHERE id = 692;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 711 and sa.provider = 'salesforce';\n\nSELECT * FROM crm_profiles cp JOIN users u on u.id = cp.user_id WHERE u.team_id = 711;\n\nselect * from leads;\n\nselect * from calendars;\n\nSELECT\n t.id AS team_id,\n t.name,\n LOWER(SUBSTRING_INDEX(c.calendar_provider_id, '@', -1)) AS calendar_domain\nFROM teams t\nJOIN users u ON u.team_id = t.id\nJOIN calendars c ON c.user_id = u.id AND c.status = 'active' AND c.calendar_provider_id LIKE '%@%'\nLEFT JOIN team_domains td\n ON td.team_id = t.id\n AND td.deleted_at IS NULL\n AND td.domain = LOWER(SUBSTRING_INDEX(c.calendar_provider_id, '@', -1))\nGROUP BY t.id, t.name, calendar_domain\nORDER BY t.name, calendar_domain;\n\nselect * from users u join calendars c on c.user_id = u.id\nwhere u.team_id = 882;\n\n\nselect * from activities where id = 74049485; # team 563 crm 537\nselect * from activities where id = 73272382; # team 563 crm 537\nselect * from activities where id = 64400389; # team 563 crm 537\nselect * from activities where id = 58081273; # team 563 crm 537\nselect * from activities where id = 54520297; # team 563 crm 537\nselect * from participants where activity_id = 58081273;\n\nselect * from activities where crm_configuration_id = 537 and provider = 'aircall'\nand account_id = 19003658 order by updated_at desc;\n\nselect * from contacts where crm_configuration_id = 537 and id = 35957759;\nselect * from accounts where crm_configuration_id = 537 and id = 19003658;\n\nselect * from automated_report_results where id = 1976;\nselect * from automated_reports where id = 583;\nselect * from activity_searches where id = 87714;\nselect * from activity_search_filters where activity_search_id = 87714;\n\nSELECT * FROM activities WHERE uuid_to_bin('8827f672-202d-4162-9d04-73ff5f0566a9') = uuid\nor uuid_to_bin('47842446-af51-4bcb-854f-cc6560290101') = uuid;\n\nSELECT * FROM crm_configurations WHERE provider = 'hubspot';\nselect * from rate_limits;\n\nselect * from automated_report_results where media_type = 'pdf' and status = 2\nand id IN (18, 1872);\nselect * from automated_reports where id = 54;\nSELECT * FROM users WHERE id IN (24623,29443,29613);\n\nSELECT * FROM automated_report_results WHERE uuid_to_bin('822fa41b-afd3-43a9-a248-86b0e36f3131') = uuid;\n\nselect * from text_relays where created_at > '2026-05-01';\nand id IN (32415, 32416);\n# and id = 32412;\n\nselect * from users where team_id = 2 and email like '%scott%' and id = 29510;\n\nSELECT * FROM activities WHERE uuid_to_bin('67cebfc2-ed56-44a2-8c68-7a0286ed8618') = uuid; # 79763436\n\nSELECT email_provider_id, COUNT(*) as count, GROUP_CONCAT(id) as ids, GROUP_CONCAT(status) as statuses\nFROM text_relays\nWHERE email_provider_id IN ('19e2027868a64b42', '19e2033ed8ea6b10')\nGROUP BY email_provider_id;\nSELECT id, status, telephony_provider_id, created_at\nFROM activities\nWHERE id IN (80028719, 80028846);\nSELECT id, status, code, email_sent_at, created_at, updated_at\nFROM text_relays\nWHERE id IN (32415, 32416);\nSELECT id, status, code, sender, recipient, created_at\nFROM text_relays\nWHERE sender LIKE '%mario.georgiev%' OR sender LIKE '%stoyan.tomov%'\nORDER BY created_at DESC\nLIMIT 10;\n\nSELECT id, uuid, status, code, sender, recipient, created_at, updated_at\nFROM text_relays\nWHERE uuid = uuid_to_bin('0626141c-27a6-4d8c-aff8-7c8020a2c656');\n\n# ***************\nSELECT DISTINCT u.id, u.email, u.name, u.softphone_number, COUNT(a.id) as sms_count\nFROM users u\nINNER JOIN activities a ON u.id = .user_id\nWHERE a.type LIKE 'sms%'\nAND a.created_at > DATE_SUB(NOW(), INTERVAL 30 DAY)\nGROUP BY u.id, u.email, u.name, u.softphone_number\nORDER BY sms_count DESC;\n\nselect * from teams where id = 1;\n\nselect * from roles;\n\n\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1117 and sa.provider = 'hubspot';\nSELECT * FROM activities WHERE uuid_to_bin('8024fffb-2df7-4017-91f4-d9f896050248') = uuid; # 79933459 YES\nSELECT * FROM activities WHERE uuid_to_bin('25529043-8094-4781-927f-4f4da2a8185c') = uuid; # 80186192 NO\nSELECT * FROM crm_configurations WHERE id = 1053;\nSELECT * FROM teams WHERE id = 1117;\nselect * from users where id = 30249;\nselect * from playbooks where id = 5473;\nselect * from playbook_categories where id = 43783;\nselect * from playbook_categories where playbook_id = 5473;\nselect * from crm_fields where id = 659242;\nselect * from crm_field_values where crm_field_id = 659242;\n\nSELECT * FROM crm_field_data fd\n# JOIN crm_fields f ON fd.crm_field_id = f.id\n# JOIN activities a ON fd.activity_id = a.id\nWHERE activity_id = 79933459\n# AND f.crm_provider_id = 'hs_activity_type';\n\n\nSELECT * FROM activity_messages;\nselect * from text_relays where created_at > '2026-05-01';\nselect * from activities where user_id IN (7160, 18608) and created_at > '2026-05-22' order by id desc;\nselect * from users where team_id = 1 and id IN (18608, 13934, 7160);\nselect * from activities where user_id = 7160 order by id desc limit 10;\n\nselect * from accounts where team_id = 1 and name = 'Column5';\n\nselect * from users where name like '%Subra%'; # 31054, 1117\nselect * from teams where id = 1117;\nselect * from activity_searches where user_id = 31054;\nselect * from activity_search_filters where activity_search_id IN (88882, 88902);","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.011968086,"top":0.047885075,"width":0.024268618,"height":0.024740623},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
6187952496506413928
|
2218652951799805519
|
visual_change
|
accessibility
|
NULL
|
Project: faVsco.js, menu
#12121 on JY-20963-fix-im Project: faVsco.js, menu
#12121 on JY-20963-fix-import-on-deleted-entity, menu
Start Listening for PHP Debug Connections
ServiceTest
Run 'ServiceTest'
Debug 'ServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
22
5
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Crm;
use Illuminate\Support\Collection;
use Jiminny\Contracts\Repositories\TeamRepository;
use Jiminny\Contracts\Services\Crm\ServiceInterface;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Models\Account;
use Jiminny\Models\Activity;
use Jiminny\Models\Contact;
use Jiminny\Models\Lead;
use Jiminny\Models\Opportunity;
use Jiminny\Models\Participant;
use Jiminny\Models\Stage;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Services\ResolveTeamCrmConnection;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Psr\Log\LoggerInterface;
use Exception;
use Throwable;
class CrmActivityService
{
public function __construct(
private readonly TeamRepository $teamRepository,
private readonly CachedCrmServiceDecorator $decorator,
private readonly EmailHelper $emailHelper,
private readonly ResolveTeamCrmConnection $teamCrmResolver,
private readonly LoggerInterface $logger,
) {
}
/**
* Updates CRM data for an activity and its participants.
*
* NOTE: This method performs multiple database writes and should be called
* within a transaction by the caller to ensure atomicity.
*
* @param Activity $activity
* @param bool $remoteSearch
*
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception
*/
public function updateCrmData(
Activity $activity,
bool $remoteSearch = false,
): void {
$crmService = null;
$participants = $activity->getParticipants();
$team = $activity->getTeam();
$prospectSearchStrategy = ProspectSearchStrategyFactory::match($team);
if ($prospectSearchStrategy->ignoreCrmMatchData()) {
$this->logger->info('[CrmActivityService] Ignoring crm data because of prospect strategy', [
'activity_id' => $activity->getId(),
'strategy' => get_class($prospectSearchStrategy),
]);
return;
}
if ($remoteSearch) {
try {
$crmService = $this->teamCrmResolver->resolveForTeam($team);
} catch (SocialAccountTokenInvalidException) {
$this->logger->warning('[CrmActivityService] CRM token expired, falling back to local search', [
'activity_id' => $activity->getId(),
'team_id' => $team->getId(),
]);
}
}
$records = $this->updateParticipantsCrmData(
team: $team,
activity: $activity,
participants: $participants,
crmService: $crmService,
);
if (! empty($records)) {
$activity->updateActivityCrmData($records);
}
$activity->refresh();
}
/**
* @param Collection<Participant> $participants
*
* @throws Exception
*
* @return array{
* Lead|null,
* Account|null,
* Opportunity|null,
* Contact|null,
* Stage|null,
* string|null
*}|array{}
*/
private function updateParticipantsCrmData(
Team $team,
Activity $activity,
Collection $participants,
?ServiceInterface $crmService = null,
): array {
$matchedRecords = [];
$matchedDomainRecords = [];
$this->validateCrmConfiguration($activity);
$this->decorator->setConfiguration($activity->getCrm());
$this->decorator->setCrmService($crmService);
foreach ($participants as $participant) {
if ($this->shouldSkipParticipant($participant)) {
continue;
}
if (! $this->shouldPerformLookup($participant, $team)) {
$this->logger->info('[CrmActivityService] Email domain belongs to the team, skipping crm lookup', [
'activity_id' => $activity->getId(),
'team_id' => $team->getId(),
'email' => $participant->getEmailAddress(),
]);
$this->attachUserIfExists($participant, $team);
continue;
}
$records = $this->findCrmRecords($participant, $activity);
if (! empty($records)) {
$matchedRecords[] = $records;
} else {
$records = $this->findCrmDomainRecords(
crmService: $crmService,
participant: $participant,
activity: $activity,
);
if (! empty($records)) {
$matchedDomainRecords[] = $records;
}
}
if (empty($records)) {
continue;
}
try {
$activity->updateParticipantCrmData($records, $participant);
} catch (Throwable $ex) {
$this->logger->error('[CrmActivityService] Failed to update participant CRM data', [
'activity_id' => $activity->getId(),
'participant_id' => $participant->getId(),
'exception' => $ex->getMessage(),
]);
continue;
}
}
$bestMatch = $this->getBestMatch(
matchedRecords : $matchedRecords,
matchedDomainRecords: $matchedDomainRecords,
);
$this->logger->info('[CrmActivityService] CRM matching completed', [
'activity_id' => $activity->getId(),
'participants_processed' => $participants->count(),
'exact_matches' => count($matchedRecords),
'domain_matches' => count($matchedDomainRecords),
'best_match_found' => ! empty($bestMatch),
]);
return $bestMatch;
}
private function shouldPerformLookup(Participant $participant, Team $team): bool
{
if ($participant->hasEmailAddress()) {
return $this->emailHelper->shouldPerformLookup($team, $participant->getEmailAddress());
}
return true;
}
private function validateCrmConfiguration(Activity $activity): void
{
if ($activity->getCrm() === null) {
throw new InvalidArgumentException('Cannot find CRM configuration');
}
}
private function getBestMatch(?array $matchedRecords, ?array $matchedDomainRecords): array
{
return RecordSelector::pickBestFromLists($matchedRecords, $matchedDomainRecords);
}
private function findCrmRecords(Participant $participant, Activity $activity): ?array
{
$records = null;
if ($participant->hasEmailAddress()) {
$records = $this->decorator->matchExactlyByEmail(
email: $participant->getEmailAddress(),
userId: $activity->getUser()->getId()
);
}
if (empty($records) && $participant->getPhoneNumber() !== null) {
$records = $this->decorator->matchByPhone(
phone: $participant->getPhoneNumber(),
userId: $activity->getUser()->getId(),
);
}
if (empty($records) && $participant->getName() !== null) {
$records = $this->decorator->matchByName(
name: $participant->getName(),
userId: $activity->getUser()->getId(),
);
}
return $records;
}
private function shouldSkipParticipant(Participant $participant): bool
{
return $participant->hasUser();
}
private function attachUserIfExists(Participant $participant, Team $team): void
{
if ($participant->hasEmailAddress() === false) {
return;
}
$user = $this->teamRepository->findActiveTeamMemberByEmail($team, $participant->getEmailAddress());
if ($user instanceof User) {
$participant->user_id = $user->getId();
$participant->save();
}
}
private function findCrmDomainRecords(
?ServiceInterface $crmService,
Participant $participant,
Activity $activity,
): array {
if ($participant->hasEmailAddress()) {
$this->decorator->setConfiguration($activity->getCrm());
$this->decorator->setCrmService($crmService);
$records = $this->decorator->matchByDomain(
email: $participant->getEmailAddress(),
userId: $activity->getUser()->getId()
);
if (! empty($records)) {
return $records;
}
}
return [];
}
}
Execute
Explain Plan
Browse Query History
View Parameters
Open Query Execution Settings…
In-Editor Results
Tx: Auto
Cancel Running Statements
Playground
jiminny
Sync Changes
Hide This Notification
Code changed:
Hide
45
1
41
66
Previous Highlighted Error
Next Highlighted Error
SELECT * FROM teams WHERE name LIKE '%litify%'; # 1069, 994, 24993
SELECT * FROM users WHERE id = 25061;
SELECT * FROM crm_profiles WHERE crm_configuration_id = 994;
SELECT * FROM crm_profiles WHERE user_id = 25061;
select * from crm_configurations where id = 834;
SELECT * FROM teams WHERE id = 882;
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 882 and sa.provider = 'hubspot';
SELECT * FROM crm_configurations where id = 882; # 933 - GoGlobal
SELECT * FROM opportunities WHERE team_id = 933 order by updated_at desc;
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 933 and sa.provider = 'hubspot';
SELECT * FROM crm_configurations WHERE provider = 'hubspot' and crm_provider_id = 7270388;
SELECT * FROM contacts where crm_configuration_id = 834;
SELECT * FROM opportunities WHERE team_id = 933
# AND crm_provider_id IN ('20131586060','46017317898','52543911090','53451356564','54101251892','54323768459');
AND id IN (8482561,18352941,19042734,19232139,19445140,19472541);
SELECT * FROM opportunity_contacts
WHERE opportunity_id IN (8482561,18352941,19042734,19232139,19445140,19472541);
# [PASSWORD_DOTS]
SELECT * FROM crm_configurations where id = 485; #
SELECT * FROM opportunities WHERE team_id = 933 order by updated_at desc;
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 933 and sa.provider = 'hubspot';
select crm.provider, l.* from leads l join crm_configurations crm on l.crm_configuration_id = crm.id
where crm.provider NOT IN ('salesforce', 'integration-app', 'bullhorn', 'copper')
# and l.converted_at IS NOT NULL
;
# [PASSWORD_DOTS]
SELECT * FROM activities a WHERE type IN ('email-inbound', 'email-outbound')
and opportunity_id IS NULL
order by id desc;
SELECT * FROM teams WHERE id = 604; # 598
SELECT * FROM activities WHERE id = 74410828; # [EMAIL]
SELECT * FROM accounts WHERE id = 20068382;
SELECT * FROM accounts WHERE id = 35186038;
SELECT * FROM contacts WHERE team_id = 852 and updated_at > '2026-01-23 12:30:00' order by updated_at desc;
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 559 and sa.provider = 'hubspot';
SELECT * FROM activities WHERE uuid_to_bin('cb6342b6-a183-401c-b0af-ede92b2ae763') = uuid;
select * from sidekick_settings where team_id = 781;
SELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id = 26651871; # Teya
SELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id = 7562435;
SELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id = 8420347; # opflit 2100
SELECT * FROM crm_layouts WHERE crm_configuration_id = 711;
SELECT * FROM activities where crm_configuration_id = 711 and crm_provider_id IS NULL
and is_internal = 0 and status = 'completed'
order by id desc;
SELECT * FROM crm_layout_entities
WHERE crm_layout_id IN (2352, 2353);
;
SELECT * FROM crm_configurations where provider = 'hubspot' and id = 530;
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 556 and sa.provider = 'hubspot';
SELECT * FROM activities WHERE uuid_to_bin('c6ca4b22-7738-4563-a95d-b8a9598924ae') = uuid;
SELECT * FROM activities WHERE uuid_to_bin('442abb2b-28bd-4be8-9c25-19e9bf02766d') = uuid;
select * from contacts
where crm_configuration_id = 530
and crm_provider_id = 872252;
select * from activities where crm_configuration_id = 530
and user_id = 14343 and type like '%softphone%'
and created_at between '2026-01-28 15:00:00' and '2026-01-28 15:10:00';
SELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id = 25666868; # Teya
SELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id = 8646335; # Teya
SELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id IN (5933397);
SELECT t.name, t.id, t.owner_id, c.id, c.provider, c.crm_base_url FROM teams t
JOIN crm_configurations c ON t.id = c.team_id
WHERE t.status = 'active';
SELECT * FROM teams where id = 1091;
SELECT * FROM crm_configurations where team_id = 1091;
SELECT * FROM activity_providers where team_id = 1091;
SELECT * FROM activities where crm_configuration_id = 1024 and type IN ('softphone', 'softphone-outbound')
and provider NOT IN ('hubspot', 'aircall')
# and telephony_provider_id = '019c1131-a22f-4792-b9ea-20adf6a02ed0'
order by id desc;
SELECT * FROM teams WHERE name LIKE '%Leadventure%';
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 1091 and sa.provider = 'salesforce';
SELECT * FROM teams WHERE name LIKE '%Wilson%'; # 862, 812
SELECT * FROM teams where id = 862;
SELECT * FROM crm_configurations where team_id = 862;
SELECT * FROM activity_providers where team_id = 862;
SELECT * FROM activities where crm_configuration_id = 812 and type IN ('softphone', 'softphone-outbound')
and provider NOT IN ('hubspot', 'aircall')
# and telephony_provider_id = '019c1131-a22f-4792-b9ea-20adf6a02ed0'
order by id desc;
SELECT t.id, crm.id, crm.provider, ap.* FROM teams t
join crm_configurations crm on t.id = crm.team_id
join activity_providers ap on t.id = ap.team_id
where t.status = 'active' and ap.is_enabled = 1
and crm.provider = 'hubspot'
and ap.provider NOT IN ('hubspot', 'aircall', 'uploader', 'gong', 'twilio', 'zoom-bot', 'google-meet', 'ms-teams',
'outreach', 'close', 'ringcentral', 'dialpad', 'zoom-phone');
SELECT * FROM teams where id = 1068;
SELECT * FROM crm_configurations where team_id = 1068;
SELECT * FROM activity_providers where team_id = 1068;
SELECT * FROM activities a
where crm_configuration_id = 993 and type IN ('softphone', 'softphone-outbound')
and a.provider NOT IN ('hubspot', 'uploader', 'gong', 'twilio', 'google-meet', 'ms-teams','close'
)
# and telephony_provider_id = '019c1131-a22f-4792-b9ea-20adf6a02ed0'
order by a.id desc;
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 1068 and sa.provider = 'hubspot';
# [PASSWORD_DOTS]
# [PASSWORD_DOTS]
# [PASSWORD_DOTS]
SELECT * FROM crm_configurations where id = 882; # 933 - GoGlobal , portalId: 6017093
SELECT * FROM opportunities WHERE team_id = 933 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 933 and updated_at > '2026-02-06 00:00:00' order by updated_at desc;
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 933 and sa.provider = 'hubspot';
# [PASSWORD_DOTS]
SELECT * FROM crm_configurations where id = 834; # 882 - AnyVan , portalId: 5468262
SELECT * FROM contacts WHERE crm_configuration_id = 834 and updated_at > '2026-03-30 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE crm_configuration_id = 834 and updated_at > '2026-03-04 08:00:00' order by updated_at desc;
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 882 and sa.provider = 'hubspot';
select * from crm_layouts where crm_configuration_id = 834;
select * from crm_layout_entities where crm_layout_id = 2780;
select * from crm_fields where id IN (321153,321192,321193,321194);
SELECT * FROM opportunities WHERE crm_configuration_id = 834 and id = 10993426;
# [PASSWORD_DOTS]
SELECT * FROM crm_configurations where id = 988; # 1057 - Teya (543ce4f4-168c-4571-91ea-5b35c253f06f) , portalId: 26651871
SELECT * FROM opportunities WHERE team_id = 1057 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 1057 and updated_at > '2026-02-04 00:00:00' order by updated_at desc;
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 1057 and sa.provider = 'hubspot';
SELECT * FROM crm_configurations where id = 533; # 559 - Connectd , portalId: 6710988
SELECT * FROM opportunities WHERE team_id = 559 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 559 and updated_at > '2026-02-04 00:00:00' order by updated_at desc;
SELECT * FROM crm_configurations where id = 801; # 852 - Rise Vision , portalId: 2700250
SELECT * FROM opportunities WHERE team_id = 852 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 852 and updated_at > '2026-02-04 00:00:00' order by updated_at desc; # 6th last
SELECT * FROM crm_configurations where id = 962; # 1034 - evergrowth.io , portalId: 143180990
SELECT * FROM opportunities WHERE team_id = 1034 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 1034 and updated_at > '2026-02-04 00:00:00' order by updated_at desc;
# [PASSWORD_DOTS]
SELECT * FROM crm_configurations where id = 1037; # 1102 - Jibble , portalId: 6649755
SELECT * FROM opportunities WHERE team_id = 1102 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 1102 and updated_at > '2026-02-04 12:30:00' order by updated_at desc; # 8
SELECT * FROM crm_configurations where id = 1015; # 1049 - Travefy , portalId: 48904401
SELECT * FROM opportunities WHERE team_id = 1049 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 1049 and updated_at > '2026-02-04 12:30:00' order by updated_at desc; # 20
SELECT * FROM crm_configurations where id = 64; # 70 - SalaryFinance , portalId: 3404115
SELECT * FROM opportunities WHERE team_id = 70 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 70 and updated_at > '2026-02-04 12:30:00' order by updated_at desc; # 6th last
SELECT * FROM crm_configurations where id = 802; # 853 - Street Group , portalId: 7658438
SELECT * FROM opportunities WHERE team_id = 853 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 853 and updated_at > '2026-02-04 12:30:00' order by updated_at desc; # 10
SELECT * FROM crm_configurations where id = 872; # 921 - In Professional Development , portalId: 9238273
SELECT * FROM opportunities WHERE team_id = 921 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 921 and updated_at > '2026-02-04 12:30:00' order by updated_at desc; # 2
# [PASSWORD_DOTS]
SELECT * FROM crm_configurations where id = 550; # 576 - SeedLegals , portalId: 3028661
SELECT * FROM opportunities WHERE team_id = 576 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 576 and updated_at > '2026-02-05 14:00:00' order by updated_at desc;
SELECT * FROM crm_configurations where id = 989; # 1058 - rtaoutdoor.com , portalId: 22371204
SELECT * FROM opportunities WHERE team_id = 1058 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 1058 and updated_at > '2026-02-05 14:00:00' order by updated_at desc;
SELECT * FROM crm_configurations where id = 896; # 946 - Mintago , portalId: 6621281
SELECT * FROM opportunities WHERE team_id = 946 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 946 and updated_at > '2026-02-05 14:00:00' order by updated_at desc;
SELECT * FROM crm_configurations where id = 617; # 641 - PCS , portalId: 5244937
SELECT * FROM opportunities WHERE team_id = 641 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 641 and updated_at > '2026-02-05 14:00:00' order by updated_at desc; # 7th
# [PASSWORD_DOTS]
SELECT * FROM crm_configurations where id = 649; # 670 - Eventeny , portalId: 4492849
SELECT * FROM opportunities WHERE team_id = 670 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 670 and updated_at > '2026-02-09 08:00:00' order by updated_at desc; #
SELECT * FROM crm_configurations where id = 48; # 51 - CleanCloud , portalId: 4373137
SELECT * FROM opportunities WHERE team_id = 51 and updated_at > '2026-03-04 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 51 and updated_at > '2026-02-09 08:00:00' order by updated_at desc;
select * from users where team_id = 51; # 7783
SELECT * FROM groups WHERE uuid_to_bin('8a8d2cb6-8b55-4fa3-8b5c-5f0e3d8de59a') = uuid; # 1130
select * from activity_searches where user_id = 7783;
select * from activity_search_filters where activity_search_id IN (32291, 32292);
SELECT asf.activity_search_id, asf.id, asf.value
FROM activity_search_filters asf
WHERE asf.filter = 'group_id'
AND asf.value IN (
SELECT CONCAT(
HEX(SUBSTR(uuid, 5, 4)), '-',
HEX(SUBSTR(uuid, 3, 2)), '-',
HEX(SUBSTR(uuid, 1, 2)), '-',
HEX(SUBSTR(uuid, 9, 2)), '-',
HEX(SUBSTR(uuid, 11))
)
FROM groups
WHERE deleted_at IS NOT NULL
);
SELECT * FROM crm_configurations where id = 272; # 290 - Bonham & Brook , portalId: 5705856
SELECT * FROM opportunities WHERE team_id = 290 and updated_at > '2026-02-05 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 290 and updated_at > '2026-02-09 08:00:00' order by updated_at desc; # 6th
# [PASSWORD_DOTS]
SELECT * FROM crm_configurations where provider = 'hubspot';
SELECT * FROM crm_configurations where id = 1056; # 1119 - Chromatic , portalId: 45602133
SELECT * FROM opportunities WHERE team_id = 1119 and remotely_created_at > '2026-02-01 00:00:00' order by updated_at desc;
SELECT * FROM opportunities WHERE team_id = 1119 and updated_at > '2026-02-09 09:00:00' order by updated_at desc; # null
# [PASSWORD_DOTS]
select * from contacts where crm_provider_id = '003Uu00000ojD4NIAU';
select
cp.*
# DISTINCT t.id
# cp.id, cp.user_id, t.id, cp.crm_configuration_id, cp.contact_fields
FROM crm_profiles cp
JOIN crm_configurations crm on crm.id = cp.crm_configuration_id
JOIN users u on u.id = cp.user_id
JOIN teams t ON t.id = crm.team_id
WHERE crm.provider = 'salesforce' and t.status = 'active'
and cp.archived_at IS NULL and u.deleted_at IS NULL
and t.id NOT IN (1093)
and t.id = 2
and cp.contact_fields IS NULL;
# and c.crm_provider_id = '003Uu00000ojD4NIAU';
SELECT * FROM users WHERE id = 26484;
SELECT * FROM crm_profiles WHERE user_id = 26484;
SELECT * FROM social_accounts WHERE sociable_id = 26484;
SELECT * FROM crm_configurations where provider = 'salesforce';
select * from users where id IN (10022, 10403);
select * from users where team_id IN (526);
select * from teams where id IN (526, 532);
select * from crm_configurations where id IN (500, 516);
select * from crm_profiles where crm_configuration_id IN (500, 516) and user_id IN (10022, 10403);
select * from contacts where crm_configuration_id IN (500, 516) and crm_provider_id = '003Uu00000ojD4NIAU';
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 526 and sa.provider = 'salesforce';
select * from team_settings where team_id IN (526, 532);
select * from users where id IN (22824);
select * from crm_profiles where crm_configuration_id IN (1026);
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 1093 and sa.provider = 'salesforce';
select * from teams where id = 1099;
select * from users where id = 29643
select * from activity_processing_states;
SELECT * FROM teams where name LIKE '%Fare%'; # 233
SELECT * FROM opportunities where crm_configuration_id = 215
# and crm_provider_id = 'oppo_ogESZf2P50nDrd1nGPvKDXeA6sSaTN5v51Lp4ayVzKR'
;
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 1088 and sa.provider = 'hubspot';
SELECT * FROM teams order by updated_at DESC
SELECT * FROM crm_configurations WHERE id = 1019; # SimpleConsign 1088 - no social account
select * from crm_configurations where provider = 'pipedrive';
select * from teams where id = 957;
select * from crm_configurations where id = 957;
SELECT * FROM teams WHERE name LIKE '%Prolific%'; # 544, 518, 10743
SELECT * FROM opportunities where crm_configuration_id = 518 order by id desc;
select * from users where team_id = 1; # 26726 - Gabriela Dureva
SELECT * FROM opportunities where user_id = 26726; # 16834447 - Prolific
select * from activities where user_id = 26726 order by id desc;
select * from contacts where crm_configuration_id = 1
and email IN ('[EMAIL]', '[EMAIL]'); # 2094416, 2093620
SELECT * FROM contacts WHERE id = 6284931;
SELECT p.* FROM activities a JOIN participants p ON a.id = p.activity_id
WHERE a.user_id = 26726 and p.lead_id IN (2094416, 2093620) and a.created_at > '2026-01-01 00:00:00' order by p.email;
select * from activities where id IN (75509259,75509261,75509261,75511034,75026464,75517602,75517605);
select * from crm_configurations where id = 1;
43801692-1aeb-32ce-acba-5b80a479701a
44c3c9cf-6f5e-75f3-8179-bc9f75dd2b1b
405975c0-b3d0-7aaa-821f-09d59cae6dd1
4caf848d-4bed-2299-b248-7788d41f9fca
49bedc3f-f196-eef3-89c3-dea6a3b4aa63
43420989-a09d-b8f8-9806-c8bbf7a02aac
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 1 and sa.provider = 'salesforce';
SELECT * FROM activities WHERE id = 75461988;
SELECT * FROM activities WHERE uuid_to_bin('d6c5052e-e972-49e9-8912-26f2f7d6c5f6') = uuid;
select * from contacts where id = 17900517;
select * from contact_roles cr join crm_configurations crm on cr.crm_configuration_id = crm.id
where crm.provider != 'salesforce';
select * from users where id = 21047;
SELECT * FROM crm_configurations WHERE id = 892;
SELECT * FROM teams WHERE id = 942;
select * from opportunities where team_id = 942 order by updated_at desc;
select * from contacts where team_id = 942 order by updated_at desc;
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 942 and sa.provider = 'hubspot';
SELECT * FROM opportunities where team_id = 1 and crm_provider_id IN ('006Pq00000NeH6XIAV', '006Pq000007z8kdIAA'); # 10697889, 6621430
SELECT * FROM crm_configurations WHERE id = 1;
SELECT * FROM teams WHERE crm_id = 1;
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 1 and sa.provider = 'salesforce';
select id, user_id, opportunity_fields from crm_profiles where crm_configuration_id = 1
SELECT * FROM opportunities where team_id = 1 order by updated_at desc; # 10697889, 6621430
select * from teams where id = 852;
select * from groups where id = 2286;
select * from sidekick_settings where team_id = 852;
select * from default_activity_types where team_id = 852;
SELECT cc.provider, cc.id, p.id, u.*
FROM users u
LEFT JOIN crm_profiles p ON u.id = p.user_id AND p.id IS NULL -- no profile
INNER JOIN teams t ON u.team_id = t.id AND t.status = 'active' -- team is active
INNER JOIN crm_configurations cc ON t.crm_id = cc.id
WHERE u.status = 1 AND u.deleted_at IS NULL
AND u.crm_required = 1
AND u.team_id = 1
ORDER BY u.team_id;
SELECT * FROM crm_profiles cp where cp.crm_configuration_id = 1 and cp.user_id IN (
18481
);
SELECT cc.provider, cc.id, p.id, u.*
FROM users u
LEFT JOIN crm_profiles p ON u.id = p.user_id
INNER JOIN teams t ON u.team_id = t.id AND t.status = 'active'
INNER JOIN crm_configurations cc ON t.crm_id = cc.id
WHERE u.status = 1
AND u.deleted_at IS NULL
AND u.crm_required = 1
# AND u.team_id = 1
AND p.id IS NULL -- Move this condition to WHERE clause
ORDER BY u.team_id;
SELECT * FROM opportunities WHERE id = 20002609;
select * from teams where id = 1122; # Velatir, 29953 - [EMAIL]
select * from crm_configurations where id = 1060;
select * from crm_layouts where crm_configuration_id = 1060;
SELECT * FROM crm_layout_entities WHERE crm_layout_id = 3596;
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 1122 and sa.provider = 'hubspot';
select * from opportunities where team_id = 1122 order by updated_at desc;
select * from crm_field_data where object_type = 'contact';
SELECT * FROM activities WHERE uuid_to_bin('374fc8ed-3315-4c9f-9b25-318b7fd2928f') = uuid; # 76584262
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 248 and sa.provider = 'salesforce';
SELECT * FROM crm_profiles where user_id = 24115; # 005QF000002CswMYAS
SELECT * FROM users where id = 24115;
SELECT * FROM accounts where id = 4002896;
SELECT * FROM teams WHERE name LIKE '%adswerve%';
SELECT * FROM opportunities where crm_configuration_id = 230 AND crm_provider_id IN ("0069N000003GIQ9QAO","0061r000019yGP9AAM","0066900001S2KWlAAN","0066900001TDpj2AAD","0066900001b8uEwAAI","0069N000001rQi0QAE","006QF00000KD40mYAD","006QF00000LzpRJYAZ","0069N000002uomtQAA","0069N000002xlMLQAY","0066900001NV6ubAAD","0061r00001HJp45AAD","006QF00000uTlUoYAK","006QF00000v0bZqYAI");
SELECT * FROM opportunities WHERE crm_configuration_id = 230 AND crm_provider_id = '0069N000003GIQ9QAO'; # 6272203
SELECT u.id, u.email, ac.name, a.* FROM activities a
JOIN users u ON a.user_id = u.id
JOIN accounts ac ON a.account_id = ac.id
WHERE
uuid_to_bin('e3269598-b562-44fb-b5e9-9d2694dc63e0') = a.uuid or
uuid_to_bin('66ddc3ab-4e15-45aa-af0c-248c1eece593') = a.uuid or
uuid_to_bin('826bd328-e1cc-4213-b8d8-572454cacc07') = a.uuid;
select * from users where id = 5825;
SELECT * FROM activities WHERE uuid_to_bin('e56aa2e8-231a-421b-ab1f-cb38ed2bf573') = uuid;
select * from activities where uuid_to_bin('91e13b2f-2d1b-45f8-b1fd-1141b6563782') = uuid;
19594, 862
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 862 and sa.provider = 'salesforce';
select * from automated_reports where id = 36;
select ar.frequency, r.*, ar.* from automated_report_results r
join automated_reports ar on r.report_id = ar.id
where ar.frequency != 'one_off';
select s.* from activity_searches s join users u ON s.user_id = u.id where u.team_id = 882;
select * from nudges n where n.activity_search_id
select * from teams where created_at > '2026-03-09';
SELECT * FROM crm_layouts WHERE crm_configuration_id = 1065; # 1065
SELECT * FROM crm_layout_entities WHERE crm_layout_id = 3617;
select * from users where team_id = 1 and name like '%Lukas%'; # 7160
SELECT * FROM teams WHERE id = 575;
select * from opportunities where team_id = 575;
SELECT * FROM teams WHERE name LIKE '%Integrum ESG%'; # 1126, 1065,
select * from opportunities where team_id = 1126;
SELECT * FROM teams WHERE name LIKE '%Base%'; # 1125, 1063,
select * from opportunities where team_id = 1125;
select * from contacts c
where c.team_id = 882;
SELECT * FROM activities WHERE id = 76822967;
SELECT * FROM crm_profiles WHERE user_id = 15440;
SELECT * FROM crm_profiles WHERE crm_configuration_id = 555;
SELECT * FROM crm_configurations WHERE id = 555;
SELECT * FROM users WHERE id = 15440; # team. 581, gr. 15440, pl. 3911, act. field 162182
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 581 and sa.provider = 'salesforce';
SELECT * FROM automated_report_results order by id desc;
select * from features;
select * from team_features where feature_id = 40;
select * from teams where id = 556;
select * from automated_reports;
where id = 54; # 4fdd41f6-dcf0-30d0-b339-7345381b6044 , ["pdf","podcast"]
SELECT * FROM automated_report_results WHERE uuid_to_bin('822fa41b-afd3-43a9-a248-86b0e36f3131') = uuid;
select * from automated_report_results order by id desc;
SELECT * FROM automated_report_results WHERE id = 1919;
select * from automated_report_results WHERE report_id = 54;
select * from opportunities where id = 7594349;
SELECT * FROM teams WHERE name LIKE '%Les%'; # 711, 692, 16067 - [EMAIL]
select * from playbooks where team_id = 711; # event 226147
SELECT * FROM playbook_categories WHERE playbook_id = 5515;
SELECT * FROM crm_fields WHERE crm_configuration_id = 692 and object_type = 'event';
SELECT * FROM crm_fields WHERE id = 226147;
SELECT * FROM crm_field_values WHERE crm_field_id = 226147;
SELECT * FROM crm_configurations WHERE id = 692;
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 711 and sa.provider = 'salesforce';
SELECT * FROM crm_profiles cp JOIN users u on u.id = cp.user_id WHERE u.team_id = 711;
select * from leads;
select * from calendars;
SELECT
t.id AS team_id,
t.name,
LOWER(SUBSTRING_INDEX(c.calendar_provider_id, '@', -1)) AS calendar_domain
FROM teams t
JOIN users u ON u.team_id = t.id
JOIN calendars c ON c.user_id = u.id AND c.status = 'active' AND c.calendar_provider_id LIKE '%@%'
LEFT JOIN team_domains td
ON td.team_id = t.id
AND td.deleted_at IS NULL
AND td.domain = LOWER(SUBSTRING_INDEX(c.calendar_provider_id, '@', -1))
GROUP BY t.id, t.name, calendar_domain
ORDER BY t.name, calendar_domain;
select * from users u join calendars c on c.user_id = u.id
where u.team_id = 882;
select * from activities where id = 74049485; # team 563 crm 537
select * from activities where id = 73272382; # team 563 crm 537
select * from activities where id = 64400389; # team 563 crm 537
select * from activities where id = 58081273; # team 563 crm 537
select * from activities where id = 54520297; # team 563 crm 537
select * from participants where activity_id = 58081273;
select * from activities where crm_configuration_id = 537 and provider = 'aircall'
and account_id = 19003658 order by updated_at desc;
select * from contacts where crm_configuration_id = 537 and id = 35957759;
select * from accounts where crm_configuration_id = 537 and id = 19003658;
select * from automated_report_results where id = 1976;
select * from automated_reports where id = 583;
select * from activity_searches where id = 87714;
select * from activity_search_filters where activity_search_id = 87714;
SELECT * FROM activities WHERE uuid_to_bin('8827f672-202d-4162-9d04-73ff5f0566a9') = uuid
or uuid_to_bin('47842446-af51-4bcb-854f-cc6560290101') = uuid;
SELECT * FROM crm_configurations WHERE provider = 'hubspot';
select * from rate_limits;
select * from automated_report_results where media_type = 'pdf' and status = 2
and id IN (18, 1872);
select * from automated_reports where id = 54;
SELECT * FROM users WHERE id IN (24623,29443,29613);
SELECT * FROM automated_report_results WHERE uuid_to_bin('822fa41b-afd3-43a9-a248-86b0e36f3131') = uuid;
select * from text_relays where created_at > '2026-05-01';
and id IN (32415, 32416);
# and id = 32412;
select * from users where team_id = 2 and email like '%scott%' and id = 29510;
SELECT * FROM activities WHERE uuid_to_bin('67cebfc2-ed56-44a2-8c68-7a0286ed8618') = uuid; # 79763436
SELECT email_provider_id, COUNT(*) as count, GROUP_CONCAT(id) as ids, GROUP_CONCAT(status) as statuses
FROM text_relays
WHERE email_provider_id IN ('19e2027868a64b42', '19e2033ed8ea6b10')
GROUP BY email_provider_id;
SELECT id, status, telephony_provider_id, created_at
FROM activities
WHERE id IN (80028719, 80028846);
SELECT id, status, code, email_sent_at, created_at, updated_at
FROM text_relays
WHERE id IN (32415, 32416);
SELECT id, status, code, sender, recipient, created_at
FROM text_relays
WHERE sender LIKE '%mario.georgiev%' OR sender LIKE '%stoyan.tomov%'
ORDER BY created_at DESC
LIMIT 10;
SELECT id, uuid, status, code, sender, recipient, created_at, updated_at
FROM text_relays
WHERE uuid = uuid_to_bin('0626141c-27a6-4d8c-aff8-7c8020a2c656');
# [PASSWORD_DOTS]
SELECT DISTINCT u.id, u.email, u.name, u.softphone_number, COUNT(a.id) as sms_count
FROM users u
INNER JOIN activities a ON u.id = .user_id
WHERE a.type LIKE 'sms%'
AND a.created_at > DATE_SUB(NOW(), INTERVAL 30 DAY)
GROUP BY u.id, u.email, u.name, u.softphone_number
ORDER BY sms_count DESC;
select * from teams where id = 1;
select * from roles;
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 1117 and sa.provider = 'hubspot';
SELECT * FROM activities WHERE uuid_to_bin('8024fffb-2df7-4017-91f4-d9f896050248') = uuid; # 79933459 YES
SELECT * FROM activities WHERE uuid_to_bin('[CREDIT_CARD]-927f-4f4da2a8185c') = uuid; # 80186192 NO
SELECT * FROM crm_configurations WHERE id = 1053;
SELECT * FROM teams WHERE id = 1117;
select * from users where id = 30249;
select * from playbooks where id = 5473;
select * from playbook_categories where id = 43783;
select * from playbook_categories where playbook_id = 5473;
select * from crm_fields where id = 659242;
select * from crm_field_values where crm_field_id = 659242;
SELECT * FROM crm_field_data fd
# JOIN crm_fields f ON fd.crm_field_id = f.id
# JOIN activities a ON fd.activity_id = a.id
WHERE activity_id = 79933459
# AND f.crm_provider_id = 'hs_activity_type';
SELECT * FROM activity_messages;
select * from text_relays where created_at > '2026-05-01';
select * from activities where user_id IN (7160, 18608) and created_at > '2026-05-22' order by id desc;
select * from users where team_id = 1 and id IN (18608, 13934, 7160);
select * from activities where user_id = 7160 order by id desc limit 10;
select * from accounts where team_id = 1 and name = 'Column5';
select * from users where name like '%Subra%'; # 31054, 1117
select * from teams where id = 1117;
select * from activity_searches where user_id = 31054;
select * from activity_search_filters where activity_search_id IN (88882, 88902);
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
86387
|
NULL
|
NULL
|
NULL
|
|
86388
|
2962
|
7
|
2026-05-28T14:07:01.157965+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-28/1779 /Users/lukas/.screenpipe/data/data/2026-05-28/1779977221157_m1.jpg...
|
PhpStorm
|
faVsco.js – CrmActivityService.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
#12121 on JY-20963-fix-im Project: faVsco.js, menu
#12121 on JY-20963-fix-import-on-deleted-entity, menu
Start Listening for PHP Debug Connections
ServiceTest
Run 'ServiceTest'
Debug 'ServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
22
5
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Crm;
use Illuminate\Support\Collection;
use Jiminny\Contracts\Repositories\TeamRepository;
use Jiminny\Contracts\Services\Crm\ServiceInterface;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Models\Account;
use Jiminny\Models\Activity;
use Jiminny\Models\Contact;
use Jiminny\Models\Lead;
use Jiminny\Models\Opportunity;
use Jiminny\Models\Participant;
use Jiminny\Models\Stage;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Services\ResolveTeamCrmConnection;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Psr\Log\LoggerInterface;
use Exception;
use Throwable;
class CrmActivityService
{
public function __construct(
private readonly TeamRepository $teamRepository,
private readonly CachedCrmServiceDecorator $decorator,
private readonly EmailHelper $emailHelper,
private readonly ResolveTeamCrmConnection $teamCrmResolver,
private readonly LoggerInterface $logger,
) {
}
/**
* Updates CRM data for an activity and its participants.
*
* NOTE: This method performs multiple database writes and should be called
* within a transaction by the caller to ensure atomicity.
*
* @param Activity $activity
* @param bool $remoteSearch
*
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception
*/
public function updateCrmData(
Activity $activity,
bool $remoteSearch = false,
): void {
$crmService = null;
$participants = $activity->getParticipants();
$team = $activity->getTeam();
$prospectSearchStrategy = ProspectSearchStrategyFactory::match($team);
if ($prospectSearchStrategy->ignoreCrmMatchData()) {
$this->logger->info('[CrmActivityService] Ignoring crm data because of prospect strategy', [
'activity_id' => $activity->getId(),
'strategy' => get_class($prospectSearchStrategy),
]);
return;
}
if ($remoteSearch) {
try {
$crmService = $this->teamCrmResolver->resolveForTeam($team);
} catch (SocialAccountTokenInvalidException) {
$this->logger->warning('[CrmActivityService] CRM token expired, falling back to local search', [
'activity_id' => $activity->getId(),
'team_id' => $team->getId(),
]);
}
}
$records = $this->updateParticipantsCrmData(
team: $team,
activity: $activity,
participants: $participants,
crmService: $crmService,
);
if (! empty($records)) {
$activity->updateActivityCrmData($records);
}
$activity->refresh();
}
/**
* @param Collection<Participant> $participants
*
* @throws Exception
*
* @return array{
* Lead|null,
* Account|null,
* Opportunity|null,
* Contact|null,
* Stage|null,
* string|null
*}|array{}
*/
private function updateParticipantsCrmData(
Team $team,
Activity $activity,
Collection $participants,
?ServiceInterface $crmService = null,
): array {
$matchedRecords = [];
$matchedDomainRecords = [];
$this->validateCrmConfiguration($activity);
$this->decorator->setConfiguration($activity->getCrm());
$this->decorator->setCrmService($crmService);
foreach ($participants as $participant) {
if ($this->shouldSkipParticipant($participant)) {
continue;
}
if (! $this->shouldPerformLookup($participant, $team)) {
$this->logger->info('[CrmActivityService] Email domain belongs to the team, skipping crm lookup', [
'activity_id' => $activity->getId(),
'team_id' => $team->getId(),
'email' => $participant->getEmailAddress(),
]);
$this->attachUserIfExists($participant, $team);
continue;
}
$records = $this->findCrmRecords($participant, $activity);
if (! empty($records)) {
$matchedRecords[] = $records;
} else {
$records = $this->findCrmDomainRecords(
crmService: $crmService,
participant: $participant,
activity: $activity,
);
if (! empty($records)) {
$matchedDomainRecords[] = $records;
}
}
if (empty($records)) {
continue;
}
try {
$activity->updateParticipantCrmData($records, $participant);
} catch (Throwable $ex) {
$this->logger->error('[CrmActivityService] Failed to update participant CRM data', [
'activity_id' => $activity->getId(),
'participant_id' => $participant->getId(),
'exception' => $ex->getMessage(),
]);
continue;
}
}
$bestMatch = $this->getBestMatch(
matchedRecords : $matchedRecords,
matchedDomainRecords: $matchedDomainRecords,
);
$this->logger->info('[CrmActivityService] CRM matching completed', [
'activity_id' => $activity->getId(),
'participants_processed' => $participants->count(),
'exact_matches' => count($matchedRecords),
'domain_matches' => count($matchedDomainRecords),
'best_match_found' => ! empty($bestMatch),
]);
return $bestMatch;
}
private function shouldPerformLookup(Participant $participant, Team $team): bool
{
if ($participant->hasEmailAddress()) {
return $this->emailHelper->shouldPerformLookup($team, $participant->getEmailAddress());
}
return true;
}
private function validateCrmConfiguration(Activity $activity): void
{
if ($activity->getCrm() === null) {
throw new InvalidArgumentException('Cannot find CRM configuration');
}
}
private function getBestMatch(?array $matchedRecords, ?array $matchedDomainRecords): array
{
return RecordSelector::pickBestFromLists($matchedRecords, $matchedDomainRecords);
}
private function findCrmRecords(Participant $participant, Activity $activity): ?array
{
$records = null;
if ($participant->hasEmailAddress()) {
$records = $this->decorator->matchExactlyByEmail(
email: $participant->getEmailAddress(),
userId: $activity->getUser()->getId()
);
}
if (empty($records) && $participant->getPhoneNumber() !== null) {
$records = $this->decorator->matchByPhone(
phone: $participant->getPhoneNumber(),
userId: $activity->getUser()->getId(),
);
}
if (empty($records) && $participant->getName() !== null) {
$records = $this->decorator->matchByName(
name: $participant->getName(),
userId: $activity->getUser()->getId(),
);
}
return $records;
}
private function shouldSkipParticipant(Participant $participant): bool
{
return $participant->hasUser();
}
private function attachUserIfExists(Participant $participant, Team $team): void
{
if ($participant->hasEmailAddress() === false) {
return;
}
$user = $this->teamRepository->findActiveTeamMemberByEmail($team, $participant->getEmailAddress());
if ($user instanceof User) {
$participant->user_id = $user->getId();
$participant->save();
}
}
private function findCrmDomainRecords(
?ServiceInterface $crmService,
Participant $participant,
Activity $activity,
): array {
if ($participant->hasEmailAddress()) {
$this->decorator->setConfiguration($activity->getCrm());
$this->decorator->setCrmService($crmService);
$records = $this->decorator->matchByDomain(
email: $participant->getEmailAddress(),
userId: $activity->getUser()->getId()
);
if (! empty($records)) {
return $records;
}
}
return [];
}
}...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"#12121 on JY-20963-fix-import-on-deleted-entity, menu","depth":5,"on_screen":true,"help_text":"Pull request #12121 exists for current branch JY-20963-fix-import-on-deleted-entity, but pull request details loading failed","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"ServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'ServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'ServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"22","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"5","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Crm;\n\nuse Illuminate\\Support\\Collection;\nuse Jiminny\\Contracts\\Repositories\\TeamRepository;\nuse Jiminny\\Contracts\\Services\\Crm\\ServiceInterface;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Exceptions\\SocialAccountTokenInvalidException;\nuse Jiminny\\Models\\Account;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Contact;\nuse Jiminny\\Models\\Lead;\nuse Jiminny\\Models\\Opportunity;\nuse Jiminny\\Models\\Participant;\nuse Jiminny\\Models\\Stage;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\ResolveTeamCrmConnection;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Psr\\Log\\LoggerInterface;\nuse Exception;\nuse Throwable;\n\nclass CrmActivityService\n{\n public function __construct(\n private readonly TeamRepository $teamRepository,\n private readonly CachedCrmServiceDecorator $decorator,\n private readonly EmailHelper $emailHelper,\n private readonly ResolveTeamCrmConnection $teamCrmResolver,\n private readonly LoggerInterface $logger,\n ) {\n }\n\n /**\n * Updates CRM data for an activity and its participants.\n *\n * NOTE: This method performs multiple database writes and should be called\n * within a transaction by the caller to ensure atomicity.\n *\n * @param Activity $activity\n * @param bool $remoteSearch\n *\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception\n */\n public function updateCrmData(\n Activity $activity,\n bool $remoteSearch = false,\n ): void {\n $crmService = null;\n $participants = $activity->getParticipants();\n $team = $activity->getTeam();\n\n $prospectSearchStrategy = ProspectSearchStrategyFactory::match($team);\n if ($prospectSearchStrategy->ignoreCrmMatchData()) {\n $this->logger->info('[CrmActivityService] Ignoring crm data because of prospect strategy', [\n 'activity_id' => $activity->getId(),\n 'strategy' => get_class($prospectSearchStrategy),\n ]);\n\n return;\n }\n\n if ($remoteSearch) {\n try {\n $crmService = $this->teamCrmResolver->resolveForTeam($team);\n } catch (SocialAccountTokenInvalidException) {\n $this->logger->warning('[CrmActivityService] CRM token expired, falling back to local search', [\n 'activity_id' => $activity->getId(),\n 'team_id' => $team->getId(),\n ]);\n }\n }\n\n $records = $this->updateParticipantsCrmData(\n team: $team,\n activity: $activity,\n participants: $participants,\n crmService: $crmService,\n );\n\n if (! empty($records)) {\n $activity->updateActivityCrmData($records);\n }\n\n $activity->refresh();\n }\n\n /**\n * @param Collection<Participant> $participants\n *\n * @throws Exception\n *\n * @return array{\n * Lead|null,\n * Account|null,\n * Opportunity|null,\n * Contact|null,\n * Stage|null,\n * string|null\n *}|array{}\n */\n private function updateParticipantsCrmData(\n Team $team,\n Activity $activity,\n Collection $participants,\n ?ServiceInterface $crmService = null,\n ): array {\n $matchedRecords = [];\n $matchedDomainRecords = [];\n\n $this->validateCrmConfiguration($activity);\n $this->decorator->setConfiguration($activity->getCrm());\n $this->decorator->setCrmService($crmService);\n\n foreach ($participants as $participant) {\n if ($this->shouldSkipParticipant($participant)) {\n continue;\n }\n\n if (! $this->shouldPerformLookup($participant, $team)) {\n $this->logger->info('[CrmActivityService] Email domain belongs to the team, skipping crm lookup', [\n 'activity_id' => $activity->getId(),\n 'team_id' => $team->getId(),\n 'email' => $participant->getEmailAddress(),\n ]);\n\n $this->attachUserIfExists($participant, $team);\n\n continue;\n }\n\n $records = $this->findCrmRecords($participant, $activity);\n\n if (! empty($records)) {\n $matchedRecords[] = $records;\n } else {\n $records = $this->findCrmDomainRecords(\n crmService: $crmService,\n participant: $participant,\n activity: $activity,\n );\n if (! empty($records)) {\n $matchedDomainRecords[] = $records;\n }\n }\n\n if (empty($records)) {\n continue;\n }\n\n try {\n $activity->updateParticipantCrmData($records, $participant);\n } catch (Throwable $ex) {\n $this->logger->error('[CrmActivityService] Failed to update participant CRM data', [\n 'activity_id' => $activity->getId(),\n 'participant_id' => $participant->getId(),\n 'exception' => $ex->getMessage(),\n ]);\n\n continue;\n }\n }\n\n $bestMatch = $this->getBestMatch(\n matchedRecords : $matchedRecords,\n matchedDomainRecords: $matchedDomainRecords,\n );\n\n $this->logger->info('[CrmActivityService] CRM matching completed', [\n 'activity_id' => $activity->getId(),\n 'participants_processed' => $participants->count(),\n 'exact_matches' => count($matchedRecords),\n 'domain_matches' => count($matchedDomainRecords),\n 'best_match_found' => ! empty($bestMatch),\n ]);\n\n return $bestMatch;\n }\n\n private function shouldPerformLookup(Participant $participant, Team $team): bool\n {\n if ($participant->hasEmailAddress()) {\n return $this->emailHelper->shouldPerformLookup($team, $participant->getEmailAddress());\n }\n\n return true;\n }\n\n private function validateCrmConfiguration(Activity $activity): void\n {\n if ($activity->getCrm() === null) {\n throw new InvalidArgumentException('Cannot find CRM configuration');\n }\n }\n\n private function getBestMatch(?array $matchedRecords, ?array $matchedDomainRecords): array\n {\n return RecordSelector::pickBestFromLists($matchedRecords, $matchedDomainRecords);\n }\n\n private function findCrmRecords(Participant $participant, Activity $activity): ?array\n {\n $records = null;\n\n if ($participant->hasEmailAddress()) {\n $records = $this->decorator->matchExactlyByEmail(\n email: $participant->getEmailAddress(),\n userId: $activity->getUser()->getId()\n );\n }\n\n if (empty($records) && $participant->getPhoneNumber() !== null) {\n $records = $this->decorator->matchByPhone(\n phone: $participant->getPhoneNumber(),\n userId: $activity->getUser()->getId(),\n );\n }\n\n if (empty($records) && $participant->getName() !== null) {\n $records = $this->decorator->matchByName(\n name: $participant->getName(),\n userId: $activity->getUser()->getId(),\n );\n }\n\n return $records;\n }\n\n private function shouldSkipParticipant(Participant $participant): bool\n {\n return $participant->hasUser();\n }\n\n private function attachUserIfExists(Participant $participant, Team $team): void\n {\n if ($participant->hasEmailAddress() === false) {\n return;\n }\n\n $user = $this->teamRepository->findActiveTeamMemberByEmail($team, $participant->getEmailAddress());\n\n if ($user instanceof User) {\n $participant->user_id = $user->getId();\n $participant->save();\n }\n }\n\n private function findCrmDomainRecords(\n ?ServiceInterface $crmService,\n Participant $participant,\n Activity $activity,\n ): array {\n if ($participant->hasEmailAddress()) {\n $this->decorator->setConfiguration($activity->getCrm());\n $this->decorator->setCrmService($crmService);\n\n $records = $this->decorator->matchByDomain(\n email: $participant->getEmailAddress(),\n userId: $activity->getUser()->getId()\n );\n if (! empty($records)) {\n return $records;\n }\n }\n\n return [];\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Crm;\n\nuse Illuminate\\Support\\Collection;\nuse Jiminny\\Contracts\\Repositories\\TeamRepository;\nuse Jiminny\\Contracts\\Services\\Crm\\ServiceInterface;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Exceptions\\SocialAccountTokenInvalidException;\nuse Jiminny\\Models\\Account;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Contact;\nuse Jiminny\\Models\\Lead;\nuse Jiminny\\Models\\Opportunity;\nuse Jiminny\\Models\\Participant;\nuse Jiminny\\Models\\Stage;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\ResolveTeamCrmConnection;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Psr\\Log\\LoggerInterface;\nuse Exception;\nuse Throwable;\n\nclass CrmActivityService\n{\n public function __construct(\n private readonly TeamRepository $teamRepository,\n private readonly CachedCrmServiceDecorator $decorator,\n private readonly EmailHelper $emailHelper,\n private readonly ResolveTeamCrmConnection $teamCrmResolver,\n private readonly LoggerInterface $logger,\n ) {\n }\n\n /**\n * Updates CRM data for an activity and its participants.\n *\n * NOTE: This method performs multiple database writes and should be called\n * within a transaction by the caller to ensure atomicity.\n *\n * @param Activity $activity\n * @param bool $remoteSearch\n *\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception\n */\n public function updateCrmData(\n Activity $activity,\n bool $remoteSearch = false,\n ): void {\n $crmService = null;\n $participants = $activity->getParticipants();\n $team = $activity->getTeam();\n\n $prospectSearchStrategy = ProspectSearchStrategyFactory::match($team);\n if ($prospectSearchStrategy->ignoreCrmMatchData()) {\n $this->logger->info('[CrmActivityService] Ignoring crm data because of prospect strategy', [\n 'activity_id' => $activity->getId(),\n 'strategy' => get_class($prospectSearchStrategy),\n ]);\n\n return;\n }\n\n if ($remoteSearch) {\n try {\n $crmService = $this->teamCrmResolver->resolveForTeam($team);\n } catch (SocialAccountTokenInvalidException) {\n $this->logger->warning('[CrmActivityService] CRM token expired, falling back to local search', [\n 'activity_id' => $activity->getId(),\n 'team_id' => $team->getId(),\n ]);\n }\n }\n\n $records = $this->updateParticipantsCrmData(\n team: $team,\n activity: $activity,\n participants: $participants,\n crmService: $crmService,\n );\n\n if (! empty($records)) {\n $activity->updateActivityCrmData($records);\n }\n\n $activity->refresh();\n }\n\n /**\n * @param Collection<Participant> $participants\n *\n * @throws Exception\n *\n * @return array{\n * Lead|null,\n * Account|null,\n * Opportunity|null,\n * Contact|null,\n * Stage|null,\n * string|null\n *}|array{}\n */\n private function updateParticipantsCrmData(\n Team $team,\n Activity $activity,\n Collection $participants,\n ?ServiceInterface $crmService = null,\n ): array {\n $matchedRecords = [];\n $matchedDomainRecords = [];\n\n $this->validateCrmConfiguration($activity);\n $this->decorator->setConfiguration($activity->getCrm());\n $this->decorator->setCrmService($crmService);\n\n foreach ($participants as $participant) {\n if ($this->shouldSkipParticipant($participant)) {\n continue;\n }\n\n if (! $this->shouldPerformLookup($participant, $team)) {\n $this->logger->info('[CrmActivityService] Email domain belongs to the team, skipping crm lookup', [\n 'activity_id' => $activity->getId(),\n 'team_id' => $team->getId(),\n 'email' => $participant->getEmailAddress(),\n ]);\n\n $this->attachUserIfExists($participant, $team);\n\n continue;\n }\n\n $records = $this->findCrmRecords($participant, $activity);\n\n if (! empty($records)) {\n $matchedRecords[] = $records;\n } else {\n $records = $this->findCrmDomainRecords(\n crmService: $crmService,\n participant: $participant,\n activity: $activity,\n );\n if (! empty($records)) {\n $matchedDomainRecords[] = $records;\n }\n }\n\n if (empty($records)) {\n continue;\n }\n\n try {\n $activity->updateParticipantCrmData($records, $participant);\n } catch (Throwable $ex) {\n $this->logger->error('[CrmActivityService] Failed to update participant CRM data', [\n 'activity_id' => $activity->getId(),\n 'participant_id' => $participant->getId(),\n 'exception' => $ex->getMessage(),\n ]);\n\n continue;\n }\n }\n\n $bestMatch = $this->getBestMatch(\n matchedRecords : $matchedRecords,\n matchedDomainRecords: $matchedDomainRecords,\n );\n\n $this->logger->info('[CrmActivityService] CRM matching completed', [\n 'activity_id' => $activity->getId(),\n 'participants_processed' => $participants->count(),\n 'exact_matches' => count($matchedRecords),\n 'domain_matches' => count($matchedDomainRecords),\n 'best_match_found' => ! empty($bestMatch),\n ]);\n\n return $bestMatch;\n }\n\n private function shouldPerformLookup(Participant $participant, Team $team): bool\n {\n if ($participant->hasEmailAddress()) {\n return $this->emailHelper->shouldPerformLookup($team, $participant->getEmailAddress());\n }\n\n return true;\n }\n\n private function validateCrmConfiguration(Activity $activity): void\n {\n if ($activity->getCrm() === null) {\n throw new InvalidArgumentException('Cannot find CRM configuration');\n }\n }\n\n private function getBestMatch(?array $matchedRecords, ?array $matchedDomainRecords): array\n {\n return RecordSelector::pickBestFromLists($matchedRecords, $matchedDomainRecords);\n }\n\n private function findCrmRecords(Participant $participant, Activity $activity): ?array\n {\n $records = null;\n\n if ($participant->hasEmailAddress()) {\n $records = $this->decorator->matchExactlyByEmail(\n email: $participant->getEmailAddress(),\n userId: $activity->getUser()->getId()\n );\n }\n\n if (empty($records) && $participant->getPhoneNumber() !== null) {\n $records = $this->decorator->matchByPhone(\n phone: $participant->getPhoneNumber(),\n userId: $activity->getUser()->getId(),\n );\n }\n\n if (empty($records) && $participant->getName() !== null) {\n $records = $this->decorator->matchByName(\n name: $participant->getName(),\n userId: $activity->getUser()->getId(),\n );\n }\n\n return $records;\n }\n\n private function shouldSkipParticipant(Participant $participant): bool\n {\n return $participant->hasUser();\n }\n\n private function attachUserIfExists(Participant $participant, Team $team): void\n {\n if ($participant->hasEmailAddress() === false) {\n return;\n }\n\n $user = $this->teamRepository->findActiveTeamMemberByEmail($team, $participant->getEmailAddress());\n\n if ($user instanceof User) {\n $participant->user_id = $user->getId();\n $participant->save();\n }\n }\n\n private function findCrmDomainRecords(\n ?ServiceInterface $crmService,\n Participant $participant,\n Activity $activity,\n ): array {\n if ($participant->hasEmailAddress()) {\n $this->decorator->setConfiguration($activity->getCrm());\n $this->decorator->setCrmService($crmService);\n\n $records = $this->decorator->matchByDomain(\n email: $participant->getEmailAddress(),\n userId: $activity->getUser()->getId()\n );\n if (! empty($records)) {\n return $records;\n }\n }\n\n return [];\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false}]...
|
-5135967619275051346
|
-8454960550680315650
|
visual_change
|
accessibility
|
NULL
|
Project: faVsco.js, menu
#12121 on JY-20963-fix-im Project: faVsco.js, menu
#12121 on JY-20963-fix-import-on-deleted-entity, menu
Start Listening for PHP Debug Connections
ServiceTest
Run 'ServiceTest'
Debug 'ServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
22
5
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Crm;
use Illuminate\Support\Collection;
use Jiminny\Contracts\Repositories\TeamRepository;
use Jiminny\Contracts\Services\Crm\ServiceInterface;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Models\Account;
use Jiminny\Models\Activity;
use Jiminny\Models\Contact;
use Jiminny\Models\Lead;
use Jiminny\Models\Opportunity;
use Jiminny\Models\Participant;
use Jiminny\Models\Stage;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Services\ResolveTeamCrmConnection;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Psr\Log\LoggerInterface;
use Exception;
use Throwable;
class CrmActivityService
{
public function __construct(
private readonly TeamRepository $teamRepository,
private readonly CachedCrmServiceDecorator $decorator,
private readonly EmailHelper $emailHelper,
private readonly ResolveTeamCrmConnection $teamCrmResolver,
private readonly LoggerInterface $logger,
) {
}
/**
* Updates CRM data for an activity and its participants.
*
* NOTE: This method performs multiple database writes and should be called
* within a transaction by the caller to ensure atomicity.
*
* @param Activity $activity
* @param bool $remoteSearch
*
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception
*/
public function updateCrmData(
Activity $activity,
bool $remoteSearch = false,
): void {
$crmService = null;
$participants = $activity->getParticipants();
$team = $activity->getTeam();
$prospectSearchStrategy = ProspectSearchStrategyFactory::match($team);
if ($prospectSearchStrategy->ignoreCrmMatchData()) {
$this->logger->info('[CrmActivityService] Ignoring crm data because of prospect strategy', [
'activity_id' => $activity->getId(),
'strategy' => get_class($prospectSearchStrategy),
]);
return;
}
if ($remoteSearch) {
try {
$crmService = $this->teamCrmResolver->resolveForTeam($team);
} catch (SocialAccountTokenInvalidException) {
$this->logger->warning('[CrmActivityService] CRM token expired, falling back to local search', [
'activity_id' => $activity->getId(),
'team_id' => $team->getId(),
]);
}
}
$records = $this->updateParticipantsCrmData(
team: $team,
activity: $activity,
participants: $participants,
crmService: $crmService,
);
if (! empty($records)) {
$activity->updateActivityCrmData($records);
}
$activity->refresh();
}
/**
* @param Collection<Participant> $participants
*
* @throws Exception
*
* @return array{
* Lead|null,
* Account|null,
* Opportunity|null,
* Contact|null,
* Stage|null,
* string|null
*}|array{}
*/
private function updateParticipantsCrmData(
Team $team,
Activity $activity,
Collection $participants,
?ServiceInterface $crmService = null,
): array {
$matchedRecords = [];
$matchedDomainRecords = [];
$this->validateCrmConfiguration($activity);
$this->decorator->setConfiguration($activity->getCrm());
$this->decorator->setCrmService($crmService);
foreach ($participants as $participant) {
if ($this->shouldSkipParticipant($participant)) {
continue;
}
if (! $this->shouldPerformLookup($participant, $team)) {
$this->logger->info('[CrmActivityService] Email domain belongs to the team, skipping crm lookup', [
'activity_id' => $activity->getId(),
'team_id' => $team->getId(),
'email' => $participant->getEmailAddress(),
]);
$this->attachUserIfExists($participant, $team);
continue;
}
$records = $this->findCrmRecords($participant, $activity);
if (! empty($records)) {
$matchedRecords[] = $records;
} else {
$records = $this->findCrmDomainRecords(
crmService: $crmService,
participant: $participant,
activity: $activity,
);
if (! empty($records)) {
$matchedDomainRecords[] = $records;
}
}
if (empty($records)) {
continue;
}
try {
$activity->updateParticipantCrmData($records, $participant);
} catch (Throwable $ex) {
$this->logger->error('[CrmActivityService] Failed to update participant CRM data', [
'activity_id' => $activity->getId(),
'participant_id' => $participant->getId(),
'exception' => $ex->getMessage(),
]);
continue;
}
}
$bestMatch = $this->getBestMatch(
matchedRecords : $matchedRecords,
matchedDomainRecords: $matchedDomainRecords,
);
$this->logger->info('[CrmActivityService] CRM matching completed', [
'activity_id' => $activity->getId(),
'participants_processed' => $participants->count(),
'exact_matches' => count($matchedRecords),
'domain_matches' => count($matchedDomainRecords),
'best_match_found' => ! empty($bestMatch),
]);
return $bestMatch;
}
private function shouldPerformLookup(Participant $participant, Team $team): bool
{
if ($participant->hasEmailAddress()) {
return $this->emailHelper->shouldPerformLookup($team, $participant->getEmailAddress());
}
return true;
}
private function validateCrmConfiguration(Activity $activity): void
{
if ($activity->getCrm() === null) {
throw new InvalidArgumentException('Cannot find CRM configuration');
}
}
private function getBestMatch(?array $matchedRecords, ?array $matchedDomainRecords): array
{
return RecordSelector::pickBestFromLists($matchedRecords, $matchedDomainRecords);
}
private function findCrmRecords(Participant $participant, Activity $activity): ?array
{
$records = null;
if ($participant->hasEmailAddress()) {
$records = $this->decorator->matchExactlyByEmail(
email: $participant->getEmailAddress(),
userId: $activity->getUser()->getId()
);
}
if (empty($records) && $participant->getPhoneNumber() !== null) {
$records = $this->decorator->matchByPhone(
phone: $participant->getPhoneNumber(),
userId: $activity->getUser()->getId(),
);
}
if (empty($records) && $participant->getName() !== null) {
$records = $this->decorator->matchByName(
name: $participant->getName(),
userId: $activity->getUser()->getId(),
);
}
return $records;
}
private function shouldSkipParticipant(Participant $participant): bool
{
return $participant->hasUser();
}
private function attachUserIfExists(Participant $participant, Team $team): void
{
if ($participant->hasEmailAddress() === false) {
return;
}
$user = $this->teamRepository->findActiveTeamMemberByEmail($team, $participant->getEmailAddress());
if ($user instanceof User) {
$participant->user_id = $user->getId();
$participant->save();
}
}
private function findCrmDomainRecords(
?ServiceInterface $crmService,
Participant $participant,
Activity $activity,
): array {
if ($participant->hasEmailAddress()) {
$this->decorator->setConfiguration($activity->getCrm());
$this->decorator->setCrmService($crmService);
$records = $this->decorator->matchByDomain(
email: $participant->getEmailAddress(),
userId: $activity->getUser()->getId()
);
if (! empty($records)) {
return $records;
}
}
return [];
}
}...
|
86386
|
NULL
|
NULL
|
NULL
|
|
86387
|
2963
|
6
|
2026-05-28T14:07:00.344565+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-28/1779 /Users/lukas/.screenpipe/data/data/2026-05-28/1779977220344_m2.jpg...
|
PhpStorm
|
faVsco.js – CrmActivityService.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
#12121 on JY-20963-fix-im Project: faVsco.js, menu
#12121 on JY-20963-fix-import-on-deleted-entity, menu
Start Listening for PHP Debug Connections
ServiceTest
Run 'ServiceTest'
Debug 'ServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
22
5
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Crm;
use Illuminate\Support\Collection;
use Jiminny\Contracts\Repositories\TeamRepository;
use Jiminny\Contracts\Services\Crm\ServiceInterface;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Models\Account;
use Jiminny\Models\Activity;
use Jiminny\Models\Contact;
use Jiminny\Models\Lead;
use Jiminny\Models\Opportunity;
use Jiminny\Models\Participant;
use Jiminny\Models\Stage;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Services\ResolveTeamCrmConnection;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Psr\Log\LoggerInterface;
use Exception;
use Throwable;
class CrmActivityService
{
public function __construct(
private readonly TeamRepository $teamRepository,
private readonly CachedCrmServiceDecorator $decorator,
private readonly EmailHelper $emailHelper,
private readonly ResolveTeamCrmConnection $teamCrmResolver,
private readonly LoggerInterface $logger,
) {
}
/**
* Updates CRM data for an activity and its participants.
*
* NOTE: This method performs multiple database writes and should be called
* within a transaction by the caller to ensure atomicity.
*
* @param Activity $activity
* @param bool $remoteSearch
*
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception
*/
public function updateCrmData(
Activity $activity,
bool $remoteSearch = false,
): void {
$crmService = null;
$participants = $activity->getParticipants();
$team = $activity->getTeam();
$prospectSearchStrategy = ProspectSearchStrategyFactory::match($team);
if ($prospectSearchStrategy->ignoreCrmMatchData()) {
$this->logger->info('[CrmActivityService] Ignoring crm data because of prospect strategy', [
'activity_id' => $activity->getId(),
'strategy' => get_class($prospectSearchStrategy),
]);
return;
}
if ($remoteSearch) {
try {
$crmService = $this->teamCrmResolver->resolveForTeam($team);
} catch (SocialAccountTokenInvalidException) {
$this->logger->warning('[CrmActivityService] CRM token expired, falling back to local search', [
'activity_id' => $activity->getId(),
'team_id' => $team->getId(),
]);
}
}
$records = $this->updateParticipantsCrmData(
team: $team,
activity: $activity,
participants: $participants,
crmService: $crmService,
);
if (! empty($records)) {
$activity->updateActivityCrmData($records);
}
$activity->refresh();
}
/**
* @param Collection<Participant> $participants
*
* @throws Exception
*
* @return array{
* Lead|null,
* Account|null,
* Opportunity|null,
* Contact|null,
* Stage|null,
* string|null
*}|array{}
*/
private function updateParticipantsCrmData(
Team $team,
Activity $activity,
Collection $participants,
?ServiceInterface $crmService = null,
): array {
$matchedRecords = [];
$matchedDomainRecords = [];
$this->validateCrmConfiguration($activity);
$this->decorator->setConfiguration($activity->getCrm());
$this->decorator->setCrmService($crmService);
foreach ($participants as $participant) {
if ($this->shouldSkipParticipant($participant)) {
continue;
}
if (! $this->shouldPerformLookup($participant, $team)) {
$this->logger->info('[CrmActivityService] Email domain belongs to the team, skipping crm lookup', [
'activity_id' => $activity->getId(),
'team_id' => $team->getId(),
'email' => $participant->getEmailAddress(),
]);
$this->attachUserIfExists($participant, $team);
continue;
}
$records = $this->findCrmRecords($participant, $activity);
if (! empty($records)) {
$matchedRecords[] = $records;
} else {
$records = $this->findCrmDomainRecords(
crmService: $crmService,
participant: $participant,
activity: $activity,
);
if (! empty($records)) {
$matchedDomainRecords[] = $records;
}
}
if (empty($records)) {
continue;
}
try {
$activity->updateParticipantCrmData($records, $participant);
} catch (Throwable $ex) {
$this->logger->error('[CrmActivityService] Failed to update participant CRM data', [
'activity_id' => $activity->getId(),
'participant_id' => $participant->getId(),
'exception' => $ex->getMessage(),
]);
continue;
}
}
$bestMatch = $this->getBestMatch(
matchedRecords : $matchedRecords,
matchedDomainRecords: $matchedDomainRecords,
);
$this->logger->info('[CrmActivityService] CRM matching completed', [
'activity_id' => $activity->getId(),
'participants_processed' => $participants->count(),
'exact_matches' => count($matchedRecords),
'domain_matches' => count($matchedDomainRecords),
'best_match_found' => ! empty($bestMatch),
]);
return $bestMatch;
}
private function shouldPerformLookup(Participant $participant, Team $team): bool
{
if ($participant->hasEmailAddress()) {
return $this->emailHelper->shouldPerformLookup($team, $participant->getEmailAddress());
}
return true;
}
private function validateCrmConfiguration(Activity $activity): void
{
if ($activity->getCrm() === null) {
throw new InvalidArgumentException('Cannot find CRM configuration');
}
}
private function getBestMatch(?array $matchedRecords, ?array $matchedDomainRecords): array
{
return RecordSelector::pickBestFromLists($matchedRecords, $matchedDomainRecords);
}
private function findCrmRecords(Participant $participant, Activity $activity): ?array
{
$records = null;
if ($participant->hasEmailAddress()) {
$records = $this->decorator->matchExactlyByEmail(
email: $participant->getEmailAddress(),
userId: $activity->getUser()->getId()
);
}
if (empty($records) && $participant->getPhoneNumber() !== null) {
$records = $this->decorator->matchByPhone(
phone: $participant->getPhoneNumber(),
userId: $activity->getUser()->getId(),
);
}
if (empty($records) && $participant->getName() !== null) {
$records = $this->decorator->matchByName(
name: $participant->getName(),
userId: $activity->getUser()->getId(),
);
}
return $records;
}
private function shouldSkipParticipant(Participant $participant): bool
{
return $participant->hasUser();
}
private function attachUserIfExists(Participant $participant, Team $team): void
{
if ($participant->hasEmailAddress() === false) {
return;
}
$user = $this->teamRepository->findActiveTeamMemberByEmail($team, $participant->getEmailAddress());
if ($user instanceof User) {
$participant->user_id = $user->getId();
$participant->save();
}
}
private function findCrmDomainRecords(
?ServiceInterface $crmService,
Participant $participant,
Activity $activity,
): array {
if ($participant->hasEmailAddress()) {
$this->decorator->setConfiguration($activity->getCrm());
$this->decorator->setCrmService($crmService);
$records = $this->decorator->matchByDomain(
email: $participant->getEmailAddress(),
userId: $activity->getUser()->getId()
);
if (! empty($records)) {
return $records;
}
}
return [];
}
}...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"#12121 on JY-20963-fix-import-on-deleted-entity, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.11569149,"height":0.025538707},"on_screen":true,"help_text":"Pull request #12121 exists for current branch JY-20963-fix-import-on-deleted-entity, but pull request details loading failed","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.85638297,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"ServiceTest","depth":6,"bounds":{"left":0.87167555,"top":0.019952115,"width":0.043882977,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'ServiceTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'ServiceTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"22","depth":4,"bounds":{"left":0.34042552,"top":0.12529927,"width":0.009973404,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"5","depth":4,"bounds":{"left":0.35239363,"top":0.12529927,"width":0.007978723,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.3620346,"top":0.123703115,"width":0.00731383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.3693484,"top":0.123703115,"width":0.006981383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Crm;\n\nuse Illuminate\\Support\\Collection;\nuse Jiminny\\Contracts\\Repositories\\TeamRepository;\nuse Jiminny\\Contracts\\Services\\Crm\\ServiceInterface;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Exceptions\\SocialAccountTokenInvalidException;\nuse Jiminny\\Models\\Account;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Contact;\nuse Jiminny\\Models\\Lead;\nuse Jiminny\\Models\\Opportunity;\nuse Jiminny\\Models\\Participant;\nuse Jiminny\\Models\\Stage;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\ResolveTeamCrmConnection;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Psr\\Log\\LoggerInterface;\nuse Exception;\nuse Throwable;\n\nclass CrmActivityService\n{\n public function __construct(\n private readonly TeamRepository $teamRepository,\n private readonly CachedCrmServiceDecorator $decorator,\n private readonly EmailHelper $emailHelper,\n private readonly ResolveTeamCrmConnection $teamCrmResolver,\n private readonly LoggerInterface $logger,\n ) {\n }\n\n /**\n * Updates CRM data for an activity and its participants.\n *\n * NOTE: This method performs multiple database writes and should be called\n * within a transaction by the caller to ensure atomicity.\n *\n * @param Activity $activity\n * @param bool $remoteSearch\n *\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception\n */\n public function updateCrmData(\n Activity $activity,\n bool $remoteSearch = false,\n ): void {\n $crmService = null;\n $participants = $activity->getParticipants();\n $team = $activity->getTeam();\n\n $prospectSearchStrategy = ProspectSearchStrategyFactory::match($team);\n if ($prospectSearchStrategy->ignoreCrmMatchData()) {\n $this->logger->info('[CrmActivityService] Ignoring crm data because of prospect strategy', [\n 'activity_id' => $activity->getId(),\n 'strategy' => get_class($prospectSearchStrategy),\n ]);\n\n return;\n }\n\n if ($remoteSearch) {\n try {\n $crmService = $this->teamCrmResolver->resolveForTeam($team);\n } catch (SocialAccountTokenInvalidException) {\n $this->logger->warning('[CrmActivityService] CRM token expired, falling back to local search', [\n 'activity_id' => $activity->getId(),\n 'team_id' => $team->getId(),\n ]);\n }\n }\n\n $records = $this->updateParticipantsCrmData(\n team: $team,\n activity: $activity,\n participants: $participants,\n crmService: $crmService,\n );\n\n if (! empty($records)) {\n $activity->updateActivityCrmData($records);\n }\n\n $activity->refresh();\n }\n\n /**\n * @param Collection<Participant> $participants\n *\n * @throws Exception\n *\n * @return array{\n * Lead|null,\n * Account|null,\n * Opportunity|null,\n * Contact|null,\n * Stage|null,\n * string|null\n *}|array{}\n */\n private function updateParticipantsCrmData(\n Team $team,\n Activity $activity,\n Collection $participants,\n ?ServiceInterface $crmService = null,\n ): array {\n $matchedRecords = [];\n $matchedDomainRecords = [];\n\n $this->validateCrmConfiguration($activity);\n $this->decorator->setConfiguration($activity->getCrm());\n $this->decorator->setCrmService($crmService);\n\n foreach ($participants as $participant) {\n if ($this->shouldSkipParticipant($participant)) {\n continue;\n }\n\n if (! $this->shouldPerformLookup($participant, $team)) {\n $this->logger->info('[CrmActivityService] Email domain belongs to the team, skipping crm lookup', [\n 'activity_id' => $activity->getId(),\n 'team_id' => $team->getId(),\n 'email' => $participant->getEmailAddress(),\n ]);\n\n $this->attachUserIfExists($participant, $team);\n\n continue;\n }\n\n $records = $this->findCrmRecords($participant, $activity);\n\n if (! empty($records)) {\n $matchedRecords[] = $records;\n } else {\n $records = $this->findCrmDomainRecords(\n crmService: $crmService,\n participant: $participant,\n activity: $activity,\n );\n if (! empty($records)) {\n $matchedDomainRecords[] = $records;\n }\n }\n\n if (empty($records)) {\n continue;\n }\n\n try {\n $activity->updateParticipantCrmData($records, $participant);\n } catch (Throwable $ex) {\n $this->logger->error('[CrmActivityService] Failed to update participant CRM data', [\n 'activity_id' => $activity->getId(),\n 'participant_id' => $participant->getId(),\n 'exception' => $ex->getMessage(),\n ]);\n\n continue;\n }\n }\n\n $bestMatch = $this->getBestMatch(\n matchedRecords : $matchedRecords,\n matchedDomainRecords: $matchedDomainRecords,\n );\n\n $this->logger->info('[CrmActivityService] CRM matching completed', [\n 'activity_id' => $activity->getId(),\n 'participants_processed' => $participants->count(),\n 'exact_matches' => count($matchedRecords),\n 'domain_matches' => count($matchedDomainRecords),\n 'best_match_found' => ! empty($bestMatch),\n ]);\n\n return $bestMatch;\n }\n\n private function shouldPerformLookup(Participant $participant, Team $team): bool\n {\n if ($participant->hasEmailAddress()) {\n return $this->emailHelper->shouldPerformLookup($team, $participant->getEmailAddress());\n }\n\n return true;\n }\n\n private function validateCrmConfiguration(Activity $activity): void\n {\n if ($activity->getCrm() === null) {\n throw new InvalidArgumentException('Cannot find CRM configuration');\n }\n }\n\n private function getBestMatch(?array $matchedRecords, ?array $matchedDomainRecords): array\n {\n return RecordSelector::pickBestFromLists($matchedRecords, $matchedDomainRecords);\n }\n\n private function findCrmRecords(Participant $participant, Activity $activity): ?array\n {\n $records = null;\n\n if ($participant->hasEmailAddress()) {\n $records = $this->decorator->matchExactlyByEmail(\n email: $participant->getEmailAddress(),\n userId: $activity->getUser()->getId()\n );\n }\n\n if (empty($records) && $participant->getPhoneNumber() !== null) {\n $records = $this->decorator->matchByPhone(\n phone: $participant->getPhoneNumber(),\n userId: $activity->getUser()->getId(),\n );\n }\n\n if (empty($records) && $participant->getName() !== null) {\n $records = $this->decorator->matchByName(\n name: $participant->getName(),\n userId: $activity->getUser()->getId(),\n );\n }\n\n return $records;\n }\n\n private function shouldSkipParticipant(Participant $participant): bool\n {\n return $participant->hasUser();\n }\n\n private function attachUserIfExists(Participant $participant, Team $team): void\n {\n if ($participant->hasEmailAddress() === false) {\n return;\n }\n\n $user = $this->teamRepository->findActiveTeamMemberByEmail($team, $participant->getEmailAddress());\n\n if ($user instanceof User) {\n $participant->user_id = $user->getId();\n $participant->save();\n }\n }\n\n private function findCrmDomainRecords(\n ?ServiceInterface $crmService,\n Participant $participant,\n Activity $activity,\n ): array {\n if ($participant->hasEmailAddress()) {\n $this->decorator->setConfiguration($activity->getCrm());\n $this->decorator->setCrmService($crmService);\n\n $records = $this->decorator->matchByDomain(\n email: $participant->getEmailAddress(),\n userId: $activity->getUser()->getId()\n );\n if (! empty($records)) {\n return $records;\n }\n }\n\n return [];\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Crm;\n\nuse Illuminate\\Support\\Collection;\nuse Jiminny\\Contracts\\Repositories\\TeamRepository;\nuse Jiminny\\Contracts\\Services\\Crm\\ServiceInterface;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Exceptions\\SocialAccountTokenInvalidException;\nuse Jiminny\\Models\\Account;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Contact;\nuse Jiminny\\Models\\Lead;\nuse Jiminny\\Models\\Opportunity;\nuse Jiminny\\Models\\Participant;\nuse Jiminny\\Models\\Stage;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\ResolveTeamCrmConnection;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Psr\\Log\\LoggerInterface;\nuse Exception;\nuse Throwable;\n\nclass CrmActivityService\n{\n public function __construct(\n private readonly TeamRepository $teamRepository,\n private readonly CachedCrmServiceDecorator $decorator,\n private readonly EmailHelper $emailHelper,\n private readonly ResolveTeamCrmConnection $teamCrmResolver,\n private readonly LoggerInterface $logger,\n ) {\n }\n\n /**\n * Updates CRM data for an activity and its participants.\n *\n * NOTE: This method performs multiple database writes and should be called\n * within a transaction by the caller to ensure atomicity.\n *\n * @param Activity $activity\n * @param bool $remoteSearch\n *\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception\n */\n public function updateCrmData(\n Activity $activity,\n bool $remoteSearch = false,\n ): void {\n $crmService = null;\n $participants = $activity->getParticipants();\n $team = $activity->getTeam();\n\n $prospectSearchStrategy = ProspectSearchStrategyFactory::match($team);\n if ($prospectSearchStrategy->ignoreCrmMatchData()) {\n $this->logger->info('[CrmActivityService] Ignoring crm data because of prospect strategy', [\n 'activity_id' => $activity->getId(),\n 'strategy' => get_class($prospectSearchStrategy),\n ]);\n\n return;\n }\n\n if ($remoteSearch) {\n try {\n $crmService = $this->teamCrmResolver->resolveForTeam($team);\n } catch (SocialAccountTokenInvalidException) {\n $this->logger->warning('[CrmActivityService] CRM token expired, falling back to local search', [\n 'activity_id' => $activity->getId(),\n 'team_id' => $team->getId(),\n ]);\n }\n }\n\n $records = $this->updateParticipantsCrmData(\n team: $team,\n activity: $activity,\n participants: $participants,\n crmService: $crmService,\n );\n\n if (! empty($records)) {\n $activity->updateActivityCrmData($records);\n }\n\n $activity->refresh();\n }\n\n /**\n * @param Collection<Participant> $participants\n *\n * @throws Exception\n *\n * @return array{\n * Lead|null,\n * Account|null,\n * Opportunity|null,\n * Contact|null,\n * Stage|null,\n * string|null\n *}|array{}\n */\n private function updateParticipantsCrmData(\n Team $team,\n Activity $activity,\n Collection $participants,\n ?ServiceInterface $crmService = null,\n ): array {\n $matchedRecords = [];\n $matchedDomainRecords = [];\n\n $this->validateCrmConfiguration($activity);\n $this->decorator->setConfiguration($activity->getCrm());\n $this->decorator->setCrmService($crmService);\n\n foreach ($participants as $participant) {\n if ($this->shouldSkipParticipant($participant)) {\n continue;\n }\n\n if (! $this->shouldPerformLookup($participant, $team)) {\n $this->logger->info('[CrmActivityService] Email domain belongs to the team, skipping crm lookup', [\n 'activity_id' => $activity->getId(),\n 'team_id' => $team->getId(),\n 'email' => $participant->getEmailAddress(),\n ]);\n\n $this->attachUserIfExists($participant, $team);\n\n continue;\n }\n\n $records = $this->findCrmRecords($participant, $activity);\n\n if (! empty($records)) {\n $matchedRecords[] = $records;\n } else {\n $records = $this->findCrmDomainRecords(\n crmService: $crmService,\n participant: $participant,\n activity: $activity,\n );\n if (! empty($records)) {\n $matchedDomainRecords[] = $records;\n }\n }\n\n if (empty($records)) {\n continue;\n }\n\n try {\n $activity->updateParticipantCrmData($records, $participant);\n } catch (Throwable $ex) {\n $this->logger->error('[CrmActivityService] Failed to update participant CRM data', [\n 'activity_id' => $activity->getId(),\n 'participant_id' => $participant->getId(),\n 'exception' => $ex->getMessage(),\n ]);\n\n continue;\n }\n }\n\n $bestMatch = $this->getBestMatch(\n matchedRecords : $matchedRecords,\n matchedDomainRecords: $matchedDomainRecords,\n );\n\n $this->logger->info('[CrmActivityService] CRM matching completed', [\n 'activity_id' => $activity->getId(),\n 'participants_processed' => $participants->count(),\n 'exact_matches' => count($matchedRecords),\n 'domain_matches' => count($matchedDomainRecords),\n 'best_match_found' => ! empty($bestMatch),\n ]);\n\n return $bestMatch;\n }\n\n private function shouldPerformLookup(Participant $participant, Team $team): bool\n {\n if ($participant->hasEmailAddress()) {\n return $this->emailHelper->shouldPerformLookup($team, $participant->getEmailAddress());\n }\n\n return true;\n }\n\n private function validateCrmConfiguration(Activity $activity): void\n {\n if ($activity->getCrm() === null) {\n throw new InvalidArgumentException('Cannot find CRM configuration');\n }\n }\n\n private function getBestMatch(?array $matchedRecords, ?array $matchedDomainRecords): array\n {\n return RecordSelector::pickBestFromLists($matchedRecords, $matchedDomainRecords);\n }\n\n private function findCrmRecords(Participant $participant, Activity $activity): ?array\n {\n $records = null;\n\n if ($participant->hasEmailAddress()) {\n $records = $this->decorator->matchExactlyByEmail(\n email: $participant->getEmailAddress(),\n userId: $activity->getUser()->getId()\n );\n }\n\n if (empty($records) && $participant->getPhoneNumber() !== null) {\n $records = $this->decorator->matchByPhone(\n phone: $participant->getPhoneNumber(),\n userId: $activity->getUser()->getId(),\n );\n }\n\n if (empty($records) && $participant->getName() !== null) {\n $records = $this->decorator->matchByName(\n name: $participant->getName(),\n userId: $activity->getUser()->getId(),\n );\n }\n\n return $records;\n }\n\n private function shouldSkipParticipant(Participant $participant): bool\n {\n return $participant->hasUser();\n }\n\n private function attachUserIfExists(Participant $participant, Team $team): void\n {\n if ($participant->hasEmailAddress() === false) {\n return;\n }\n\n $user = $this->teamRepository->findActiveTeamMemberByEmail($team, $participant->getEmailAddress());\n\n if ($user instanceof User) {\n $participant->user_id = $user->getId();\n $participant->save();\n }\n }\n\n private function findCrmDomainRecords(\n ?ServiceInterface $crmService,\n Participant $participant,\n Activity $activity,\n ): array {\n if ($participant->hasEmailAddress()) {\n $this->decorator->setConfiguration($activity->getCrm());\n $this->decorator->setCrmService($crmService);\n\n $records = $this->decorator->matchByDomain(\n email: $participant->getEmailAddress(),\n userId: $activity->getUser()->getId()\n );\n if (! empty($records)) {\n return $records;\n }\n }\n\n return [];\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false}]...
|
-5135967619275051346
|
-8454960550680315650
|
visual_change
|
accessibility
|
NULL
|
Project: faVsco.js, menu
#12121 on JY-20963-fix-im Project: faVsco.js, menu
#12121 on JY-20963-fix-import-on-deleted-entity, menu
Start Listening for PHP Debug Connections
ServiceTest
Run 'ServiceTest'
Debug 'ServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
22
5
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Crm;
use Illuminate\Support\Collection;
use Jiminny\Contracts\Repositories\TeamRepository;
use Jiminny\Contracts\Services\Crm\ServiceInterface;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Models\Account;
use Jiminny\Models\Activity;
use Jiminny\Models\Contact;
use Jiminny\Models\Lead;
use Jiminny\Models\Opportunity;
use Jiminny\Models\Participant;
use Jiminny\Models\Stage;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Services\ResolveTeamCrmConnection;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Psr\Log\LoggerInterface;
use Exception;
use Throwable;
class CrmActivityService
{
public function __construct(
private readonly TeamRepository $teamRepository,
private readonly CachedCrmServiceDecorator $decorator,
private readonly EmailHelper $emailHelper,
private readonly ResolveTeamCrmConnection $teamCrmResolver,
private readonly LoggerInterface $logger,
) {
}
/**
* Updates CRM data for an activity and its participants.
*
* NOTE: This method performs multiple database writes and should be called
* within a transaction by the caller to ensure atomicity.
*
* @param Activity $activity
* @param bool $remoteSearch
*
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception
*/
public function updateCrmData(
Activity $activity,
bool $remoteSearch = false,
): void {
$crmService = null;
$participants = $activity->getParticipants();
$team = $activity->getTeam();
$prospectSearchStrategy = ProspectSearchStrategyFactory::match($team);
if ($prospectSearchStrategy->ignoreCrmMatchData()) {
$this->logger->info('[CrmActivityService] Ignoring crm data because of prospect strategy', [
'activity_id' => $activity->getId(),
'strategy' => get_class($prospectSearchStrategy),
]);
return;
}
if ($remoteSearch) {
try {
$crmService = $this->teamCrmResolver->resolveForTeam($team);
} catch (SocialAccountTokenInvalidException) {
$this->logger->warning('[CrmActivityService] CRM token expired, falling back to local search', [
'activity_id' => $activity->getId(),
'team_id' => $team->getId(),
]);
}
}
$records = $this->updateParticipantsCrmData(
team: $team,
activity: $activity,
participants: $participants,
crmService: $crmService,
);
if (! empty($records)) {
$activity->updateActivityCrmData($records);
}
$activity->refresh();
}
/**
* @param Collection<Participant> $participants
*
* @throws Exception
*
* @return array{
* Lead|null,
* Account|null,
* Opportunity|null,
* Contact|null,
* Stage|null,
* string|null
*}|array{}
*/
private function updateParticipantsCrmData(
Team $team,
Activity $activity,
Collection $participants,
?ServiceInterface $crmService = null,
): array {
$matchedRecords = [];
$matchedDomainRecords = [];
$this->validateCrmConfiguration($activity);
$this->decorator->setConfiguration($activity->getCrm());
$this->decorator->setCrmService($crmService);
foreach ($participants as $participant) {
if ($this->shouldSkipParticipant($participant)) {
continue;
}
if (! $this->shouldPerformLookup($participant, $team)) {
$this->logger->info('[CrmActivityService] Email domain belongs to the team, skipping crm lookup', [
'activity_id' => $activity->getId(),
'team_id' => $team->getId(),
'email' => $participant->getEmailAddress(),
]);
$this->attachUserIfExists($participant, $team);
continue;
}
$records = $this->findCrmRecords($participant, $activity);
if (! empty($records)) {
$matchedRecords[] = $records;
} else {
$records = $this->findCrmDomainRecords(
crmService: $crmService,
participant: $participant,
activity: $activity,
);
if (! empty($records)) {
$matchedDomainRecords[] = $records;
}
}
if (empty($records)) {
continue;
}
try {
$activity->updateParticipantCrmData($records, $participant);
} catch (Throwable $ex) {
$this->logger->error('[CrmActivityService] Failed to update participant CRM data', [
'activity_id' => $activity->getId(),
'participant_id' => $participant->getId(),
'exception' => $ex->getMessage(),
]);
continue;
}
}
$bestMatch = $this->getBestMatch(
matchedRecords : $matchedRecords,
matchedDomainRecords: $matchedDomainRecords,
);
$this->logger->info('[CrmActivityService] CRM matching completed', [
'activity_id' => $activity->getId(),
'participants_processed' => $participants->count(),
'exact_matches' => count($matchedRecords),
'domain_matches' => count($matchedDomainRecords),
'best_match_found' => ! empty($bestMatch),
]);
return $bestMatch;
}
private function shouldPerformLookup(Participant $participant, Team $team): bool
{
if ($participant->hasEmailAddress()) {
return $this->emailHelper->shouldPerformLookup($team, $participant->getEmailAddress());
}
return true;
}
private function validateCrmConfiguration(Activity $activity): void
{
if ($activity->getCrm() === null) {
throw new InvalidArgumentException('Cannot find CRM configuration');
}
}
private function getBestMatch(?array $matchedRecords, ?array $matchedDomainRecords): array
{
return RecordSelector::pickBestFromLists($matchedRecords, $matchedDomainRecords);
}
private function findCrmRecords(Participant $participant, Activity $activity): ?array
{
$records = null;
if ($participant->hasEmailAddress()) {
$records = $this->decorator->matchExactlyByEmail(
email: $participant->getEmailAddress(),
userId: $activity->getUser()->getId()
);
}
if (empty($records) && $participant->getPhoneNumber() !== null) {
$records = $this->decorator->matchByPhone(
phone: $participant->getPhoneNumber(),
userId: $activity->getUser()->getId(),
);
}
if (empty($records) && $participant->getName() !== null) {
$records = $this->decorator->matchByName(
name: $participant->getName(),
userId: $activity->getUser()->getId(),
);
}
return $records;
}
private function shouldSkipParticipant(Participant $participant): bool
{
return $participant->hasUser();
}
private function attachUserIfExists(Participant $participant, Team $team): void
{
if ($participant->hasEmailAddress() === false) {
return;
}
$user = $this->teamRepository->findActiveTeamMemberByEmail($team, $participant->getEmailAddress());
if ($user instanceof User) {
$participant->user_id = $user->getId();
$participant->save();
}
}
private function findCrmDomainRecords(
?ServiceInterface $crmService,
Participant $participant,
Activity $activity,
): array {
if ($participant->hasEmailAddress()) {
$this->decorator->setConfiguration($activity->getCrm());
$this->decorator->setCrmService($crmService);
$records = $this->decorator->matchByDomain(
email: $participant->getEmailAddress(),
userId: $activity->getUser()->getId()
);
if (! empty($records)) {
return $records;
}
}
return [];
}
}...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
86386
|
2962
|
6
|
2026-05-28T14:06:48.968914+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-28/1779 /Users/lukas/.screenpipe/data/data/2026-05-28/1779977208968_m1.jpg...
|
PhpStorm
|
faVsco.js – CrmActivityService.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
#12121 on JY-20963-fix-im Project: faVsco.js, menu
#12121 on JY-20963-fix-import-on-deleted-entity, menu
Start Listening for PHP Debug Connections
ServiceTest
Run 'ServiceTest'
Debug 'ServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
22...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"#12121 on JY-20963-fix-import-on-deleted-entity, menu","depth":5,"on_screen":true,"help_text":"Pull request #12121 exists for current branch JY-20963-fix-import-on-deleted-entity, but pull request details loading failed","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"ServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'ServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'ServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"22","depth":4,"on_screen":true,"role_description":"text"}]...
|
-676106581489811936
|
-8706353842801636416
|
visual_change
|
hybrid
|
NULL
|
Project: faVsco.js, menu
#12121 on JY-20963-fix-im Project: faVsco.js, menu
#12121 on JY-20963-fix-import-on-deleted-entity, menu
Start Listening for PHP Debug Connections
ServiceTest
Run 'ServiceTest'
Debug 'ServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
22
JAlActivity MonitorFileEditViewWindowHelpActivity MonitorAll ProcessesProcess Name% CPUPhpStormkernel_taskVirtual Machine Service for DockerreplaydWindowServerscreenpipelaunchservicesdFirefoxCP Isolated Web Contentlanguage_server_macos_armSlack Helper (Renderer)coreaudiodlogdSlack HelperActivity MonitoriTerm2launchdmds_storesWispr Flow Helper (Renderer)SlackFirefoxCP Isolated Web ContentBitwardenWispr FlowClaudeFirefoxCP Isolated Web Contentio.kandji.KandjiAgent.ESF-ExtensionNotion Helper (Renderer)FirefoxCP Isolated Web ContentControl Centre216,9191,2115,176,637,319,912,010,56,46,15,14,84,74,64,54,33,63,02,52,42,32,12,02,01,91,71,61,6CPU Time2:31:45,5522:46:39,481:51:50,295:41:51,737:50:07,613:34:41,481:00:03,2433:36,567:24,9657:22,1556:59,7415:27,1314:23,246:45,051:03:27,1318:34,941:03:34,393:44,1713:13,8515:12,422:03,232:35,4028:20,6617:15,0231:20,1923:56,3123:20,2919:46,22System:User:Idle:Threads % C8• Thu 28 May 17:06:48CPUMemoryEnergyDiskNetworkIdle Wake-UpsKind % GPUAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleApple41,79%57,75%0,47%8,80,00,00,049,00,00,00,00,00,00,00,05,10,00,50,00,00,00,00,00,00,00,00,00,00,00,00,0CPU LOADThreads:Processes:GPU Time7:46,440,000,000,003:19:59,845:03,900,000,000,000,000,000,006:05,880,0019,300,000,000,000,050,000,000,000,000,000,000,000,000,04PID (ah)Userlukasrootlukaslukas_windowserverlukasrootlukaslukaslukas_coreaudiodrootlukaslukaslukasrootrootlukaslukaslukaslukaslukaslukaslukasrootlukaslukaslukasSearch...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
86385
|
2963
|
5
|
2026-05-28T14:06:36.086316+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-28/1779 /Users/lukas/.screenpipe/data/data/2026-05-28/1779977196086_m2.jpg...
|
PhpStorm
|
faVsco.js – CrmActivityService.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
#12121 on JY-20963-fix-im Project: faVsco.js, menu
#12121 on JY-20963-fix-import-on-deleted-entity, menu
Start Listening for PHP Debug Connections...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"#12121 on JY-20963-fix-import-on-deleted-entity, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.11569149,"height":0.025538707},"on_screen":true,"help_text":"Pull request #12121 exists for current branch JY-20963-fix-import-on-deleted-entity, but pull request details loading failed","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.85638297,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
8569994499127135030
|
-3986301007428073024
|
click
|
hybrid
|
NULL
|
Project: faVsco.js, menu
#12121 on JY-20963-fix-im Project: faVsco.js, menu
#12121 on JY-20963-fix-import-on-deleted-entity, menu
Start Listening for PHP Debug Connections
rapstomViewCoocWindowFV faVsco.|s ~#12121 on JY-20963-fx-l= custom.logSrminovsioenthoe?l>mutkewolcnnedvityCimbald.ong© CrmActivityService.php xC Activity.phpolcom.pnioA console (EUTA console [STAGING"Webhookc Batchsynceo ecror.orrcRatchSuncRedeCanrce.oclass cruacevzcyservice822×5^ y 699© Clent.phoc elosedbeaistdcssermiouDealFieldsService.pho© DecorateActivity.phgpuoLIetuncczon uodareum vacaoreaochinidons.oneoreclyseconvere.one(© HubspotClientinterface.pl© HubspotTokenManager.pt© PayloadBuilder.phoc) Remotcermocrec.wan .oulResponseNormalize.phoc) service.ono© SyncFieidAction.phoc Suncrelateohcwiywanx© WebhookSvncBatchProcelnteotationtro@ listeners→lweradatal→p nedrivesalestorce• FaldeMi Onnortun tMatchenOpportunitySyncStrategyProspectSearchStrategy>Ih SemicetrairdeCant nhr© DecorateActivity.phpDeleteObiectsTrait.phpe Faldnafoitione mho© PayloadBuilder.pho(© Profile.phpe ouewer der.onp© QueryHandler.pho© Querviterator.ohc© QueryResuits.pho©) Service.oho© SyncBatchRedisService.otla Traitsg Basecllent.phpCBaseService.choc countrvcoderesolver ohdCrmActivitvProviderinteorateClermciv tsarioe.ooo© CrmConficurationSattinosSer 105scracery a> dec class sorospectsearchstrateay1):if (SrenoteSeanch) !catchSocalAccountokentinvalsidaxceottonSthis->loggen->wacning(*(GonActävitySerwice) CPM token exoired. falling back711activity_id' => Sactivity->get.dO.team_id' => Stean-›getidO,Srecords = Sthis-›updateParticipantsCrnData(ceon, ceolactivity: Sactivityparcacaponts, sudr ecaodntscrnService: ScrmServiceif enpty(Srecords))Sactivity->updateActivityCrnData(Srecords):sacazvacy->retresho"ooren touueee oncPara ahandsoorelcroontsthrows Eycene.on* Areturn accaud737leadlnutAccount nualOpportunitylnultContactnuhStagelnutt,strsnalnuh*; array.fclermoniac shaes wero(@ nelanttDrosnaetSaarchStrate 1051 usageprivate function updateParticipantsCrnDatalmiomwAND a.created at > DATE SUB(NOWO, INTERVAL 38 DAY)GROUP RY u.sd urenaitl, u.nane. M. softahone nunberORDER BY sms count DESCOYSAAATARAselect * from teans where 1d = 1select * fron rolesCONCATu.id, CASE WHEN u.id = t.owner id THEN " (onner))' ELSE ** END) AS user idt.oanerid FROM social accounts saJOIN users u on u.id = sa.sociable.idJOIN teans t1.n<->1: on taid = u.tean_idTHERE u,tean sid = 1117 and saroroviden = "hubspot*.SELECT * FROM activities WHERE uuid_to_bin('8024fffb-2df7-4017-91f4-d9f896850248') = uuid; # 79933459 YESSELECT + FPOM activitios THERE unid to binf+[CREDIT_CARD]-9276-464d22-8185e*) = uusde # 80186192 NGSELECT * FROM crn_configurations WHERE id = 1053:SSLECT + FROMselect * from playbooks where 1a = s47x:playbook_categories where id = 43783;select + Eron dlavbook cateoonses whene nilavhook5d 5673)eon &eld values where com Sield 3d = 6502/%SELEOT + EPOM enn Ejeldi date foJOIN cra_fields f ON fd.crn_field_id = f.jd# JOIN activities a ON fd.activity id = a.10# AND f.crn provider id = 'hs activity type':select * fron activities where user_id IN (7168, 18688) and created at > *2826-85-22' order by id descaselect * fron users where tean_id = 1 and id IN (18688. 13934, 7160)=select * fron activities where user id = 7169 order by id desc Linit 10:select * fron accounts wheretean_id=1 and nane= "Colunns":select * fron usens where nane Like '%Subrax": # 31954. 1117select + fron activity sparches where usen 1d = 319541sparch &ilters where actiuity seanch ia TN (9998), BROR)):Inu co moy tuo.seServiceTest+0.Cecsdales Orcnnworceoeionh1/ 0rosers/ tuxos/2Toreach (Spartzciponts as sparcicipant)Srecords = Sthis-s1indCreRecords(Sparticipant, Sactivity):coly piccororritonter tiit w::wwinrecorddlierarnch. kerserth/l & This nay call Salesforce::matchByDomain()Step 7: Domain Search tnggers opportunity synoboin senrh eirs necount ens drinee" withi relaired oncorunt/l To get opportunity details, it calls:Scraservicesyncopponontylscrrrovzoerso):l colts aaporcopporcunstyl)Step 8: The bua - importo pportunity creates temporary Recoroaond/Users/luxas/1m1nny//app/app/Services/crm/Salestorce/Serv1cc.php:1430-1448oriivate tunctionimoort@sportunt tylScr iatal: 20000rtuntvMxino early chelete chack8a. Restore fron trash (tine 1430)SthcesrestorAcyTrachwenttylsthicescontoesomoortuntlecol. Ser DataletoenrTuny na sests wich valdldio. l0anes1430583%Sopportunity = Sthsooruaeolnoalasosaat RosborunitweseVADio la.or7hosy5yiumoor edusin thesid edine42sthices roort DocortuntverFlel bata SerDatar SerBields, Conoortunt tyesto)eM8ar Noh celeteit ansin itine 1444Sthis-shandle0bjectDeletion(Sopportunity, ScrnData):Sopportuntty=trashedIl Methad returs null, ButAat anuthientuaRe:dowht.%t4 space...
|
86383
|
NULL
|
NULL
|
NULL
|
|
86384
|
2962
|
5
|
2026-05-28T14:06:33.610782+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-28/1779 /Users/lukas/.screenpipe/data/data/2026-05-28/1779977193610_m1.jpg...
|
PhpStorm
|
faVsco.js – CrmActivityService.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
JAlActivity MonitorFileEditViewWindowHelpActivity JAlActivity MonitorFileEditViewWindowHelpActivity MonitorAll ProcessesProcess Name% CPUVirtual Machine Service for Dockerkernel_taskPhpStormreplaydWindowServerdeletedscreenpipeFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentlaunchservicesdiTerm2coreaudiodFirefoxSlack HelperActivity Monitorlanguage_server_macos_armSlack Helper (Renderer)Wispr Flow Helper (Renderer)FirefoxCP Isolated Web Contentcontainermanagerdio.kandji.KandjiAgent.eSF-ExtensionClaudeFirefoxCP Isolated Web ContentWispr FlowFirefoxCP Isolated Web ContentDocker168,5152,177,652,127,226,718,115,411,99,68,26,05,54,64,64,43,93,93,82,92,62,42,42,21,91,91,71,5CPU Time1:51:32,1922:46:10,712:31:18,175:41:42,717:50:00,611:23,223:34:34,1040:32,8217:14,585:57,1433:35,051:00:02,191:03:26,3656:58,931:46:43,2314:22,496:44,347:23,4857:21,443:43,7015:11,9910,9531:19,9428:20,044:12,602:35,0712:21,201:25,30System:User:Idle:Threads CPUMemoryEnergyDiskNetworkIdle Wake-UpsKind% GPU280Apple AppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleApple0,00,06,80,033,90,00,00,00,00,00,00,00,30,00,03,50,00,00,00,00,00,00,00,00,00,00,00,041,87%45,74%12,39%CPU LOADThreads:Processes:GPU Time0,000,007:45,930,003:19:57,360,005:03,900,000,000,000,000,0019,280,000,046:05,650,000,000,000,000,000,000,000,000,000,000,000,00‹ >0 lhlPID % C8• Thu 28 May 17:06:33SearchUserlukasrootlukaslukas_windowserverlukaslukaslukaslukaslukaslukasrootlukas_coreaudiodlukaslukaslukaslukaslukaslukaslukaslukasrootlukaslukaslukaslukaslukas...
|
NULL
|
-8997944358730767784
|
NULL
|
visual_change
|
ocr
|
NULL
|
JAlActivity MonitorFileEditViewWindowHelpActivity JAlActivity MonitorFileEditViewWindowHelpActivity MonitorAll ProcessesProcess Name% CPUVirtual Machine Service for Dockerkernel_taskPhpStormreplaydWindowServerdeletedscreenpipeFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentlaunchservicesdiTerm2coreaudiodFirefoxSlack HelperActivity Monitorlanguage_server_macos_armSlack Helper (Renderer)Wispr Flow Helper (Renderer)FirefoxCP Isolated Web Contentcontainermanagerdio.kandji.KandjiAgent.eSF-ExtensionClaudeFirefoxCP Isolated Web ContentWispr FlowFirefoxCP Isolated Web ContentDocker168,5152,177,652,127,226,718,115,411,99,68,26,05,54,64,64,43,93,93,82,92,62,42,42,21,91,91,71,5CPU Time1:51:32,1922:46:10,712:31:18,175:41:42,717:50:00,611:23,223:34:34,1040:32,8217:14,585:57,1433:35,051:00:02,191:03:26,3656:58,931:46:43,2314:22,496:44,347:23,4857:21,443:43,7015:11,9910,9531:19,9428:20,044:12,602:35,0712:21,201:25,30System:User:Idle:Threads CPUMemoryEnergyDiskNetworkIdle Wake-UpsKind% GPU280Apple AppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleApple0,00,06,80,033,90,00,00,00,00,00,00,00,30,00,03,50,00,00,00,00,00,00,00,00,00,00,00,041,87%45,74%12,39%CPU LOADThreads:Processes:GPU Time0,000,007:45,930,003:19:57,360,005:03,900,000,000,000,000,0019,280,000,046:05,650,000,000,000,000,000,000,000,000,000,000,000,00‹ >0 lhlPID % C8• Thu 28 May 17:06:33SearchUserlukasrootlukaslukas_windowserverlukaslukaslukaslukaslukaslukasrootlukas_coreaudiodlukaslukaslukaslukaslukaslukaslukaslukasrootlukaslukaslukaslukaslukas...
|
86382
|
NULL
|
NULL
|
NULL
|
|
86383
|
2963
|
4
|
2026-05-28T14:06:31.868649+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-28/1779 /Users/lukas/.screenpipe/data/data/2026-05-28/1779977191868_m2.jpg...
|
iTerm2
|
NULL
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
rapstomProinet vViewNeweNNCCoocWindowFV faVsco.|s rapstomProinet vViewNeweNNCCoocWindowFV faVsco.|s ~#12121 on JY-20963-fx-lAdtwtyontroller.ong=custom.logSF [minny@localhost)h Wahboalwolcnnedvilycimbato.ong(©) Activity,php xwlcomlpipA console (EUTA console [STAGING"oaccoundoneAcuvil sond© Address.phoC AiPromot.phpCAulomaleo keoottoneC AutomatedReportResult.ohocCalenoaroncallmeeonclass Acavicy excenos nodel anplenencs20382078cCoacnoreeooeen© CoachingFeedbackVisibllity.php 2032c) coach nosection choc) coach nosectioneritenonFeed or 208C commentAostract ono8 commenti ntertace onoC contact ond© Device.ohecSmalllMacesed dooleCanarie oromot oorg Group.phpC Inbox.phpC InboxEmail.phpc InboxEmailBatch.phpC Invitation.php© JobLog.php© JobTitle.php© Language.php© LanguageDialect.php© Lead.php© MobileSetting.phpC. Model.ohoC) Moment oholC Nudge oho© NudgeRun .oho© Particinant.ohdowanhertC Permission.ohoC PhoneNumoer. ono© PlaybackTheme.choc Pavbook ond©. PlaybookCategory.ohec [EMAIL] Rec on oho©. Role-phoe ealdthsnadsvont nhrA connoGrnun nha e Coceion nhmA4AXA8VPY103 D4AV=79dpubLze functzon updaterartaczpantirabata(array Srecoras, Pertzcapant Spartzczpant): voll 70s* Updates activity crh data* Вnarae acnauilLeadpnoee,Account|null,OpportunitylnullContact|null,sagelnoee,stringlnulz*, Srecords=713ublic function undateActivityCraData(array Srecords): voidrxdch the necoros,715717oeat saccount sodoorUhtyr deohaetosattreconosSresover = sthis-›gerUodarenataraResover oSstrateay = Sreso ver-›reso veforacevirylstead, Scontact Saccount)if (Sstrategy == UpdateConDataßvStrategv.:Lead)Sstrategy in UpdateCrmDataßyStrategy:: Lead) checkina me donit orente a mi$724if (Sthis->account_id aa= null && Sthis-›contact_id a== null && Sthis->lead_10m€ 725Sthis-> ead.d= Sleado>dh34 (Sthisesstade fa aue null fe Sstane) !thnsosstace e SetaceoseeE732elseif (Sstrategy == UpdateCraDataByStrategy::Contact) 4ao (lstresey = pestecrmesaestrseay: comest) e ne terit erate nteCthiecslond id = oull.if (Sthis->stage && Sthis->stage-›getType() === Stage:: TYPE_LEAD) €Sthis-›stage_id = null;if (Saccount &s Saccount->id laz Sthis->account.id)thiersnooount iar Coccmunt.tidsif (Soppontunity fs Sthis-sopportunity id fas Sopportunity-sid) $wtwttwiew outreoneetxminutas.sndimiamyAND a.created at > DATE SUB(NOWO, INTERVAL 38 DAY)GROUP RY u.sd urenaitl, u.nane. M. softahone nunberORDER BY sms count DESC045 A1 A41 Y 66 Aselect * from teans where 1d = 1select * fron rolesSELECTCONCATu.id, CASE WHEN u.id = t.owner id THEN " (onner' ELSE ** END) AS user idt.oanerid FROM social accounts s.JOIN users u on u.id = sa.sociable_idJOIN teans t1.n<->1: on taid = u.tean_idTHERE u,tean sid = 1117 and saroroviden = "hubspot*.SELENFRONNOOMICTOSTHERE MUSGTOMML8874020-2012048109104-09:896858748 E WUSA: B79983459 1ESSELEN FRUNACAMICIOS HERE MISGA mnlysSyoeu-8e9uuiooeeuconasiisee wusd:881801197 11eSELECT * FROM Crn_configurations WHERE id = 1053;SELECT * FROM teansHERE SOTIWYselect * fron users where id = 30249;select * from playbooks where 10 = 5473select * fron playbook_categories where id = 43783;select + Eron dlavbook cateoonses where nilavhook 5d 56731select * fron crn_fields where id = 659242;select + Eron eon 6eld values where com Seid 3d = 650242)SSIEN + FROM Co 45e1d data 4.# JOIN cra fields + ON fd.crn field id = f.ic*JOIN activities a ON fd.activity id = a.10WHERE actávity_$d = 79933459# AND f.crn provider id = 'hs activity type':101SELECT * FROM activity-nessages:select * fron text_relays where created at > *2826-85-01°:select * fron activities where userid IN (7168, 18688) and created at > *2826-85-22' order by id descaselect * fron users where tean_id = 1 and id IN (18688, 13934, 7160):select * fron activities where user id = 7169 order by id desc Linit 10:select * fron accounts wheretean_id=1 and nane= "Colunns":select * fron usens where nane Like '%Subrax": # 31954. 1117sellect * on actuity searches where lusen Oe XHRSAselect + fron actiwity sparch &iltens where actiuity seanch ia TN (9998). BROR)) :Inu co moy twuoisServiceTest+0.Cecsdales Orceancworceoeionh1/ 0rosers/ tuxos/2Toreach (Sparticiponts as spariicipant)Srecords = Sthis-s1indCreRecords(Sparticipant, Sactivity):coly piccoroKrionter thieetw.wwtinrecorddierSaruich: kereridh/l & This nay call Salesforce::matchByDomain()Step 7: Domain Search tnggers opportunity synoboiin senrth eirs necoun lens diinc" withi inelmired oncortuntIl To get opportunity details, it calls:Scraservicesyncopponontylscrrrovzoerso):# couts aaporcopportunztyl)Step 8: The bua - importo pportunity creates temporary Recoroaond/Users/lukas/1m1nny/app/app/Services/crm/Salestorce/Serv1cc.php:1430-1448oriivate tunctionimoort@sportunt tylScr iatal: 20000rtuntvVxino early chelete chack8a. Restore fron trash (tine 1430)SthcesrestorAcyTrachwEnttylSthisescontoesomoortintnsol. Ser Dataletaenruny no ests wich valdld0i0.0anes34309433Sopportunity = SthswoWrtreucreonowloer1Dcreudeonoakasosaat RosborunitweseVADio la.or7hosy5yiamoor edtusin thesin euinetysthices oort DocortuntyerFel Data (Ser Datar SorBtelds, Conoortunt tyestd)eM8ar Noh celeteit ansin itine 1444Sthis-shandle0bjectDeletion(Sopportunity, ScrnData):if (Sopportunity=trashed()) 4Il Methad returs null, ButAat anuthientua"eoohKtwodturTasoe95hireh*4 space...
|
NULL
|
6428897811063916844
|
NULL
|
click
|
ocr
|
NULL
|
rapstomProinet vViewNeweNNCCoocWindowFV faVsco.|s rapstomProinet vViewNeweNNCCoocWindowFV faVsco.|s ~#12121 on JY-20963-fx-lAdtwtyontroller.ong=custom.logSF [minny@localhost)h Wahboalwolcnnedvilycimbato.ong(©) Activity,php xwlcomlpipA console (EUTA console [STAGING"oaccoundoneAcuvil sond© Address.phoC AiPromot.phpCAulomaleo keoottoneC AutomatedReportResult.ohocCalenoaroncallmeeonclass Acavicy excenos nodel anplenencs20382078cCoacnoreeooeen© CoachingFeedbackVisibllity.php 2032c) coach nosection choc) coach nosectioneritenonFeed or 208C commentAostract ono8 commenti ntertace onoC contact ond© Device.ohecSmalllMacesed dooleCanarie oromot oorg Group.phpC Inbox.phpC InboxEmail.phpc InboxEmailBatch.phpC Invitation.php© JobLog.php© JobTitle.php© Language.php© LanguageDialect.php© Lead.php© MobileSetting.phpC. Model.ohoC) Moment oholC Nudge oho© NudgeRun .oho© Particinant.ohdowanhertC Permission.ohoC PhoneNumoer. ono© PlaybackTheme.choc Pavbook ond©. PlaybookCategory.ohec [EMAIL] Rec on oho©. Role-phoe ealdthsnadsvont nhrA connoGrnun nha e Coceion nhmA4AXA8VPY103 D4AV=79dpubLze functzon updaterartaczpantirabata(array Srecoras, Pertzcapant Spartzczpant): voll 70s* Updates activity crh data* Вnarae acnauilLeadpnoee,Account|null,OpportunitylnullContact|null,sagelnoee,stringlnulz*, Srecords=713ublic function undateActivityCraData(array Srecords): voidrxdch the necoros,715717oeat saccount sodoorUhtyr deohaetosattreconosSresover = sthis-›gerUodarenataraResover oSstrateay = Sreso ver-›reso veforacevirylstead, Scontact Saccount)if (Sstrategy == UpdateConDataßvStrategv.:Lead)Sstrategy in UpdateCrmDataßyStrategy:: Lead) checkina me donit orente a mi$724if (Sthis->account_id aa= null && Sthis-›contact_id a== null && Sthis->lead_10m€ 725Sthis-> ead.d= Sleado>dh34 (Sthisesstade fa aue null fe Sstane) !thnsosstace e SetaceoseeE732elseif (Sstrategy == UpdateCraDataByStrategy::Contact) 4ao (lstresey = pestecrmesaestrseay: comest) e ne terit erate nteCthiecslond id = oull.if (Sthis->stage && Sthis->stage-›getType() === Stage:: TYPE_LEAD) €Sthis-›stage_id = null;if (Saccount &s Saccount->id laz Sthis->account.id)thiersnooount iar Coccmunt.tidsif (Soppontunity fs Sthis-sopportunity id fas Sopportunity-sid) $wtwttwiew outreoneetxminutas.sndimiamyAND a.created at > DATE SUB(NOWO, INTERVAL 38 DAY)GROUP RY u.sd urenaitl, u.nane. M. softahone nunberORDER BY sms count DESC045 A1 A41 Y 66 Aselect * from teans where 1d = 1select * fron rolesSELECTCONCATu.id, CASE WHEN u.id = t.owner id THEN " (onner' ELSE ** END) AS user idt.oanerid FROM social accounts s.JOIN users u on u.id = sa.sociable_idJOIN teans t1.n<->1: on taid = u.tean_idTHERE u,tean sid = 1117 and saroroviden = "hubspot*.SELENFRONNOOMICTOSTHERE MUSGTOMML8874020-2012048109104-09:896858748 E WUSA: B79983459 1ESSELEN FRUNACAMICIOS HERE MISGA mnlysSyoeu-8e9uuiooeeuconasiisee wusd:881801197 11eSELECT * FROM Crn_configurations WHERE id = 1053;SELECT * FROM teansHERE SOTIWYselect * fron users where id = 30249;select * from playbooks where 10 = 5473select * fron playbook_categories where id = 43783;select + Eron dlavbook cateoonses where nilavhook 5d 56731select * fron crn_fields where id = 659242;select + Eron eon 6eld values where com Seid 3d = 650242)SSIEN + FROM Co 45e1d data 4.# JOIN cra fields + ON fd.crn field id = f.ic*JOIN activities a ON fd.activity id = a.10WHERE actávity_$d = 79933459# AND f.crn provider id = 'hs activity type':101SELECT * FROM activity-nessages:select * fron text_relays where created at > *2826-85-01°:select * fron activities where userid IN (7168, 18688) and created at > *2826-85-22' order by id descaselect * fron users where tean_id = 1 and id IN (18688, 13934, 7160):select * fron activities where user id = 7169 order by id desc Linit 10:select * fron accounts wheretean_id=1 and nane= "Colunns":select * fron usens where nane Like '%Subrax": # 31954. 1117sellect * on actuity searches where lusen Oe XHRSAselect + fron actiwity sparch &iltens where actiuity seanch ia TN (9998). BROR)) :Inu co moy twuoisServiceTest+0.Cecsdales Orceancworceoeionh1/ 0rosers/ tuxos/2Toreach (Sparticiponts as spariicipant)Srecords = Sthis-s1indCreRecords(Sparticipant, Sactivity):coly piccoroKrionter thieetw.wwtinrecorddierSaruich: kereridh/l & This nay call Salesforce::matchByDomain()Step 7: Domain Search tnggers opportunity synoboiin senrth eirs necoun lens diinc" withi inelmired oncortuntIl To get opportunity details, it calls:Scraservicesyncopponontylscrrrovzoerso):# couts aaporcopportunztyl)Step 8: The bua - importo pportunity creates temporary Recoroaond/Users/lukas/1m1nny/app/app/Services/crm/Salestorce/Serv1cc.php:1430-1448oriivate tunctionimoort@sportunt tylScr iatal: 20000rtuntvVxino early chelete chack8a. Restore fron trash (tine 1430)SthcesrestorAcyTrachwEnttylSthisescontoesomoortintnsol. Ser Dataletaenruny no ests wich valdld0i0.0anes34309433Sopportunity = SthswoWrtreucreonowloer1Dcreudeonoakasosaat RosborunitweseVADio la.or7hosy5yiamoor edtusin thesin euinetysthices oort DocortuntyerFel Data (Ser Datar SorBtelds, Conoortunt tyestd)eM8ar Noh celeteit ansin itine 1444Sthis-shandle0bjectDeletion(Sopportunity, ScrnData):if (Sopportunity=trashed()) 4Il Methad returs null, ButAat anuthientua"eoohKtwodturTasoe95hireh*4 space...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
86382
|
2962
|
4
|
2026-05-28T14:06:30.369781+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-28/1779 /Users/lukas/.screenpipe/data/data/2026-05-28/1779977190369_m1.jpg...
|
PhpStorm
|
faVsco.js – Activity.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
#12121 on JY-20963-fix-im Project: faVsco.js, menu
#12121 on JY-20963-fix-import-on-deleted-entity, menu
Start Listening for PHP Debug Connections
ServiceTest
Run 'ServiceTest'
Debug 'ServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"#12121 on JY-20963-fix-import-on-deleted-entity, menu","depth":5,"on_screen":true,"help_text":"Pull request #12121 exists for current branch JY-20963-fix-import-on-deleted-entity, but pull request details loading failed","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"ServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'ServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'ServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
6806313800532383212
|
-8129894190043395130
|
visual_change
|
hybrid
|
NULL
|
Project: faVsco.js, menu
#12121 on JY-20963-fix-im Project: faVsco.js, menu
#12121 on JY-20963-fix-import-on-deleted-entity, menu
Start Listening for PHP Debug Connections
ServiceTest
Run 'ServiceTest'
Debug 'ServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Activity MonitorFileEditViewWindowHelpActivity MonitorAll ProcessesProcess Name% CPUkernel_taskVirtual Machine Service for DockerPhpStormreplayddeletedWindowServerscreenpipeFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentFirefoxcoreaudiodiTerm2FirefoxCP Isolated Web ContentSlack HelperFirefoxCP Isolated Web Contentlaunchservicesdlanguage_server_macos_armActivity MonitorSlack Helper (Renderer)containermanagerdio.kandji.KandjiAgent.ESF-ExtensionWispr Flow Helper (Renderer)FirefoxCP Isolated Web ContentFirefoxCP Isolated Web Contenttccd183,3163,387,435,923,719,017,59,99,48,07,86,76,35,65,55,24,64,54,54,24,03,73,42,92,92,52,42,4CPU Time22:46:02,701:51:23,322:31:14,085:41:39,961:21,827:49:59,173:34:33,1540:32,0133:34,6117:13,955:56,6312:21,111:46:42,9956:58,681:03:26,0711:16,6614:22,264:48,241:00:01,877:23,276:44,1357:21,2410,8231:19,823:43,5515:11,869:44,578:35,69System:User:Idle:Threads ‹ >0 lhl100% C8• Thu 28 May 17:06:29CPUMemoryEnergyDiskNetworkIdle Wake-UpsKind % GPUAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleApple0,00,09,60,00,042,40,00,00,00,00,00,00,00,00,40,04,10,00,00,00,00,00,00,00,00,00,00,031,20%30,43%38,36%CPU LOADThreads:Processes:SearchGPU Time0,000,007:45,930,000,003:19:56,885:03,900,000,000,000,000,000,040,0019,270,006:05,560,000,000,000,000,000,000,000,000,000,000,00PID Userrootlukaslukaslukaslukas_windowserverlukaslukaslukaslukaslukaslukaslukas_coreaudiodlukaslukaslukaslukasrootlukaslukaslukaslukasrootlukaslukaslukaslukas...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
86381
|
2963
|
3
|
2026-05-28T14:06:29.746544+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-28/1779 /Users/lukas/.screenpipe/data/data/2026-05-28/1779977189746_m2.jpg...
|
PhpStorm
|
faVsco.js – Activity.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
rapstomProinet vViewNeweNNCCoocWindowFV faVsco.|s rapstomProinet vViewNeweNNCCoocWindowFV faVsco.|s ~#12121 on JY-20963-fx-lAdtwtyontroller.ong=custom.logSF [minny@localhost)h Wahboalwolcnnedvilycimbato.ong(©) Activity,php xwlcomlpipA console (EUTA console [STAGING"oaccoundoneAcuvil sond© Address.phoC AiPromot.phpCAulomaleo keoottoneC AutomatedReportResult.ohocCalenoaroncallmeeonclass Acavicy excenos nodel anplenencs20382078cCoacnoreeooeen© CoachingFeedbackVisibllity.php 2032c) coach nosection choc) coach nosectioneritenonFeed or 208C commentAostract ono8 commenti ntertace onoC contact ond© Device.ohecSmalllMacesed dooleCanarie oromot oorg Group.phpC Inbox.phpC InboxEmail.phpc InboxEmailBatch.phpC Invitation.php© JobLog.php© JobTitle.php© Language.php© LanguageDialect.php© Lead.php© MobileSetting.phpC. Model.ohoC) Moment oholC Nudge oho© NudgeRun .ohoC Particinant.ohdowanhertC Permission.ohoC PhoneNumoer. ono© PlaybackTheme.choc Pavbook ond©. PlaybookCategory.ohec [EMAIL] Rec on oho©. Role-phoe ealdthsnadsvont nhrA connoGrnun nha e Coceion nhmA4AXA8VPY103 D4AV=79dpubLze functzon updaterartaczpantirabata(array Srecoras, Pertzcapant Spartzczpant): voll 70s* Updates activity crh data* Вnarae acnauilLeadpnoee,Account|null,OpportunitylnullContact|null,sagelnoee,stringlnulz*, Srecords=713ublic function undateActivityCraData(array Srecords): voidrxdch the necoros,715717oeat saccount sodoorUhtyr deohaetosattreconosSresover = sthis-›gerUodarenataraResover oSstrateay = Sreso ver-›reso veforacevirylstead, Scontact Saccount)if (Sstrategy == UpdateConDataßvStrategv.:Lead)Sstrategy in UpdateCrmDataßyStrategy:: Lead) checkina me donit orente a mi$724if (Sthis->account_id aa= null && Sthis-›contact_id a== null && Sthis->lead_10m€ 725Sthis-> ead.d= Sleado>dh34 (Sthisesstade fa aue null fe Sstane) !thnsosstace e SetaceoseeE732elseif (Sstrategy == UpdateCraDataByStrategy::Contact) 4ao (lstresey = pestecrmesaestrseay: comest) e ne terit erate nteCthiecslond id = oull.if (Sthis->stage && Sthis->stage-›getType() === Stage:: TYPE_LEAD) €Sthis-›stage_id = null;if (Saccount &s Saccount->id laz Sthis->account.id)thiersnooount iar Coccmunt.tidsif (Soppontunity fs Sthis-sopportunity id fas Sopportunity-sid) $wtwttwiew outreoneetxminutas.sndimiamyAND a.created at > DATE SUB(NOWO, INTERVAL 38 DAY)GROUP RY u.sd urenaitl, u.nane. M. softahone nunberORDER BY sms count DESC045 A1 A41 Y 66 Aselect * from teans where 1d = 1select * fron rolesSELECTCONCATu.id, CASE WHEN u.id = t.owner id THEN " (onner' ELSE ** END) AS user idt.oanerid FROM social accounts s.JOIN users u on u.id = sa.sociable_idJOIN teans t1.n<->1: on taid = u.tean_idTHERE u,tean sid = 1117 and saroroviden = "hubspot*.SELENFRONNOOMICTOSTHERE MUSGTOMML8874020-2012048109104-09:896858748 E WUSA: B79983459 1ESSELEN FRUNACAMICIOS HERE MISGA mnlysSyoeu-8e9uuiooeeuconasiisee wusd:881801197 11eSELECT * FROM Crn_configurations WHERE id = 1053;SELECT * FROM teansHERE SOTIWYselect * fron users where id = 30249;select * from playbooks where 10 = 5473select * fron playbook_categories where id = 43783;select + Eron dlavbook cateoonses where nilavhook 5d 56731select * fron crn_fields where id = 659242;select + Eron eon 6eld values where com Seid 3d = 650242)SSIEN + FROM Co 45e1d data 4.# JOIN cra fields + ON fd.crn field id = f.ic*JOIN activities a ON fd.activity id = a.10WHERE actávity_$d = 79933459# AND f.crn provider id = 'hs activity type':101SELECT * FROM activity-nessages:select * fron text_relays where created at > *2826-85-01°:select * fron activities where userid IN (7168, 18688) and created at > *2826-85-22' order by id descaselect * fron users where tean_id = 1 and id IN (18688, 13934, 7160):select * fron activities where user id = 7169 order by id desc Linit 10:select * fron accounts wheretean_id=1 and nane= "Colunns":select * fron usens where nane Like '%Subrax": # 31954. 1117sellect * on actuity searches where lusen Oe XHRSAselect + fron actiwity sparch &iltens where actiuity seanch ia TN (9998). BROR)) :Inu co moy twuoiServiceTest+0.Cecsdales Orceancworceoeionh1/ 0rosers/ tuxos/2Toreach (Sparticiponts as spariicipant)Srecords = Sthis-s1indCreRecords(Sparticipant, Sactivity):coly piccoroKrionter thieetw.wwtinrecorddierSaruich: kereridh/l & This nay call Salesforce::matchByDomain()Step 7: Domain Search tnggers opportunity synoboiin senrth eirs necoun lens diinc" withi inelmired oncortuntIl To get opportunity details, it calls:Scraservicesyncopponontylscrrrovzoerso):# couts aaporcopportunztyl)Step 8: The bua - importo pportunity creates temporary Recoroaond/Users/lukas/1m1nny/app/app/Services/crm/Salestorce/Serv1cc.php:1430-1448oriivate tunctionimoort@sportunt tylScr iatal: 20000rtuntvVxino early chelete chack8a. Restore fron trash (tine 1430)SthcesrestorAcyTrachwEnttylSthisescontoesomoortintnsol. Ser Dataletaenruny no ests wich valdld0i0.0anes34309433Sopportunity = SthswoWrtreucreonowloer1Dcreuaeounoaasosaat RosborunitweseVADio la.or7hosy5yiamoor edtusin thesin euinetysthices oort DocortuntyerFel Data (Ser Datar SorBtelds, Conoortunt tyestd)eM8ar Noh celeteit ansin itine 1444Sthis-shandle0bjectDeletion(Sopportunity, ScrnData):if (Sopportunity=trashed()) 4Il Methad returs null, ButAat anuthientua"eoohKtwodturTasoe95hireh*4 space...
|
NULL
|
-7144293350501696560
|
NULL
|
visual_change
|
ocr
|
NULL
|
rapstomProinet vViewNeweNNCCoocWindowFV faVsco.|s rapstomProinet vViewNeweNNCCoocWindowFV faVsco.|s ~#12121 on JY-20963-fx-lAdtwtyontroller.ong=custom.logSF [minny@localhost)h Wahboalwolcnnedvilycimbato.ong(©) Activity,php xwlcomlpipA console (EUTA console [STAGING"oaccoundoneAcuvil sond© Address.phoC AiPromot.phpCAulomaleo keoottoneC AutomatedReportResult.ohocCalenoaroncallmeeonclass Acavicy excenos nodel anplenencs20382078cCoacnoreeooeen© CoachingFeedbackVisibllity.php 2032c) coach nosection choc) coach nosectioneritenonFeed or 208C commentAostract ono8 commenti ntertace onoC contact ond© Device.ohecSmalllMacesed dooleCanarie oromot oorg Group.phpC Inbox.phpC InboxEmail.phpc InboxEmailBatch.phpC Invitation.php© JobLog.php© JobTitle.php© Language.php© LanguageDialect.php© Lead.php© MobileSetting.phpC. Model.ohoC) Moment oholC Nudge oho© NudgeRun .ohoC Particinant.ohdowanhertC Permission.ohoC PhoneNumoer. ono© PlaybackTheme.choc Pavbook ond©. PlaybookCategory.ohec [EMAIL] Rec on oho©. Role-phoe ealdthsnadsvont nhrA connoGrnun nha e Coceion nhmA4AXA8VPY103 D4AV=79dpubLze functzon updaterartaczpantirabata(array Srecoras, Pertzcapant Spartzczpant): voll 70s* Updates activity crh data* Вnarae acnauilLeadpnoee,Account|null,OpportunitylnullContact|null,sagelnoee,stringlnulz*, Srecords=713ublic function undateActivityCraData(array Srecords): voidrxdch the necoros,715717oeat saccount sodoorUhtyr deohaetosattreconosSresover = sthis-›gerUodarenataraResover oSstrateay = Sreso ver-›reso veforacevirylstead, Scontact Saccount)if (Sstrategy == UpdateConDataßvStrategv.:Lead)Sstrategy in UpdateCrmDataßyStrategy:: Lead) checkina me donit orente a mi$724if (Sthis->account_id aa= null && Sthis-›contact_id a== null && Sthis->lead_10m€ 725Sthis-> ead.d= Sleado>dh34 (Sthisesstade fa aue null fe Sstane) !thnsosstace e SetaceoseeE732elseif (Sstrategy == UpdateCraDataByStrategy::Contact) 4ao (lstresey = pestecrmesaestrseay: comest) e ne terit erate nteCthiecslond id = oull.if (Sthis->stage && Sthis->stage-›getType() === Stage:: TYPE_LEAD) €Sthis-›stage_id = null;if (Saccount &s Saccount->id laz Sthis->account.id)thiersnooount iar Coccmunt.tidsif (Soppontunity fs Sthis-sopportunity id fas Sopportunity-sid) $wtwttwiew outreoneetxminutas.sndimiamyAND a.created at > DATE SUB(NOWO, INTERVAL 38 DAY)GROUP RY u.sd urenaitl, u.nane. M. softahone nunberORDER BY sms count DESC045 A1 A41 Y 66 Aselect * from teans where 1d = 1select * fron rolesSELECTCONCATu.id, CASE WHEN u.id = t.owner id THEN " (onner' ELSE ** END) AS user idt.oanerid FROM social accounts s.JOIN users u on u.id = sa.sociable_idJOIN teans t1.n<->1: on taid = u.tean_idTHERE u,tean sid = 1117 and saroroviden = "hubspot*.SELENFRONNOOMICTOSTHERE MUSGTOMML8874020-2012048109104-09:896858748 E WUSA: B79983459 1ESSELEN FRUNACAMICIOS HERE MISGA mnlysSyoeu-8e9uuiooeeuconasiisee wusd:881801197 11eSELECT * FROM Crn_configurations WHERE id = 1053;SELECT * FROM teansHERE SOTIWYselect * fron users where id = 30249;select * from playbooks where 10 = 5473select * fron playbook_categories where id = 43783;select + Eron dlavbook cateoonses where nilavhook 5d 56731select * fron crn_fields where id = 659242;select + Eron eon 6eld values where com Seid 3d = 650242)SSIEN + FROM Co 45e1d data 4.# JOIN cra fields + ON fd.crn field id = f.ic*JOIN activities a ON fd.activity id = a.10WHERE actávity_$d = 79933459# AND f.crn provider id = 'hs activity type':101SELECT * FROM activity-nessages:select * fron text_relays where created at > *2826-85-01°:select * fron activities where userid IN (7168, 18688) and created at > *2826-85-22' order by id descaselect * fron users where tean_id = 1 and id IN (18688, 13934, 7160):select * fron activities where user id = 7169 order by id desc Linit 10:select * fron accounts wheretean_id=1 and nane= "Colunns":select * fron usens where nane Like '%Subrax": # 31954. 1117sellect * on actuity searches where lusen Oe XHRSAselect + fron actiwity sparch &iltens where actiuity seanch ia TN (9998). BROR)) :Inu co moy twuoiServiceTest+0.Cecsdales Orceancworceoeionh1/ 0rosers/ tuxos/2Toreach (Sparticiponts as spariicipant)Srecords = Sthis-s1indCreRecords(Sparticipant, Sactivity):coly piccoroKrionter thieetw.wwtinrecorddierSaruich: kereridh/l & This nay call Salesforce::matchByDomain()Step 7: Domain Search tnggers opportunity synoboiin senrth eirs necoun lens diinc" withi inelmired oncortuntIl To get opportunity details, it calls:Scraservicesyncopponontylscrrrovzoerso):# couts aaporcopportunztyl)Step 8: The bua - importo pportunity creates temporary Recoroaond/Users/lukas/1m1nny/app/app/Services/crm/Salestorce/Serv1cc.php:1430-1448oriivate tunctionimoort@sportunt tylScr iatal: 20000rtuntvVxino early chelete chack8a. Restore fron trash (tine 1430)SthcesrestorAcyTrachwEnttylSthisescontoesomoortintnsol. Ser Dataletaenruny no ests wich valdld0i0.0anes34309433Sopportunity = SthswoWrtreucreonowloer1Dcreuaeounoaasosaat RosborunitweseVADio la.or7hosy5yiamoor edtusin thesin euinetysthices oort DocortuntyerFel Data (Ser Datar SorBtelds, Conoortunt tyestd)eM8ar Noh celeteit ansin itine 1444Sthis-shandle0bjectDeletion(Sopportunity, ScrnData):if (Sopportunity=trashed()) 4Il Methad returs null, ButAat anuthientua"eoohKtwodturTasoe95hireh*4 space...
|
86380
|
NULL
|
NULL
|
NULL
|
|
86380
|
2963
|
2
|
2026-05-28T14:06:26.116779+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-28/1779 /Users/lukas/.screenpipe/data/data/2026-05-28/1779977186116_m2.jpg...
|
PhpStorm
|
faVsco.js – Activity.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
#12121 on JY-20963-fix-im Project: faVsco.js, menu
#12121 on JY-20963-fix-import-on-deleted-entity, menu
Start Listening for PHP Debug Connections
ServiceTest
Run 'ServiceTest'
Debug 'ServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
4
388
9
103
4
Previous Highlighted Error
Next Highlighted Error
<?php
namespace Jiminny\Models;
use Carbon\Carbon;
use Database\Factories\ActivityFactory;
use DateTimeInterface;
use Exception;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Database\Eloquent;
use Illuminate\Database\Eloquent\Attributes\Scope;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Auth;
use InvalidArgumentException;
use Jiminny\Component\ElasticSearch;
use Jiminny\Component\MeetingBot;
use Jiminny\Component\Model\BitwiseFlagTrait;
use Jiminny\Component\PlaybackPage\Comments\Services\ActivityCommentService;
use Jiminny\Component\Sidekick\SidekickService;
use Jiminny\Component\Uuid\UuidAwareInterface;
use Jiminny\Component\Workflow;
use Jiminny\Contracts;
use Jiminny\Contracts\Crm\ProspectInterface;
use Jiminny\DTO\ImportCall\Call;
use Jiminny\Events\Activities\ActivityTypeUpdated;
use Jiminny\Events\Activities\ActivityUpdated;
use Jiminny\Events\Activities\ProspectUpdated;
use Jiminny\Events\Activities\StageUpdated;
use Jiminny\Events\Activities\StatusUpdated;
use Jiminny\Events\Activities\TitleUpdated;
use Jiminny\Exceptions\InvalidArgumentException as InvalidArgumentJiminnyException;
use Jiminny\Exceptions\LogicException;
use Jiminny\Exceptions\RuntimeException;
use Jiminny\Models;
use Jiminny\Models\Activity\ActivitySummaryLog;
use Jiminny\Models\Activity\ActivityUploadSetting;
use Jiminny\Models\Activity\AvailabilityNotification;
use Jiminny\Models\Activity\CoachRequest;
use Jiminny\Models\Activity\Comment;
use Jiminny\Models\Activity\Log;
use Jiminny\Models\Activity\Message;
use Jiminny\Models\Activity\Moment;
use Jiminny\Models\Activity\Note;
use Jiminny\Models\Activity\ParticipantSpeech;
use Jiminny\Models\Activity\Play;
use Jiminny\Models\Activity\Question;
use Jiminny\Models\Activity\Share;
use Jiminny\Models\Activity\Snapshot;
use Jiminny\Models\Activity\Stats;
use Jiminny\Models\Activity\SubscriptionSet;
use Jiminny\Models\Activity\TopicTrigger;
use Jiminny\Models\Activity\Transcription;
use Jiminny\Models\Calendar\CalendarEvent;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Models\Crm\FieldData;
use Jiminny\Models\ElasticSearch\ActivityElasticSearchTrait;
use Jiminny\Models\Feature\FeatureEnum;
use Jiminny\Models\Participant\Connection;
use Jiminny\Models\Playlist\Activity as PlaylistActivity;
use Jiminny\Services\Activity\ActivityProviderRegistry;
use Jiminny\Services\Activity\Import\DataResolvers\UpdateCrmDataByStrategy;
use Jiminny\Services\Activity\Import\DataResolvers\UpdateCrmDataResolverFactory;
use Jiminny\Services\Activity\Import\DataResolvers\UpdateCrmDataResolverInterface;
use Jiminny\Traits\Enums;
use Jiminny\Traits\RequiresUUID;
use Jiminny\Utils\CurrencyFormatter;
use NumberFormatter;
use function in_array;
/**
* Jiminny\Models\Activity
*
* @property null|int $auto_score filled from ES hydrator, not in DB!
* @property-read Account|null $account
* @property-read CalendarEvent|null $calendarEvent
* @property-read Contact|null $contact
* @property-read Lead|null $lead
* @property-read Opportunity|null $opportunity
* @property-read Stage|null $stage
* @property int $id
* @property mixed|null $uuid
* @property string|null $source
* @property string|null $external_id
* @property string $provider
* @property string|null $location
* @property string|null $telephony_provider_id
* @property int|null $from_participant_id
* @property int|null $to_participant_id
* @property int|null $device_id
* @property string|null $type
* @property int|null $playbook_category_id
* @property int $user_id
* @property int|null $lead_id
* @property int|null $account_id
* @property int|null $contact_id
* @property int|null $opportunity_id
* @property int|null $stage_id
* @property string|null $value
* @property int|null $crm_configuration_id
* @property string|null $crm_provider_id
* @property string|null $language
* @property int|null $transcription_id
* @property int $duration
* @property string $status
* @property int|null $on_air
* @property int|null $calendar_event_id
* @property string $recording_state
* @property bool|null $recording_preference
* @property int $recording_reason_code
* @property int $summary_reminder_sent
* @property \Illuminate\Support\Carbon|null $log_reminder_sent_at
* @property \Illuminate\Support\Carbon|null $organizer_notified_at
* @property bool|null $has_recording_prompt
* @property bool $is_internal
* @property int $is_locked
* @property int $is_recording
* @property bool|null $is_processed
* @property bool $is_private
* @property bool $is_instant_invite
* @property string|null $poster_path
* @property string|null $summary
* @property string|null $title
* @property string|null $description
* @property \Illuminate\Support\Carbon|null $scheduled_start_time
* @property \Illuminate\Support\Carbon|null $scheduled_end_time
* @property \Illuminate\Support\Carbon|null $actual_start_time
* @property \Illuminate\Support\Carbon|null $actual_end_time
* @property int|null $uploaded_by
* @property \Illuminate\Support\Carbon|null $deleted_at
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property string|null $average_score
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Participant> $activeParticipants
* @property-read int|null $active_participants_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Scorecard\ActivityScorecardRuleTrigger> $activityScorecardRuleTriggers
* @property-read int|null $activity_scorecard_rule_triggers_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Scorecard\ActivityScorecardRule> $activityScorecardRules
* @property-read int|null $activity_scorecard_rules_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, AvailabilityNotification> $availabilityNotifications
* @property-read int|null $availability_notifications_count
* @property-read \Jiminny\Models\PlaybookCategory|null $category
* @property-read \Illuminate\Database\Eloquent\Collection<int, CoachRequest> $coachRequests
* @property-read int|null $coach_requests_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\CoachingFeedback> $coachingFeedbacks
* @property-read int|null $coaching_feedbacks_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, Message> $coachingMessages
* @property-read int|null $coaching_messages_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, Comment> $comments
* @property-read int|null $comments_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, Connection> $connections
* @property-read int|null $connections_count
* @property-read Configuration|null $crm
* @property-read \Illuminate\Database\Eloquent\Collection<int, FieldData> $data
* @property-read int|null $data_count
* @property-read \Jiminny\Models\Device|null $device
* @property-read \Kalnoy\Nestedset\Collection<int, \Jiminny\Models\Playlist> $favoritePlaylists
* @property-read int|null $favorite_playlists_count
* @property-read \Jiminny\Models\Participant|null $from
* @property-read string|null $activity_title
* @property-read mixed $comment_count
* @property-read mixed $duration_for_humans
* @property-read string $duration_for_humans_short
* @property-read int $favorite_count
* @property-read mixed $favorites_count
* @property-read mixed $formatted_value
* @property-read string $id_string
* @property-read \Jiminny\Models\Participant|null $organizer
* @property-read mixed $play_count
* @property-read int|null $plays_count
* @property-read ?ProspectInterface $prospect
* @property-read string|null $prospect_name
* @property-read mixed $prospect_type
* @property-read mixed $share_count
* @property-read int|null $shares_count
* @property-read int|null $tracks_with_telephony_count
* @property-read int|null $visible_comments_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\CoachingFeedback> $latestCoachingFeedbacks
* @property-read int|null $latest_coaching_feedbacks_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, Log> $logs
* @property-read int|null $logs_count
* @property-read \Jiminny\Models\Track|null $masterTrack
* @property-read \Illuminate\Database\Eloquent\Collection<int, Message> $messages
* @property-read int|null $messages_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, Moment> $moments
* @property-read int|null $moments_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, Note> $notes
* @property-read int|null $notes_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Participant\Share> $participantShares
* @property-read int|null $participant_shares_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, ParticipantSpeech> $participantSpeeches
* @property-read int|null $participant_speeches_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Participant\ParticipantStats> $participantStats
* @property-read int|null $participant_stats_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Participant> $participants
* @property-read int|null $participants_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, PlaylistActivity> $playlistActivities
* @property-read int|null $playlist_activities_count
* @property-read \Kalnoy\Nestedset\Collection<int, \Jiminny\Models\Playlist> $playlists
* @property-read int|null $playlists_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, Play> $plays
* @property-read \Illuminate\Database\Eloquent\Collection<int, Question> $questions
* @property-read int|null $questions_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Session> $sessions
* @property-read int|null $sessions_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, Share> $shares
* @property-read \Illuminate\Database\Eloquent\Collection<int, Snapshot> $snapshots
* @property-read int|null $snapshots_count
* @property-read Stats|null $stats
* @property-read \Jiminny\Models\Participant|null $to
* @property-read \Illuminate\Database\Eloquent\Collection<int, TopicTrigger> $topicTriggers
* @property-read int|null $topic_triggers_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Track> $tracks
* @property-read int|null $tracks_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Track> $tracksWithTelephony
* @property-read Transcription|null $transcription
* @property-read \Jiminny\Models\User $user
* @property-read \Illuminate\Database\Eloquent\Collection<int, Comment> $visibleComments
*
* @method static \Illuminate\Database\Eloquent\Collection<int, static> all($columns = ['*'])
* @method static \Jiminny\Component\Eloquent\Builder|Activity chunkByIdDesc($count, callable $callback, $column = null, $alias = null)
* @method static \Database\Factories\ActivityFactory factory(...$parameters)
* @method static \Illuminate\Database\Eloquent\Collection<int, static> get($columns = ['*'])
* @method static \Jiminny\Component\Eloquent\Builder|Activity heldBetween(\Carbon\Carbon $start, \Carbon\Carbon $end)
* @method static \Jiminny\Component\Eloquent\Builder|Activity idOrUuId($idOrUuid, bool $first = true)
* @method static \Jiminny\Component\Eloquent\Builder|Activity newModelQuery()
* @method static \Jiminny\Component\Eloquent\Builder|Activity newQuery()
* @method static Builder|Activity onlyTrashed()
* @method static \Jiminny\Component\Eloquent\Builder|Activity query()
* @method static \Jiminny\Component\Eloquent\Builder|Activity scheduledBetween(\Carbon\Carbon $start, \Carbon\Carbon $end)
* @method static \Jiminny\Component\Eloquent\Builder|Activity inOpenDeals()
* @method static \Jiminny\Component\Eloquent\Builder|Activity notInOpenDeals()
* @method static \Jiminny\Component\Eloquent\Builder|Activity forTeam(int $teamId)
* @method static \Jiminny\Component\Eloquent\Builder|Activity search(callable $searchQuery, $key = null, $sortByResults = true)
* @method static \Jiminny\Component\Eloquent\Builder|Activity uuid(string $uuid, bool $first = true)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereAccountId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereActualEndTime($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereActualStartTime($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereAverageScore($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereCalendarEventId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereContactId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereCreatedAt($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereCrmConfigurationId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereCrmProviderId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereDeletedAt($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereDescription($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereDeviceId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereDuration($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereFromParticipantId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereHasRecordingPrompt($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereIsInstantInvite($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereIsInternal($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereIsLocked($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereIsPrivate($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereIsProcessed($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereIsRecording($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereLanguage($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereLeadId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereLocation($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereLogReminderSentAt($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereOnAir($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereOpportunityId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereOrganizerNotifiedAt($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity wherePlaybookCategoryId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity wherePosterPath($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereProvider($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereRecordingPreference($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereRecordingReasonCode($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereRecordingState($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereScheduledEndTime($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereScheduledStartTime($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereSource($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereExternalId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereStageId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereStatus($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereSummary($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereSummaryReminderSent($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereTelephonyProviderId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereTitle($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereToParticipantId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereTranscriptionId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereType($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereUpdatedAt($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereUploadedBy($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereUserId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereUuid($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereValue($value)
* @method static Builder|Activity withTrashed()
* @method static Builder|Activity withoutTrashed()
*
* @mixin \Eloquent
*/
class Activity extends Model implements
ElasticSearch\Contract\Searchable,
Workflow\Workflow\WorkflowAwareInterface,
Models\Contracts\ActivityContract,
Contracts\Model\ActivityInterface,
UuidAwareInterface
{
use HasFactory;
use Enums;
use SoftDeletes;
use RequiresUUID;
use BitwiseFlagTrait;
use ElasticSearch\Model\Searchable;
use ActivityElasticSearchTrait;
use Workflow\Workflow\WorkflowAware {
transitionTo as traitTransitionTo;
}
public const int FLAG_RECORDING_REASON_DEFAULT = 0;
// Recording Prompted but never started
public const int FLAG_RECORDING_REASON_COMPLIANCE_PROMPT = 1;
public const int FLAG_RECORDING_REASON_COMPLIANCE_RESUMED = 2;
public const int FLAG_RECORDING_REASON_NO_AUDIO = 3;
// Recording Disabled by Organization
public const int FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT = 4;
// Recording was restricted to one-side recordings only
public const int FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT_ONE_SIDE = 8;
// Recording was not started because it was internal and team setting disabled that.
public const int FLAG_RECORDING_REASON_TEAM_INTERNAL_DISABLED = 16;
// Recording was not started because it was internal and user setting disabled that.
public const int FLAG_RECORDING_REASON_USER_INTERNAL_DISABLED = 32;
// Recording was not started because user setting disabled automatic recording.
public const int FLAG_RECORDING_REASON_USER_AUTOMATIC_DISABLED = 64;
// Recording was not started because team setting disabled automatic recording.
public const int FLAG_RECORDING_REASON_TEAM_AUTOMATIC_DISABLED = 128;
// Recording was not started because user has overriden default.
public const int FLAG_RECORDING_REASON_PREFERENCE_OVERRIDE = 256;
// Recording was not started because they don't want internal, and this meeting was not scheduled/imported in time.
public const int FLAG_RECORDING_REASON_USER_INTERNAL_DISABLED_UNSCHEDULED = 512;
// Recording was not started because their team setting does excludes the meeting type.
public const int FLAG_RECORDING_REASON_UNSUPPORTED_TYPE = 1024;
// Recording was not started because the external provider disabled it (or recording is missing etc).
public const int FLAG_RECORDING_REASON_EXTERNALLY_DISABLED = 2048;
// Recording was stopped externally ("exit-meeting" Pusher event)
public const int FLAG_RECORDING_REASON_STOPPED_EXTERNALLY = 384;
// Recording couldn't be started due to Zoom hosting conflict error
public const int FLAG_RECORDING_REASON_HOSTING_CONFLICT = 448;
// meeting.failed event with reason code BOT_DENIED_FROM_LOBBY
public const int FLAG_RECORDING_REASON_MEETING_BOT_DENIED_FROM_LOBBY = 4096;
// meeting.failed event with reason code LOBBY_TIMEOUT
public const int FLAG_RECORDING_REASON_MEETING_BOT_LOBBY_TIMEOUT = 8192;
// meeting.failed event with reason code BOT_KICKED
public const int FLAG_RECORDING_REASON_MEETING_BOT_KICKED = 16384;
// meeting.failed event with reason code UNKNOWN
public const int FLAG_RECORDING_REASON_MEETING_BOT_UNKNOWN = 32768;
public const int FLAG_RECORDING_REASON_CONSENT_DENIED = 65536;
// Invalid meeting (e.g. URL is invalid, or the meeting is not found)
public const int FLAG_RECORDING_REASON_MEETING_BOT_INVALID = 131072;
// The host stopped the recording.
public const int FLAG_RECORDING_REASON_USER_STOPPED = 262144;
// Recording was not started because an alternative vendor disabled it (or overrode it).
public const int FLAG_RECORDING_REASON_VENDOR_OVERRIDE = 1048576;
// Login required meeting.failed code
public const int FLAG_RECORDING_REASON_LOGIN_REQUIRED = 524288;
// Password for meeting was not provided - meeting.failed code
public const int FLAG_RECORDING_REASON_MEETING_PASSWORD_NOT_PROVIDED = 2097152;
// meeting.failed - when the meeting is locked
public const int FLAG_RECORDING_REASON_MEETING_IS_LOCKED = 4194304;
// max recording duration reached
public const int FLAG_RECORDING_REASON_MAX_DURATION_REACHED = 8388608;
// recording size is too small
public const int FLAG_RECORDING_REASON_EMPTY_RECORDING = 16777216;
// meeting.failed - when bot is redirected to sign in page multiple times
public const int FLAG_RECORDING_REASON_MAX_RESTART_COUNT_IS_REACHED = 33554432;
// meeting.failed event with reason code CONNECTION_LOST
public const int FLAG_RECORDING_REASON_MEETING_BOT_CONNECTION_LOST = 67108864;
// recording is corrupted.
public const int FLAG_RECORDING_REASON_MEDIA_FILE_UNSUPPORTED_MIME_TYPE = 134217728;
// meeting ended in lobby
public const int FLAG_RECORDING_REASON_MEETING_ENDED_IN_LOBBY = 268435456;
// meeting not started
public const int FLAG_RECORDING_REASON_REASON_MEETING_NOT_STARTED = 536870912;
// unfinished zoom custom disclaimer
public const int FLAG_RECORDING_REASON_FEATURE_RULE_NOT_FOUND_ERROR = 1073741824;
// recording download failed - server error
public const int FLAG_RECORDING_REASON_SERVER_ERROR = 2147483648;
// recording download failed - client code 404
public const int FLAG_RECORDING_REASON_NOT_FOUND = 2147483649;
// recording download failed - client code 401, 403
public const int FLAG_RECORDING_REASON_ACCESS_DENIED = 2147483650;
// recording download failed - client code 429
public const int FLAG_RECORDING_REASON_TOO_MANY_REQUESTS = 2147483651;
// recording download failed - unknown client error
public const int FLAG_RECORDING_REASON_CLIENT_ERROR = 2147483652;
// recording download failed - unknown error
public const int FLAG_RECORDING_REASON_UNKNOWN_ERROR = 2147483653;
// It has been setup ahead of time through calendar
public const string STATUS_SCHEDULED = 'scheduled';
// It is awaiting audio.
public const string STATUS_PENDING = 'pending';
// Participant(s) dialed in, awaiting organizer.
public const string STATUS_RINGING = 'ringing';
// Call is in progress.
public const string STATUS_IN_PROGRESS = 'in-progress';
// It has ended.
public const string STATUS_COMPLETED = 'completed';
// Cancelled prior to starting.
public const string STATUS_CANCELLED = 'canceled';
public const string STATUS_DUPLICATED = 'duplicated'; // duplicated conference
public const string STATUS_STARTING_SOON = 'starting-soon';
public const string STATUS_BOT_CREATE_SENT = 'bot-create-sent';
public const string STATUS_BOT_INSTANCE_WORKER_ASSIGNED = 'worker-assigned';
public const string STATUS_BOT_INSTANCE_STARTED = 'bot-started';
// When bot instance is waiting in lobby
public const string STATUS_BOT_INSTANCE_WAITING_LOBBY = 'bot-waiting';
public const string STATUS_BUSY = 'busy';
public const string STATUS_NO_ANSWER = 'no-answer';
public const string STATUS_FAILED = 'failed'; // Used by SMS too
// SMS related
public const string STATUS_ACCEPTED = 'accepted';
public const string STATUS_QUEUED = 'queued';
public const string STATUS_SENDING = 'sending';
public const string STATUS_SENT = 'sent';
public const string STATUS_DELIVERED = 'delivered';
public const string STATUS_UNDELIVERED = 'undelivered';
public const string STATUS_RECEIVING = 'receiving';
public const string STATUS_RECEIVED = 'received';
public const string STATUS_RESENT = 'resent';
public const array SMS_STATUSES = [
Activity::STATUS_RECEIVED,
Activity::STATUS_SENT,
Activity::STATUS_DELIVERED,
];
public const array SOFT_PHONE_CONFERENCE_STATUSES = [
Activity::STATUS_IN_PROGRESS,
Activity::STATUS_COMPLETED,
];
// @todo refactor prefix from `TYPE_` to `CHANNEL_`
public const string TYPE_SOFTPHONE = 'softphone';
public const string TYPE_SOFTPHONE_INBOUND = 'softphone-inbound';
public const string TYPE_CONFERENCE = 'conference';
public const string TYPE_SMS_INBOUND = 'sms-inbound';
public const string TYPE_SMS_OUTBOUND = 'sms-outbound';
public const string TYPE_EMAIL_INBOUND = 'email-inbound';
public const string TYPE_EMAIL_OUTBOUND = 'email-outbound';
public const array CHANNELS = [
self::TYPE_SOFTPHONE,
self::TYPE_SOFTPHONE_INBOUND,
self::TYPE_CONFERENCE,
self::TYPE_SMS_INBOUND,
self::TYPE_SMS_OUTBOUND,
self::TYPE_EMAIL_INBOUND,
self::TYPE_EMAIL_OUTBOUND,
];
public const array PLAYABLE_CHANNELS = [
self::TYPE_SOFTPHONE,
self::TYPE_SOFTPHONE_INBOUND,
self::TYPE_CONFERENCE,
];
// Recording States
public const string RECORDING_OFF = 'off'; // Default state
public const string RECORDING_IN_PROGRESS = 'in-progress';
public const string RECORDING_PAUSED = 'paused';
public const string RECORDING_STOPPED = 'stopped'; // To never be resumed.
public const string RECORDING_RECORDED = 'recorded'; // At least some portion of it was recorded.
public const string RECORDING_FAILED = 'failed'; // Recording was attempted but failed for some reason.
// Live Stream States
public const int ON_AIR_DEFAULT = 0;
public const int ON_AIR_READY = 1;
public const int ON_AIR_PREPARING = 2;
public const int ON_AIR_STREAMING = 3;
public const int ON_AIR_FINISHED = 4;
public const int ON_AIR_NOT_STREAMED = 5;
public const int ON_AIR_ERROR = -1;
public const string SOURCE_GONG = 'gong';
public const string SOURCE_CHORUS = 'chorus';
public const string SOURCE_OUTLOOK = 'outlook';
public const string SOURCE_GOOGLE = 'google';
// Activity Providers
public const string PROVIDER_TWILIO = 'twilio'; // XXX: This is run via the Jiminny Provider.
public const string PROVIDER_OUTREACH = 'outreach';
public const string PROVIDER_ZOOM_BOT = 'zoom-bot';
public const string PROVIDER_SALESLOFT = 'salesloft';
public const string PROVIDER_GOOGLE = 'google';
public const string PROVIDER_AIRCALL = 'aircall';
public const string PROVIDER_JUSTCALL = 'justcall';
public const string PROVIDER_GOOGLE_MEET = 'google-meet';
public const string PROVIDER_GONG = 'gong';
public const string PROVIDER_HUBSPOT = 'hubspot';
public const string PROVIDER_CLOSE = 'close';
public const string PROVIDER_TEAMS = 'ms-teams';
public const string PROVIDER_SALESFORCE = 'salesforce';
public const string PROVIDER_GROOVE = 'groove';
public const string PROVIDER_XANT = 'xant';
public const string PROVIDER_OFFICE = 'office';
public const string PROVIDER_NATTERBOX = 'natterbox';
public const string PROVIDER_RINGCENTRAL = 'ringcentral';
public const string PROVIDER_RINGCENTRAL_VIDEO = 'ringcentral-video';
public const string PROVIDER_GOTOMEETING = 'go-to-meeting';
public const string PROVIDER_DEMODESK = 'demo-desk';
public const string PROVIDER_DIALPAD = 'dialpad';
public const string PROVIDER_ZOOM_PHONE = 'zoom-phone';
public const string PROVIDER_CLOUDCALL = 'cloudcall';
public const string PROVIDER_CLOUDCALL_US = 'cloudcall-us';
public const string PROVIDER_EIGHT_BY_EIGHT = 'eight-by-eight'; // "8x8" UK
public const string PROVIDER_EIGHT_BY_EIGHT_CA = 'eight-by-eight-ca'; // "8x8" Canada
public const string PROVIDER_EIGHT_BY_EIGHT_AP = 'eight-by-eight-ap'; // "8x8" Australia
public const string PROVIDER_EIGHT_BY_EIGHT_US_EAST = 'eight-by-eight-use'; // "8x8" US East
public const string PROVIDER_EIGHT_BY_EIGHT_US_WEST = 'eight-by-eight-usw'; // "8x8" US West
public const string PROVIDER_CONNECT_AND_SELL = 'connect-and-sell';
public const string PROVIDER_CLOUD_TALK = 'cloud-talk';
public const string PROVIDER_AMAZON_CONNECT = 'amazon-connect';
public const string PROVIDER_VONAGE = 'vonage';
public const string PROVIDER_MIGRATOR = 'migrator';
public const string PROVIDER_UPLOADER = 'uploader';
public const string PROVIDER_TALKDESK = 'talkdesk';
public const string PROVIDER_TWILIO_FLEX = 'twilio-flex';
public const string PROVIDER_TWILIO_FLEX_DIRECT = 'twilio-flex-direct';
public const string PROVIDER_TWILIO_VIDEO = 'twilio-video';
public const string PROVIDER_AVAYA = 'avaya';
public const string PROVIDER_TELUS = 'telus';
public const string PROVIDER_FIVE_NINE = 'five-nine';
public const string PROVIDER_APOLLO = 'apollo';
public const string PROVIDER_ORUM = 'orum';
public const string PROVIDER_BLOOBIRDS = 'bloobirds';
/**
* @const API_PROVIDERS
* A list of integrations that import calls via API instead of webhooks
*/
public const array API_PROVIDERS = [
self::PROVIDER_OUTREACH,
self::PROVIDER_SALESLOFT,
self::PROVIDER_HUBSPOT,
self::PROVIDER_GROOVE,
self::PROVIDER_XANT,
self::PROVIDER_NATTERBOX,
self::PROVIDER_CLOUDCALL,
self::PROVIDER_CLOUDCALL_US,
self::PROVIDER_EIGHT_BY_EIGHT,
self::PROVIDER_EIGHT_BY_EIGHT_CA,
self::PROVIDER_EIGHT_BY_EIGHT_AP,
self::PROVIDER_EIGHT_BY_EIGHT_US_EAST,
self::PROVIDER_EIGHT_BY_EIGHT_US_WEST,
self::PROVIDER_CONNECT_AND_SELL,
self::PROVIDER_CLOUD_TALK,
self::PROVIDER_AMAZON_CONNECT,
self::PROVIDER_VONAGE,
self::PROVIDER_TALKDESK,
self::PROVIDER_TWILIO_VIDEO,
self::PROVIDER_TWILIO_FLEX,
self::PROVIDER_TWILIO_FLEX_DIRECT,
self::PROVIDER_FIVE_NINE,
self::PROVIDER_APOLLO,
self::PROVIDER_ORUM,
self::PROVIDER_BLOOBIRDS,
self::PROVIDER_RINGCENTRAL,
self::PROVIDER_AVAYA,
self::PROVIDER_TELUS,
];
public const array FINITE_STATES = [
self::TYPE_SOFTPHONE => [
self::STATUS_COMPLETED,
self::STATUS_FAILED,
self::STATUS_NO_ANSWER,
self::STATUS_BUSY,
],
self::TYPE_SOFTPHONE_INBOUND => [
self::STATUS_COMPLETED,
self::STATUS_FAILED,
self::STATUS_NO_ANSWER,
self::STATUS_BUSY,
],
self::TYPE_CONFERENCE => self::FINITE_STATES_CONFERENCE,
];
public const array FINITE_STATES_CONFERENCE = [
self::STATUS_COMPLETED,
self::STATUS_FAILED,
self::STATUS_CANCELLED,
];
public const array MEETING_BOT_JOIN_ATTEMPTED = [
self::STATUS_BOT_INSTANCE_WAITING_LOBBY,
self::STATUS_BOT_INSTANCE_STARTED,
];
public static array $enumStatuses = [
self::STATUS_SCHEDULED,
self::STATUS_PENDING,
self::STATUS_RINGING,
self::STATUS_IN_PROGRESS,
self::STATUS_COMPLETED,
self::STATUS_CANCELLED,
self::STATUS_BUSY,
self::STATUS_NO_ANSWER,
self::STATUS_FAILED,
self::STATUS_ACCEPTED,
self::STATUS_QUEUED,
self::STATUS_SENDING,
self::STATUS_SENT,
self::STATUS_RESENT,
self::STATUS_DELIVERED,
self::STATUS_UNDELIVERED,
self::STATUS_RECEIVING,
self::STATUS_RECEIVED,
self::STATUS_BOT_INSTANCE_WAITING_LOBBY,
self::STATUS_STARTING_SOON,
self::STATUS_BOT_INSTANCE_WORKER_ASSIGNED,
self::STATUS_BOT_INSTANCE_STARTED,
self::STATUS_DUPLICATED,
];
public static array $enumProviders = [
self::PROVIDER_TWILIO,
self::PROVIDER_OUTREACH,
self::PROVIDER_ZOOM_BOT,
self::PROVIDER_SALESLOFT,
self::PROVIDER_AIRCALL,
self::PROVIDER_JUSTCALL,
self::PROVIDER_GOOGLE_MEET,
self::PROVIDER_GONG,
self::PROVIDER_HUBSPOT,
self::PROVIDER_CLOSE,
self::PROVIDER_TEAMS,
self::PROVIDER_SALESFORCE,
self::PROVIDER_GROOVE,
self::PROVIDER_XANT,
self::PROVIDER_GOOGLE,
self::PROVIDER_OFFICE,
self::PROVIDER_NATTERBOX,
self::PROVIDER_RINGCENTRAL,
self::PROVIDER_RINGCENTRAL_VIDEO,
self::PROVIDER_GOTOMEETING,
self::PROVIDER_DEMODESK,
self::PROVIDER_DIALPAD,
self::PROVIDER_ZOOM_PHONE,
self::PROVIDER_CLOUDCALL,
self::PROVIDER_CLOUDCALL_US,
self::PROVIDER_EIGHT_BY_EIGHT,
self::PROVIDER_EIGHT_BY_EIGHT_CA,
self::PROVIDER_EIGHT_BY_EIGHT_AP,
self::PROVIDER_EIGHT_BY_EIGHT_US_EAST,
self::PROVIDER_EIGHT_BY_EIGHT_US_WEST,
self::PROVIDER_CONNECT_AND_SELL,
self::PROVIDER_CLOUD_TALK,
self::PROVIDER_AMAZON_CONNECT,
self::PROVIDER_VONAGE,
self::PROVIDER_TALKDESK,
self::PROVIDER_TWILIO_FLEX,
self::PROVIDER_TWILIO_FLEX_DIRECT,
self::PROVIDER_TWILIO_VIDEO,
self::PROVIDER_AVAYA,
self::PROVIDER_TELUS,
self::PROVIDER_FIVE_NINE,
self::PROVIDER_APOLLO,
self::PROVIDER_ORUM,
self::PROVIDER_BLOOBIRDS,
];
public static $enumRecordingStates = [
self::RECORDING_OFF, // Default state
self::RECORDING_IN_PROGRESS,
self::RECORDING_PAUSED,
self::RECORDING_STOPPED,
self::RECORDING_RECORDED,
self::RECORDING_FAILED,
];
// @Important:
// This collection is not used anywhere, and is fully duplicated by the Channels const.
// Validate if it is referred somehow via the enum trait, and if not, remove it entirely.
// An even better strategy will be to move all those constants to a dedicated class
protected array $enumTypes = [
self::TYPE_SOFTPHONE,
self::TYPE_SOFTPHONE_INBOUND,
self::TYPE_CONFERENCE,
self::TYPE_SMS_INBOUND,
self::TYPE_SMS_OUTBOUND,
self::TYPE_EMAIL_INBOUND,
self::TYPE_EMAIL_OUTBOUND,
];
protected static $enumFailedStatuses = [
self::STATUS_NO_ANSWER,
self::STATUS_FAILED,
self::STATUS_BUSY,
self::STATUS_CANCELLED,
];
protected $table = 'activities';
protected $fillable = [
// Type of activity.
'type', // @todo refactor to `channel`
// The activity type.
'playbook_category_id',
// User who hosts the activity.
'user_id',
// Related Lead record (if applicable)
'lead_id',
// Related Account record (if applicable)
'account_id',
// Related Contact record (if applicable)
'contact_id',
// Related Opportunity record (if applicable)
'opportunity_id',
// Stage of activity.
'stage_id',
// Value of opportunity.
'value',
// If the activity relates to a CRM task.
'crm_provider_id',
// If the activity was created through an external device.
'device_id',
// the activity's language code
'language',
// transcription id
'transcription_id',
// Duration of the call, with microseconds precision.
'duration',
// One of enumStatuses above.
'status',
// Have we reminded them to log the call?
'log_reminder_sent_at',
// If activity is private or inter-org, flagged here.
'is_internal',
// Managers and above can mark a call as private, to exclude it from other team members
'is_private',
'is_processed',
// Boolean for this activity being instant invite handled.
'is_instant_invite',
// If activity is in recording state, flagged here.
'recording_state',
// If activity recording is overidden from default.
'recording_preference',
// if recording did (not) happen, why that is
'recording_reason_code',
// Average score, updated during
'average_score',
// Summary that the organizer has taken after the call.
'summary',
// Subject of the activity, usually taken from calendar event.
'title',
// Description of the activity, usually taken from calendar event.
'description',
// Start time, usually taken from calendar event.
'scheduled_start_time',
// End time, usually taken from calendar event.
'scheduled_end_time',
// When the call actually started.
'actual_start_time',
// When the call actually ended.
'actual_end_time',
// SMS: Message reference
'telephony_provider_id',
// SMS: Participant who sent message
'from_participant_id',
// SMS: Participant who should receive the message
'to_participant_id',
// When an external guest joins an organizers meeting room and the organizer is not present,
// send them an SMS notification that someone has joined.
'organizer_notified_at',
// where was the activity imported from
'source',
// The id in the source system (e.g. the bot id in Recall.ai)
'external_id',
// The provider, by default it is twilio.
'provider',
// Meeting location url
'location',
// The snapshot for displaying a poster image.
'poster_path',
'crm_configuration_id',
// If there is an automated message that the conversation is being recorded
'has_recording_prompt',
// If the activity is being live-streamed
'on_air',
'calendar_event_id',
];
protected $appends = [
'id_string',
'organizer',
];
protected $hidden = [
'uuid',
];
protected $visible = [
'id_string',
'type',
'duration',
'average_score',
'status',
'log_reminder_sent_at',
'title',
'description',
'is_internal',
'scheduled_start_time',
'scheduled_end_time',
'actual_start_time',
'actual_end_time',
'user',
'category',
'account',
'contact',
'opportunity',
'lead',
'stage',
'stats',
'participants',
'playlists',
'tracks',
'comments',
'plays',
'coachingFeedbacks',
'shares',
'favorites',
'language',
'transcription',
'is_private',
'is_instant_invite',
'on_air',
'calendar_event_id',
];
protected function casts(): array
{
return [
'scheduled_start_time' => 'datetime',
'scheduled_end_time' => 'datetime',
'actual_start_time' => 'datetime',
'actual_end_time' => 'datetime',
'organizer_notified_at' => 'datetime',
'log_reminder_sent_at' => 'datetime',
'is_internal' => 'boolean',
'duration' => 'integer',
'average_score' => 'decimal:2',
'is_private' => 'boolean',
'is_processed' => 'boolean',
'is_instant_invite' => 'boolean',
'value' => 'decimal:2',
'recording_preference' => 'boolean',
'recording_reason_code' => 'integer',
'has_recording_prompt' => 'boolean',
'on_air' => 'integer',
];
}
protected static function boot()
{
parent::boot();
static::updated(static function (Activity $activity) {
// If activity is about to start (pending, ringing, in-progress) or event is scheduled in less than 1 week
if (in_array($activity->status, [Activity::STATUS_PENDING, Activity::STATUS_RINGING, Activity::STATUS_IN_PROGRESS], true) ||
($activity->scheduled_start_time && (int) $activity->scheduled_start_time->diffInWeeks(new Carbon(), true) < 1)) {
if ($activity->isDirty('status')) {
event(new StatusUpdated($activity));
}
if ($activity->isDirty('stage_id')) {
event(new StageUpdated($activity));
}
if ($activity->isDirty(['lead_id', 'account_id', 'contact_id'])) {
event(new ProspectUpdated($activity));
}
if ($activity->isDirty('opportunity_id')) {
event(new ActivityUpdated($activity, 'activity.opportunity-updated', Auth::user()));
}
if ($activity->isDirty('title')) {
event(new TitleUpdated($activity));
}
}
if ($activity->isDirty('playbook_category_id')) {
event(new ActivityTypeUpdated($activity));
}
});
static::deleted(static function (Activity $activity) {
// Hard delete associated playlistActivities
$activity->playlistActivities()->delete();
});
}
public function getOrganizerAttribute(): ?Participant
{
$participant = $this->participants()->where('user_id', $this->user_id)->first();
if (! $participant instanceof Participant && $participant !== null) {
throw new RuntimeException(sprintf('$participant must be an instance of "%s" or null', Participant::class));
}
return $participant;
}
public function getFormattedValueAttribute()
{
$currencyCode = 'USD';
if ($this->opportunity) {
$currencyCode = $this->opportunity->getCurrencyCode();
}
$formatter = new CurrencyFormatter();
$formatter->setTextAttribute(NumberFormatter::CURRENCY_CODE, $currencyCode);
$formatter->setAttribute(NumberFormatter::MAX_FRACTION_DIGITS, 0);
return $formatter->format($this->value, $currencyCode);
}
public function getProspectNameAttribute(): ?string
{
$prospectName = null;
if ($this->lead_id) {
$prospectName = $this->lead->name;
} elseif ($this->contact_id) {
$prospectName = $this->contact->name;
} elseif ($this->account_id) {
$prospectName = $this->account->name;
}
return $prospectName;
}
public function getProspectName(): ?string
{
/** @var string|null */
return $this->getAttribute('prospect_name');
}
/**
* Get activity title depending on prospect or title
*/
public function getActivityTitleAttribute(): ?string
{
$activityTitle = null;
if ($this->prospect && $this->prospect->getName()) {
if ($this->account_id) {
$activityTitle = $this->account->name;
} elseif ($this->lead_id) {
$activityTitle = $this->lead->company;
} elseif ($this->contact_id) {
$activityTitle = $this->contact->account ? $this->contact->account->name : $this->contact->name;
}
} elseif ($this->title) {
$activityTitle = $this->title;
}
return $activityTitle;
}
public function wasRecentlyCreated(): bool
{
return $this->wasRecentlyCreated;
}
public function getProspectTypeAttribute()
{
$prospectType = null;
if ($this->lead_id) {
$prospectType = 'Lead';
} elseif ($this->contact_id) {
$prospectType = 'Contact';
} elseif ($this->account_id) {
$prospectType = 'Account';
}
return $prospectType;
}
/**
* Return the best match for prospect. Results are in the following order of priority:
* 1. Lead
* 2. Contact
* 3. Account
* 4. NULL
*/
public function getProspectAttribute(): ?ProspectInterface
{
if ($this->hasLead()) {
return $this->getLead();
}
if ($this->hasContact()) {
return $this->getContact();
}
if ($this->hasAccount()) {
return $this->getAccount();
}
return null;
}
public function getTitleAttribute($value): ?string
{
return \getActivityTitleAttribute(
$this->user->name,
$this->getType(),
$value,
$this->prospect->name ?? null,
$this->from->national_phone_number ?? null
);
}
public function getTitle(): ?string
{
return $this->getAttribute('title');
}
public function getSummary(): ?string
{
return $this->getAttribute('summary');
}
public function isInternal(): bool
{
return $this->getAttribute('is_internal');
}
public function getIsPrivate(): bool
{
return $this->getAttribute('is_private');
}
public function getDescription(): ?string
{
return $this->getAttribute('description');
}
public function hasTitle(): bool
{
return $this->getOriginal('title') !== null;
}
public function getPlayCountAttribute()
{
return $this->getPlaysCountAttribute();
}
public function getPlaysCountAttribute()
{
if (! isset($this->attributes['plays_count'])) {
$this->loadCount('plays');
}
return $this->attributes['plays_count'];
}
public function getCommentCountAttribute()
{
return $this->getCommentsCountAttribute();
}
public function getCommentsCountAttribute()
{
if (! isset($this->attributes['comments_count'])) {
$this->loadCount('comments');
}
return $this->attributes['comments_count'];
}
public function getVisibleCommentsCountAttribute()
{
if (! isset($this->attributes['visible_comments_count'])) {
$activityCommentsService = app(ActivityCommentService::class);
$user = Auth::user() instanceof User ? Auth::user() : null;
$this->attributes['visible_comments_count'] = $activityCommentsService
->getVisibleCommentsCount($this, $user);
}
return $this->attributes['visible_comments_count'];
}
public function getShareCountAttribute()
{
return $this->getSharesCountAttribute();
}
public function getSharesCountAttribute()
{
if (! isset($this->attributes['shares_count'])) {
$this->loadCount('shares');
}
return $this->attributes['shares_count'];
}
/**
* Get the count of favorites playlists this activity appears in
*/
public function getFavoriteCountAttribute(): int
{
return $this->getFavoritesCountAttribute();
}
public function getFavoritesCountAttribute()
{
if (! isset($this->attributes['favorites_count'])) {
$this->loadCount('favorites');
}
return $this->attributes['favorites_count'];
}
public function getActiveParticipantsCountAttribute()
{
if (! isset($this->attributes['active_participants_count'])) {
$this->loadCount('activeParticipants');
}
return $this->attributes['active_participants_count'];
}
public function getTracksWithTelephonyCountAttribute()
{
if (! isset($this->attributes['tracks_with_telephony_count'])) {
$this->loadCount('tracksWithTelephony');
}
return $this->attributes['tracks_with_telephony_count'];
}
/**
* @TEMP
* $this->loadCount('tracksWithTelephony') throws null pointer exception
*/
public function countTracksWithTelephony(): int
{
return $this->tracks()->whereNotNull('telephony_provider_id')->count();
}
public function getDuration(): float
{
return $this->getAttribute('duration');
}
public function getDurationForHumansAttribute()
{
return Carbon::now()->subSeconds($this->duration)->diffForHumans(now(), true);
}
public function getDurationForHumansShortAttribute(): string
{
return Carbon::now()->subSeconds($this->duration)->diffForHumans(now(), true, true);
}
public function hasRecordingPreference(): bool
{
re...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"#12121 on JY-20963-fix-import-on-deleted-entity, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.11569149,"height":0.025538707},"on_screen":true,"help_text":"Pull request #12121 exists for current branch JY-20963-fix-import-on-deleted-entity, but pull request details loading failed","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.85638297,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"ServiceTest","depth":6,"bounds":{"left":0.87167555,"top":0.019952115,"width":0.043882977,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'ServiceTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'ServiceTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"4","depth":4,"bounds":{"left":0.30385637,"top":0.12529927,"width":0.007978723,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"388","depth":4,"bounds":{"left":0.31382978,"top":0.12529927,"width":0.012632979,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"9","depth":4,"bounds":{"left":0.32845744,"top":0.12529927,"width":0.007978723,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"103","depth":4,"bounds":{"left":0.33843085,"top":0.12529927,"width":0.011968086,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"4","depth":4,"bounds":{"left":0.35239363,"top":0.12529927,"width":0.007978723,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.3620346,"top":0.123703115,"width":0.00731383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.3693484,"top":0.123703115,"width":0.006981383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\nnamespace Jiminny\\Models;\n\nuse Carbon\\Carbon;\nuse Database\\Factories\\ActivityFactory;\nuse DateTimeInterface;\nuse Exception;\nuse Illuminate\\Contracts\\Auth\\Authenticatable;\nuse Illuminate\\Database\\Eloquent;\nuse Illuminate\\Database\\Eloquent\\Attributes\\Scope;\nuse Illuminate\\Database\\Eloquent\\Builder;\nuse Illuminate\\Database\\Eloquent\\Factories\\Factory;\nuse Illuminate\\Database\\Eloquent\\Factories\\HasFactory;\nuse Illuminate\\Database\\Eloquent\\Relations\\BelongsTo;\nuse Illuminate\\Database\\Eloquent\\Relations\\BelongsToMany;\nuse Illuminate\\Database\\Eloquent\\Relations\\HasMany;\nuse Illuminate\\Database\\Eloquent\\Relations\\HasManyThrough;\nuse Illuminate\\Database\\Eloquent\\Relations\\HasOne;\nuse Illuminate\\Database\\Eloquent\\SoftDeletes;\nuse Illuminate\\Support\\Collection;\nuse Illuminate\\Support\\Facades\\Auth;\nuse InvalidArgumentException;\nuse Jiminny\\Component\\ElasticSearch;\nuse Jiminny\\Component\\MeetingBot;\nuse Jiminny\\Component\\Model\\BitwiseFlagTrait;\nuse Jiminny\\Component\\PlaybackPage\\Comments\\Services\\ActivityCommentService;\nuse Jiminny\\Component\\Sidekick\\SidekickService;\nuse Jiminny\\Component\\Uuid\\UuidAwareInterface;\nuse Jiminny\\Component\\Workflow;\nuse Jiminny\\Contracts;\nuse Jiminny\\Contracts\\Crm\\ProspectInterface;\nuse Jiminny\\DTO\\ImportCall\\Call;\nuse Jiminny\\Events\\Activities\\ActivityTypeUpdated;\nuse Jiminny\\Events\\Activities\\ActivityUpdated;\nuse Jiminny\\Events\\Activities\\ProspectUpdated;\nuse Jiminny\\Events\\Activities\\StageUpdated;\nuse Jiminny\\Events\\Activities\\StatusUpdated;\nuse Jiminny\\Events\\Activities\\TitleUpdated;\nuse Jiminny\\Exceptions\\InvalidArgumentException as InvalidArgumentJiminnyException;\nuse Jiminny\\Exceptions\\LogicException;\nuse Jiminny\\Exceptions\\RuntimeException;\nuse Jiminny\\Models;\nuse Jiminny\\Models\\Activity\\ActivitySummaryLog;\nuse Jiminny\\Models\\Activity\\ActivityUploadSetting;\nuse Jiminny\\Models\\Activity\\AvailabilityNotification;\nuse Jiminny\\Models\\Activity\\CoachRequest;\nuse Jiminny\\Models\\Activity\\Comment;\nuse Jiminny\\Models\\Activity\\Log;\nuse Jiminny\\Models\\Activity\\Message;\nuse Jiminny\\Models\\Activity\\Moment;\nuse Jiminny\\Models\\Activity\\Note;\nuse Jiminny\\Models\\Activity\\ParticipantSpeech;\nuse Jiminny\\Models\\Activity\\Play;\nuse Jiminny\\Models\\Activity\\Question;\nuse Jiminny\\Models\\Activity\\Share;\nuse Jiminny\\Models\\Activity\\Snapshot;\nuse Jiminny\\Models\\Activity\\Stats;\nuse Jiminny\\Models\\Activity\\SubscriptionSet;\nuse Jiminny\\Models\\Activity\\TopicTrigger;\nuse Jiminny\\Models\\Activity\\Transcription;\nuse Jiminny\\Models\\Calendar\\CalendarEvent;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Models\\Crm\\FieldData;\nuse Jiminny\\Models\\ElasticSearch\\ActivityElasticSearchTrait;\nuse Jiminny\\Models\\Feature\\FeatureEnum;\nuse Jiminny\\Models\\Participant\\Connection;\nuse Jiminny\\Models\\Playlist\\Activity as PlaylistActivity;\nuse Jiminny\\Services\\Activity\\ActivityProviderRegistry;\nuse Jiminny\\Services\\Activity\\Import\\DataResolvers\\UpdateCrmDataByStrategy;\nuse Jiminny\\Services\\Activity\\Import\\DataResolvers\\UpdateCrmDataResolverFactory;\nuse Jiminny\\Services\\Activity\\Import\\DataResolvers\\UpdateCrmDataResolverInterface;\nuse Jiminny\\Traits\\Enums;\nuse Jiminny\\Traits\\RequiresUUID;\nuse Jiminny\\Utils\\CurrencyFormatter;\nuse NumberFormatter;\n\nuse function in_array;\n\n/**\n * Jiminny\\Models\\Activity\n *\n * @property null|int $auto_score filled from ES hydrator, not in DB!\n * @property-read Account|null $account\n * @property-read CalendarEvent|null $calendarEvent\n * @property-read Contact|null $contact\n * @property-read Lead|null $lead\n * @property-read Opportunity|null $opportunity\n * @property-read Stage|null $stage\n * @property int $id\n * @property mixed|null $uuid\n * @property string|null $source\n * @property string|null $external_id\n * @property string $provider\n * @property string|null $location\n * @property string|null $telephony_provider_id\n * @property int|null $from_participant_id\n * @property int|null $to_participant_id\n * @property int|null $device_id\n * @property string|null $type\n * @property int|null $playbook_category_id\n * @property int $user_id\n * @property int|null $lead_id\n * @property int|null $account_id\n * @property int|null $contact_id\n * @property int|null $opportunity_id\n * @property int|null $stage_id\n * @property string|null $value\n * @property int|null $crm_configuration_id\n * @property string|null $crm_provider_id\n * @property string|null $language\n * @property int|null $transcription_id\n * @property int $duration\n * @property string $status\n * @property int|null $on_air\n * @property int|null $calendar_event_id\n * @property string $recording_state\n * @property bool|null $recording_preference\n * @property int $recording_reason_code\n * @property int $summary_reminder_sent\n * @property \\Illuminate\\Support\\Carbon|null $log_reminder_sent_at\n * @property \\Illuminate\\Support\\Carbon|null $organizer_notified_at\n * @property bool|null $has_recording_prompt\n * @property bool $is_internal\n * @property int $is_locked\n * @property int $is_recording\n * @property bool|null $is_processed\n * @property bool $is_private\n * @property bool $is_instant_invite\n * @property string|null $poster_path\n * @property string|null $summary\n * @property string|null $title\n * @property string|null $description\n * @property \\Illuminate\\Support\\Carbon|null $scheduled_start_time\n * @property \\Illuminate\\Support\\Carbon|null $scheduled_end_time\n * @property \\Illuminate\\Support\\Carbon|null $actual_start_time\n * @property \\Illuminate\\Support\\Carbon|null $actual_end_time\n * @property int|null $uploaded_by\n * @property \\Illuminate\\Support\\Carbon|null $deleted_at\n * @property \\Illuminate\\Support\\Carbon|null $created_at\n * @property \\Illuminate\\Support\\Carbon|null $updated_at\n * @property string|null $average_score\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Participant> $activeParticipants\n * @property-read int|null $active_participants_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Scorecard\\ActivityScorecardRuleTrigger> $activityScorecardRuleTriggers\n * @property-read int|null $activity_scorecard_rule_triggers_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Scorecard\\ActivityScorecardRule> $activityScorecardRules\n * @property-read int|null $activity_scorecard_rules_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, AvailabilityNotification> $availabilityNotifications\n * @property-read int|null $availability_notifications_count\n * @property-read \\Jiminny\\Models\\PlaybookCategory|null $category\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, CoachRequest> $coachRequests\n * @property-read int|null $coach_requests_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\CoachingFeedback> $coachingFeedbacks\n * @property-read int|null $coaching_feedbacks_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Message> $coachingMessages\n * @property-read int|null $coaching_messages_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Comment> $comments\n * @property-read int|null $comments_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Connection> $connections\n * @property-read int|null $connections_count\n * @property-read Configuration|null $crm\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, FieldData> $data\n * @property-read int|null $data_count\n * @property-read \\Jiminny\\Models\\Device|null $device\n * @property-read \\Kalnoy\\Nestedset\\Collection<int, \\Jiminny\\Models\\Playlist> $favoritePlaylists\n * @property-read int|null $favorite_playlists_count\n * @property-read \\Jiminny\\Models\\Participant|null $from\n * @property-read string|null $activity_title\n * @property-read mixed $comment_count\n * @property-read mixed $duration_for_humans\n * @property-read string $duration_for_humans_short\n * @property-read int $favorite_count\n * @property-read mixed $favorites_count\n * @property-read mixed $formatted_value\n * @property-read string $id_string\n * @property-read \\Jiminny\\Models\\Participant|null $organizer\n * @property-read mixed $play_count\n * @property-read int|null $plays_count\n * @property-read ?ProspectInterface $prospect\n * @property-read string|null $prospect_name\n * @property-read mixed $prospect_type\n * @property-read mixed $share_count\n * @property-read int|null $shares_count\n * @property-read int|null $tracks_with_telephony_count\n * @property-read int|null $visible_comments_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\CoachingFeedback> $latestCoachingFeedbacks\n * @property-read int|null $latest_coaching_feedbacks_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Log> $logs\n * @property-read int|null $logs_count\n * @property-read \\Jiminny\\Models\\Track|null $masterTrack\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Message> $messages\n * @property-read int|null $messages_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Moment> $moments\n * @property-read int|null $moments_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Note> $notes\n * @property-read int|null $notes_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Participant\\Share> $participantShares\n * @property-read int|null $participant_shares_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, ParticipantSpeech> $participantSpeeches\n * @property-read int|null $participant_speeches_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Participant\\ParticipantStats> $participantStats\n * @property-read int|null $participant_stats_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Participant> $participants\n * @property-read int|null $participants_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, PlaylistActivity> $playlistActivities\n * @property-read int|null $playlist_activities_count\n * @property-read \\Kalnoy\\Nestedset\\Collection<int, \\Jiminny\\Models\\Playlist> $playlists\n * @property-read int|null $playlists_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Play> $plays\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Question> $questions\n * @property-read int|null $questions_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Session> $sessions\n * @property-read int|null $sessions_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Share> $shares\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Snapshot> $snapshots\n * @property-read int|null $snapshots_count\n * @property-read Stats|null $stats\n * @property-read \\Jiminny\\Models\\Participant|null $to\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, TopicTrigger> $topicTriggers\n * @property-read int|null $topic_triggers_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Track> $tracks\n * @property-read int|null $tracks_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Track> $tracksWithTelephony\n * @property-read Transcription|null $transcription\n * @property-read \\Jiminny\\Models\\User $user\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Comment> $visibleComments\n *\n * @method static \\Illuminate\\Database\\Eloquent\\Collection<int, static> all($columns = ['*'])\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity chunkByIdDesc($count, callable $callback, $column = null, $alias = null)\n * @method static \\Database\\Factories\\ActivityFactory factory(...$parameters)\n * @method static \\Illuminate\\Database\\Eloquent\\Collection<int, static> get($columns = ['*'])\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity heldBetween(\\Carbon\\Carbon $start, \\Carbon\\Carbon $end)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity idOrUuId($idOrUuid, bool $first = true)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity newModelQuery()\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity newQuery()\n * @method static Builder|Activity onlyTrashed()\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity query()\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity scheduledBetween(\\Carbon\\Carbon $start, \\Carbon\\Carbon $end)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity inOpenDeals()\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity notInOpenDeals()\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity forTeam(int $teamId)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity search(callable $searchQuery, $key = null, $sortByResults = true)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity uuid(string $uuid, bool $first = true)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereAccountId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereActualEndTime($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereActualStartTime($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereAverageScore($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereCalendarEventId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereContactId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereCreatedAt($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereCrmConfigurationId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereCrmProviderId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereDeletedAt($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereDescription($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereDeviceId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereDuration($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereFromParticipantId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereHasRecordingPrompt($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereIsInstantInvite($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereIsInternal($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereIsLocked($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereIsPrivate($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereIsProcessed($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereIsRecording($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereLanguage($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereLeadId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereLocation($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereLogReminderSentAt($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereOnAir($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereOpportunityId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereOrganizerNotifiedAt($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity wherePlaybookCategoryId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity wherePosterPath($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereProvider($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereRecordingPreference($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereRecordingReasonCode($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereRecordingState($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereScheduledEndTime($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereScheduledStartTime($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereSource($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereExternalId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereStageId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereStatus($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereSummary($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereSummaryReminderSent($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereTelephonyProviderId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereTitle($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereToParticipantId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereTranscriptionId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereType($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereUpdatedAt($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereUploadedBy($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereUserId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereUuid($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereValue($value)\n * @method static Builder|Activity withTrashed()\n * @method static Builder|Activity withoutTrashed()\n *\n * @mixin \\Eloquent\n */\nclass Activity extends Model implements\n ElasticSearch\\Contract\\Searchable,\n Workflow\\Workflow\\WorkflowAwareInterface,\n Models\\Contracts\\ActivityContract,\n Contracts\\Model\\ActivityInterface,\n UuidAwareInterface\n{\n use HasFactory;\n\n use Enums;\n use SoftDeletes;\n use RequiresUUID;\n use BitwiseFlagTrait;\n use ElasticSearch\\Model\\Searchable;\n use ActivityElasticSearchTrait;\n\n use Workflow\\Workflow\\WorkflowAware {\n transitionTo as traitTransitionTo;\n }\n\n public const int FLAG_RECORDING_REASON_DEFAULT = 0;\n\n // Recording Prompted but never started\n public const int FLAG_RECORDING_REASON_COMPLIANCE_PROMPT = 1;\n public const int FLAG_RECORDING_REASON_COMPLIANCE_RESUMED = 2;\n public const int FLAG_RECORDING_REASON_NO_AUDIO = 3;\n\n // Recording Disabled by Organization\n public const int FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT = 4;\n\n // Recording was restricted to one-side recordings only\n public const int FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT_ONE_SIDE = 8;\n\n // Recording was not started because it was internal and team setting disabled that.\n public const int FLAG_RECORDING_REASON_TEAM_INTERNAL_DISABLED = 16;\n\n // Recording was not started because it was internal and user setting disabled that.\n public const int FLAG_RECORDING_REASON_USER_INTERNAL_DISABLED = 32;\n\n // Recording was not started because user setting disabled automatic recording.\n public const int FLAG_RECORDING_REASON_USER_AUTOMATIC_DISABLED = 64;\n\n // Recording was not started because team setting disabled automatic recording.\n public const int FLAG_RECORDING_REASON_TEAM_AUTOMATIC_DISABLED = 128;\n\n // Recording was not started because user has overriden default.\n public const int FLAG_RECORDING_REASON_PREFERENCE_OVERRIDE = 256;\n\n // Recording was not started because they don't want internal, and this meeting was not scheduled/imported in time.\n public const int FLAG_RECORDING_REASON_USER_INTERNAL_DISABLED_UNSCHEDULED = 512;\n\n // Recording was not started because their team setting does excludes the meeting type.\n public const int FLAG_RECORDING_REASON_UNSUPPORTED_TYPE = 1024;\n\n // Recording was not started because the external provider disabled it (or recording is missing etc).\n public const int FLAG_RECORDING_REASON_EXTERNALLY_DISABLED = 2048;\n\n // Recording was stopped externally (\"exit-meeting\" Pusher event)\n public const int FLAG_RECORDING_REASON_STOPPED_EXTERNALLY = 384;\n\n // Recording couldn't be started due to Zoom hosting conflict error\n public const int FLAG_RECORDING_REASON_HOSTING_CONFLICT = 448;\n\n // meeting.failed event with reason code BOT_DENIED_FROM_LOBBY\n public const int FLAG_RECORDING_REASON_MEETING_BOT_DENIED_FROM_LOBBY = 4096;\n\n // meeting.failed event with reason code LOBBY_TIMEOUT\n public const int FLAG_RECORDING_REASON_MEETING_BOT_LOBBY_TIMEOUT = 8192;\n\n // meeting.failed event with reason code BOT_KICKED\n public const int FLAG_RECORDING_REASON_MEETING_BOT_KICKED = 16384;\n\n // meeting.failed event with reason code UNKNOWN\n public const int FLAG_RECORDING_REASON_MEETING_BOT_UNKNOWN = 32768;\n\n public const int FLAG_RECORDING_REASON_CONSENT_DENIED = 65536;\n\n // Invalid meeting (e.g. URL is invalid, or the meeting is not found)\n public const int FLAG_RECORDING_REASON_MEETING_BOT_INVALID = 131072;\n\n // The host stopped the recording.\n public const int FLAG_RECORDING_REASON_USER_STOPPED = 262144;\n\n // Recording was not started because an alternative vendor disabled it (or overrode it).\n public const int FLAG_RECORDING_REASON_VENDOR_OVERRIDE = 1048576;\n\n // Login required meeting.failed code\n public const int FLAG_RECORDING_REASON_LOGIN_REQUIRED = 524288;\n\n // Password for meeting was not provided - meeting.failed code\n public const int FLAG_RECORDING_REASON_MEETING_PASSWORD_NOT_PROVIDED = 2097152;\n\n // meeting.failed - when the meeting is locked\n public const int FLAG_RECORDING_REASON_MEETING_IS_LOCKED = 4194304;\n\n // max recording duration reached\n public const int FLAG_RECORDING_REASON_MAX_DURATION_REACHED = 8388608;\n\n // recording size is too small\n public const int FLAG_RECORDING_REASON_EMPTY_RECORDING = 16777216;\n\n // meeting.failed - when bot is redirected to sign in page multiple times\n public const int FLAG_RECORDING_REASON_MAX_RESTART_COUNT_IS_REACHED = 33554432;\n\n // meeting.failed event with reason code CONNECTION_LOST\n public const int FLAG_RECORDING_REASON_MEETING_BOT_CONNECTION_LOST = 67108864;\n\n // recording is corrupted.\n public const int FLAG_RECORDING_REASON_MEDIA_FILE_UNSUPPORTED_MIME_TYPE = 134217728;\n\n // meeting ended in lobby\n public const int FLAG_RECORDING_REASON_MEETING_ENDED_IN_LOBBY = 268435456;\n\n // meeting not started\n public const int FLAG_RECORDING_REASON_REASON_MEETING_NOT_STARTED = 536870912;\n\n // unfinished zoom custom disclaimer\n public const int FLAG_RECORDING_REASON_FEATURE_RULE_NOT_FOUND_ERROR = 1073741824;\n\n // recording download failed - server error\n public const int FLAG_RECORDING_REASON_SERVER_ERROR = 2147483648;\n\n // recording download failed - client code 404\n public const int FLAG_RECORDING_REASON_NOT_FOUND = 2147483649;\n\n // recording download failed - client code 401, 403\n public const int FLAG_RECORDING_REASON_ACCESS_DENIED = 2147483650;\n\n // recording download failed - client code 429\n public const int FLAG_RECORDING_REASON_TOO_MANY_REQUESTS = 2147483651;\n\n // recording download failed - unknown client error\n public const int FLAG_RECORDING_REASON_CLIENT_ERROR = 2147483652;\n\n // recording download failed - unknown error\n public const int FLAG_RECORDING_REASON_UNKNOWN_ERROR = 2147483653;\n\n // It has been setup ahead of time through calendar\n public const string STATUS_SCHEDULED = 'scheduled';\n\n // It is awaiting audio.\n public const string STATUS_PENDING = 'pending';\n\n // Participant(s) dialed in, awaiting organizer.\n public const string STATUS_RINGING = 'ringing';\n\n // Call is in progress.\n public const string STATUS_IN_PROGRESS = 'in-progress';\n\n // It has ended.\n public const string STATUS_COMPLETED = 'completed';\n\n // Cancelled prior to starting.\n public const string STATUS_CANCELLED = 'canceled';\n\n public const string STATUS_DUPLICATED = 'duplicated'; // duplicated conference\n\n public const string STATUS_STARTING_SOON = 'starting-soon';\n\n public const string STATUS_BOT_CREATE_SENT = 'bot-create-sent';\n\n public const string STATUS_BOT_INSTANCE_WORKER_ASSIGNED = 'worker-assigned';\n\n public const string STATUS_BOT_INSTANCE_STARTED = 'bot-started';\n\n // When bot instance is waiting in lobby\n public const string STATUS_BOT_INSTANCE_WAITING_LOBBY = 'bot-waiting';\n\n public const string STATUS_BUSY = 'busy';\n public const string STATUS_NO_ANSWER = 'no-answer';\n public const string STATUS_FAILED = 'failed'; // Used by SMS too\n\n // SMS related\n public const string STATUS_ACCEPTED = 'accepted';\n public const string STATUS_QUEUED = 'queued';\n public const string STATUS_SENDING = 'sending';\n public const string STATUS_SENT = 'sent';\n public const string STATUS_DELIVERED = 'delivered';\n public const string STATUS_UNDELIVERED = 'undelivered';\n public const string STATUS_RECEIVING = 'receiving';\n public const string STATUS_RECEIVED = 'received';\n public const string STATUS_RESENT = 'resent';\n\n public const array SMS_STATUSES = [\n Activity::STATUS_RECEIVED,\n Activity::STATUS_SENT,\n Activity::STATUS_DELIVERED,\n ];\n\n public const array SOFT_PHONE_CONFERENCE_STATUSES = [\n Activity::STATUS_IN_PROGRESS,\n Activity::STATUS_COMPLETED,\n ];\n\n // @todo refactor prefix from `TYPE_` to `CHANNEL_`\n public const string TYPE_SOFTPHONE = 'softphone';\n public const string TYPE_SOFTPHONE_INBOUND = 'softphone-inbound';\n public const string TYPE_CONFERENCE = 'conference';\n public const string TYPE_SMS_INBOUND = 'sms-inbound';\n public const string TYPE_SMS_OUTBOUND = 'sms-outbound';\n public const string TYPE_EMAIL_INBOUND = 'email-inbound';\n public const string TYPE_EMAIL_OUTBOUND = 'email-outbound';\n\n public const array CHANNELS = [\n self::TYPE_SOFTPHONE,\n self::TYPE_SOFTPHONE_INBOUND,\n self::TYPE_CONFERENCE,\n self::TYPE_SMS_INBOUND,\n self::TYPE_SMS_OUTBOUND,\n self::TYPE_EMAIL_INBOUND,\n self::TYPE_EMAIL_OUTBOUND,\n ];\n\n public const array PLAYABLE_CHANNELS = [\n self::TYPE_SOFTPHONE,\n self::TYPE_SOFTPHONE_INBOUND,\n self::TYPE_CONFERENCE,\n ];\n\n // Recording States\n public const string RECORDING_OFF = 'off'; // Default state\n public const string RECORDING_IN_PROGRESS = 'in-progress';\n public const string RECORDING_PAUSED = 'paused';\n public const string RECORDING_STOPPED = 'stopped'; // To never be resumed.\n public const string RECORDING_RECORDED = 'recorded'; // At least some portion of it was recorded.\n public const string RECORDING_FAILED = 'failed'; // Recording was attempted but failed for some reason.\n\n // Live Stream States\n public const int ON_AIR_DEFAULT = 0;\n public const int ON_AIR_READY = 1;\n public const int ON_AIR_PREPARING = 2;\n public const int ON_AIR_STREAMING = 3;\n public const int ON_AIR_FINISHED = 4;\n public const int ON_AIR_NOT_STREAMED = 5;\n public const int ON_AIR_ERROR = -1;\n\n public const string SOURCE_GONG = 'gong';\n public const string SOURCE_CHORUS = 'chorus';\n public const string SOURCE_OUTLOOK = 'outlook';\n public const string SOURCE_GOOGLE = 'google';\n\n // Activity Providers\n public const string PROVIDER_TWILIO = 'twilio'; // XXX: This is run via the Jiminny Provider.\n public const string PROVIDER_OUTREACH = 'outreach';\n public const string PROVIDER_ZOOM_BOT = 'zoom-bot';\n public const string PROVIDER_SALESLOFT = 'salesloft';\n public const string PROVIDER_GOOGLE = 'google';\n public const string PROVIDER_AIRCALL = 'aircall';\n public const string PROVIDER_JUSTCALL = 'justcall';\n public const string PROVIDER_GOOGLE_MEET = 'google-meet';\n public const string PROVIDER_GONG = 'gong';\n public const string PROVIDER_HUBSPOT = 'hubspot';\n public const string PROVIDER_CLOSE = 'close';\n public const string PROVIDER_TEAMS = 'ms-teams';\n public const string PROVIDER_SALESFORCE = 'salesforce';\n public const string PROVIDER_GROOVE = 'groove';\n public const string PROVIDER_XANT = 'xant';\n public const string PROVIDER_OFFICE = 'office';\n public const string PROVIDER_NATTERBOX = 'natterbox';\n public const string PROVIDER_RINGCENTRAL = 'ringcentral';\n public const string PROVIDER_RINGCENTRAL_VIDEO = 'ringcentral-video';\n public const string PROVIDER_GOTOMEETING = 'go-to-meeting';\n public const string PROVIDER_DEMODESK = 'demo-desk';\n public const string PROVIDER_DIALPAD = 'dialpad';\n public const string PROVIDER_ZOOM_PHONE = 'zoom-phone';\n public const string PROVIDER_CLOUDCALL = 'cloudcall';\n public const string PROVIDER_CLOUDCALL_US = 'cloudcall-us';\n public const string PROVIDER_EIGHT_BY_EIGHT = 'eight-by-eight'; // \"8x8\" UK\n public const string PROVIDER_EIGHT_BY_EIGHT_CA = 'eight-by-eight-ca'; // \"8x8\" Canada\n public const string PROVIDER_EIGHT_BY_EIGHT_AP = 'eight-by-eight-ap'; // \"8x8\" Australia\n public const string PROVIDER_EIGHT_BY_EIGHT_US_EAST = 'eight-by-eight-use'; // \"8x8\" US East\n public const string PROVIDER_EIGHT_BY_EIGHT_US_WEST = 'eight-by-eight-usw'; // \"8x8\" US West\n public const string PROVIDER_CONNECT_AND_SELL = 'connect-and-sell';\n public const string PROVIDER_CLOUD_TALK = 'cloud-talk';\n public const string PROVIDER_AMAZON_CONNECT = 'amazon-connect';\n public const string PROVIDER_VONAGE = 'vonage';\n public const string PROVIDER_MIGRATOR = 'migrator';\n public const string PROVIDER_UPLOADER = 'uploader';\n public const string PROVIDER_TALKDESK = 'talkdesk';\n public const string PROVIDER_TWILIO_FLEX = 'twilio-flex';\n public const string PROVIDER_TWILIO_FLEX_DIRECT = 'twilio-flex-direct';\n public const string PROVIDER_TWILIO_VIDEO = 'twilio-video';\n public const string PROVIDER_AVAYA = 'avaya';\n public const string PROVIDER_TELUS = 'telus';\n public const string PROVIDER_FIVE_NINE = 'five-nine';\n public const string PROVIDER_APOLLO = 'apollo';\n public const string PROVIDER_ORUM = 'orum';\n public const string PROVIDER_BLOOBIRDS = 'bloobirds';\n\n /**\n * @const API_PROVIDERS\n * A list of integrations that import calls via API instead of webhooks\n */\n public const array API_PROVIDERS = [\n self::PROVIDER_OUTREACH,\n self::PROVIDER_SALESLOFT,\n self::PROVIDER_HUBSPOT,\n self::PROVIDER_GROOVE,\n self::PROVIDER_XANT,\n self::PROVIDER_NATTERBOX,\n self::PROVIDER_CLOUDCALL,\n self::PROVIDER_CLOUDCALL_US,\n self::PROVIDER_EIGHT_BY_EIGHT,\n self::PROVIDER_EIGHT_BY_EIGHT_CA,\n self::PROVIDER_EIGHT_BY_EIGHT_AP,\n self::PROVIDER_EIGHT_BY_EIGHT_US_EAST,\n self::PROVIDER_EIGHT_BY_EIGHT_US_WEST,\n self::PROVIDER_CONNECT_AND_SELL,\n self::PROVIDER_CLOUD_TALK,\n self::PROVIDER_AMAZON_CONNECT,\n self::PROVIDER_VONAGE,\n self::PROVIDER_TALKDESK,\n self::PROVIDER_TWILIO_VIDEO,\n self::PROVIDER_TWILIO_FLEX,\n self::PROVIDER_TWILIO_FLEX_DIRECT,\n self::PROVIDER_FIVE_NINE,\n self::PROVIDER_APOLLO,\n self::PROVIDER_ORUM,\n self::PROVIDER_BLOOBIRDS,\n self::PROVIDER_RINGCENTRAL,\n self::PROVIDER_AVAYA,\n self::PROVIDER_TELUS,\n ];\n\n public const array FINITE_STATES = [\n self::TYPE_SOFTPHONE => [\n self::STATUS_COMPLETED,\n self::STATUS_FAILED,\n self::STATUS_NO_ANSWER,\n self::STATUS_BUSY,\n ],\n self::TYPE_SOFTPHONE_INBOUND => [\n self::STATUS_COMPLETED,\n self::STATUS_FAILED,\n self::STATUS_NO_ANSWER,\n self::STATUS_BUSY,\n ],\n self::TYPE_CONFERENCE => self::FINITE_STATES_CONFERENCE,\n ];\n\n public const array FINITE_STATES_CONFERENCE = [\n self::STATUS_COMPLETED,\n self::STATUS_FAILED,\n self::STATUS_CANCELLED,\n ];\n\n public const array MEETING_BOT_JOIN_ATTEMPTED = [\n self::STATUS_BOT_INSTANCE_WAITING_LOBBY,\n self::STATUS_BOT_INSTANCE_STARTED,\n ];\n\n public static array $enumStatuses = [\n self::STATUS_SCHEDULED,\n self::STATUS_PENDING,\n self::STATUS_RINGING,\n self::STATUS_IN_PROGRESS,\n self::STATUS_COMPLETED,\n self::STATUS_CANCELLED,\n self::STATUS_BUSY,\n self::STATUS_NO_ANSWER,\n self::STATUS_FAILED,\n self::STATUS_ACCEPTED,\n self::STATUS_QUEUED,\n self::STATUS_SENDING,\n self::STATUS_SENT,\n self::STATUS_RESENT,\n self::STATUS_DELIVERED,\n self::STATUS_UNDELIVERED,\n self::STATUS_RECEIVING,\n self::STATUS_RECEIVED,\n self::STATUS_BOT_INSTANCE_WAITING_LOBBY,\n self::STATUS_STARTING_SOON,\n self::STATUS_BOT_INSTANCE_WORKER_ASSIGNED,\n self::STATUS_BOT_INSTANCE_STARTED,\n self::STATUS_DUPLICATED,\n ];\n\n public static array $enumProviders = [\n self::PROVIDER_TWILIO,\n self::PROVIDER_OUTREACH,\n self::PROVIDER_ZOOM_BOT,\n self::PROVIDER_SALESLOFT,\n self::PROVIDER_AIRCALL,\n self::PROVIDER_JUSTCALL,\n self::PROVIDER_GOOGLE_MEET,\n self::PROVIDER_GONG,\n self::PROVIDER_HUBSPOT,\n self::PROVIDER_CLOSE,\n self::PROVIDER_TEAMS,\n self::PROVIDER_SALESFORCE,\n self::PROVIDER_GROOVE,\n self::PROVIDER_XANT,\n self::PROVIDER_GOOGLE,\n self::PROVIDER_OFFICE,\n self::PROVIDER_NATTERBOX,\n self::PROVIDER_RINGCENTRAL,\n self::PROVIDER_RINGCENTRAL_VIDEO,\n self::PROVIDER_GOTOMEETING,\n self::PROVIDER_DEMODESK,\n self::PROVIDER_DIALPAD,\n self::PROVIDER_ZOOM_PHONE,\n self::PROVIDER_CLOUDCALL,\n self::PROVIDER_CLOUDCALL_US,\n self::PROVIDER_EIGHT_BY_EIGHT,\n self::PROVIDER_EIGHT_BY_EIGHT_CA,\n self::PROVIDER_EIGHT_BY_EIGHT_AP,\n self::PROVIDER_EIGHT_BY_EIGHT_US_EAST,\n self::PROVIDER_EIGHT_BY_EIGHT_US_WEST,\n self::PROVIDER_CONNECT_AND_SELL,\n self::PROVIDER_CLOUD_TALK,\n self::PROVIDER_AMAZON_CONNECT,\n self::PROVIDER_VONAGE,\n self::PROVIDER_TALKDESK,\n self::PROVIDER_TWILIO_FLEX,\n self::PROVIDER_TWILIO_FLEX_DIRECT,\n self::PROVIDER_TWILIO_VIDEO,\n self::PROVIDER_AVAYA,\n self::PROVIDER_TELUS,\n self::PROVIDER_FIVE_NINE,\n self::PROVIDER_APOLLO,\n self::PROVIDER_ORUM,\n self::PROVIDER_BLOOBIRDS,\n ];\n\n public static $enumRecordingStates = [\n self::RECORDING_OFF, // Default state\n self::RECORDING_IN_PROGRESS,\n self::RECORDING_PAUSED,\n self::RECORDING_STOPPED,\n self::RECORDING_RECORDED,\n self::RECORDING_FAILED,\n ];\n\n // @Important:\n // This collection is not used anywhere, and is fully duplicated by the Channels const.\n // Validate if it is referred somehow via the enum trait, and if not, remove it entirely.\n // An even better strategy will be to move all those constants to a dedicated class\n protected array $enumTypes = [\n self::TYPE_SOFTPHONE,\n self::TYPE_SOFTPHONE_INBOUND,\n self::TYPE_CONFERENCE,\n self::TYPE_SMS_INBOUND,\n self::TYPE_SMS_OUTBOUND,\n self::TYPE_EMAIL_INBOUND,\n self::TYPE_EMAIL_OUTBOUND,\n ];\n\n protected static $enumFailedStatuses = [\n self::STATUS_NO_ANSWER,\n self::STATUS_FAILED,\n self::STATUS_BUSY,\n self::STATUS_CANCELLED,\n ];\n\n protected $table = 'activities';\n\n protected $fillable = [\n // Type of activity.\n 'type', // @todo refactor to `channel`\n // The activity type.\n 'playbook_category_id',\n // User who hosts the activity.\n 'user_id',\n // Related Lead record (if applicable)\n 'lead_id',\n // Related Account record (if applicable)\n 'account_id',\n // Related Contact record (if applicable)\n 'contact_id',\n // Related Opportunity record (if applicable)\n 'opportunity_id',\n // Stage of activity.\n 'stage_id',\n // Value of opportunity.\n 'value',\n // If the activity relates to a CRM task.\n 'crm_provider_id',\n // If the activity was created through an external device.\n 'device_id',\n // the activity's language code\n 'language',\n // transcription id\n 'transcription_id',\n // Duration of the call, with microseconds precision.\n 'duration',\n // One of enumStatuses above.\n 'status',\n // Have we reminded them to log the call?\n 'log_reminder_sent_at',\n // If activity is private or inter-org, flagged here.\n 'is_internal',\n // Managers and above can mark a call as private, to exclude it from other team members\n 'is_private',\n 'is_processed',\n // Boolean for this activity being instant invite handled.\n 'is_instant_invite',\n // If activity is in recording state, flagged here.\n 'recording_state',\n // If activity recording is overidden from default.\n 'recording_preference',\n // if recording did (not) happen, why that is\n 'recording_reason_code',\n // Average score, updated during\n 'average_score',\n // Summary that the organizer has taken after the call.\n 'summary',\n // Subject of the activity, usually taken from calendar event.\n 'title',\n // Description of the activity, usually taken from calendar event.\n 'description',\n // Start time, usually taken from calendar event.\n 'scheduled_start_time',\n // End time, usually taken from calendar event.\n 'scheduled_end_time',\n // When the call actually started.\n 'actual_start_time',\n // When the call actually ended.\n 'actual_end_time',\n // SMS: Message reference\n 'telephony_provider_id',\n // SMS: Participant who sent message\n 'from_participant_id',\n // SMS: Participant who should receive the message\n 'to_participant_id',\n // When an external guest joins an organizers meeting room and the organizer is not present,\n // send them an SMS notification that someone has joined.\n 'organizer_notified_at',\n // where was the activity imported from\n 'source',\n // The id in the source system (e.g. the bot id in Recall.ai)\n 'external_id',\n // The provider, by default it is twilio.\n 'provider',\n // Meeting location url\n 'location',\n // The snapshot for displaying a poster image.\n 'poster_path',\n 'crm_configuration_id',\n // If there is an automated message that the conversation is being recorded\n 'has_recording_prompt',\n // If the activity is being live-streamed\n 'on_air',\n 'calendar_event_id',\n ];\n\n protected $appends = [\n 'id_string',\n 'organizer',\n ];\n\n protected $hidden = [\n 'uuid',\n ];\n\n protected $visible = [\n 'id_string',\n 'type',\n 'duration',\n 'average_score',\n 'status',\n 'log_reminder_sent_at',\n 'title',\n 'description',\n 'is_internal',\n 'scheduled_start_time',\n 'scheduled_end_time',\n 'actual_start_time',\n 'actual_end_time',\n 'user',\n 'category',\n 'account',\n 'contact',\n 'opportunity',\n 'lead',\n 'stage',\n 'stats',\n 'participants',\n 'playlists',\n 'tracks',\n 'comments',\n 'plays',\n 'coachingFeedbacks',\n 'shares',\n 'favorites',\n 'language',\n 'transcription',\n 'is_private',\n 'is_instant_invite',\n 'on_air',\n 'calendar_event_id',\n ];\n\n protected function casts(): array\n {\n return [\n 'scheduled_start_time' => 'datetime',\n 'scheduled_end_time' => 'datetime',\n 'actual_start_time' => 'datetime',\n 'actual_end_time' => 'datetime',\n 'organizer_notified_at' => 'datetime',\n 'log_reminder_sent_at' => 'datetime',\n 'is_internal' => 'boolean',\n 'duration' => 'integer',\n 'average_score' => 'decimal:2',\n 'is_private' => 'boolean',\n 'is_processed' => 'boolean',\n 'is_instant_invite' => 'boolean',\n 'value' => 'decimal:2',\n 'recording_preference' => 'boolean',\n 'recording_reason_code' => 'integer',\n 'has_recording_prompt' => 'boolean',\n 'on_air' => 'integer',\n ];\n }\n\n protected static function boot()\n {\n parent::boot();\n\n static::updated(static function (Activity $activity) {\n // If activity is about to start (pending, ringing, in-progress) or event is scheduled in less than 1 week\n if (in_array($activity->status, [Activity::STATUS_PENDING, Activity::STATUS_RINGING, Activity::STATUS_IN_PROGRESS], true) ||\n ($activity->scheduled_start_time && (int) $activity->scheduled_start_time->diffInWeeks(new Carbon(), true) < 1)) {\n if ($activity->isDirty('status')) {\n event(new StatusUpdated($activity));\n }\n\n if ($activity->isDirty('stage_id')) {\n event(new StageUpdated($activity));\n }\n\n if ($activity->isDirty(['lead_id', 'account_id', 'contact_id'])) {\n event(new ProspectUpdated($activity));\n }\n\n if ($activity->isDirty('opportunity_id')) {\n event(new ActivityUpdated($activity, 'activity.opportunity-updated', Auth::user()));\n }\n\n if ($activity->isDirty('title')) {\n event(new TitleUpdated($activity));\n }\n }\n\n if ($activity->isDirty('playbook_category_id')) {\n event(new ActivityTypeUpdated($activity));\n }\n });\n\n static::deleted(static function (Activity $activity) {\n // Hard delete associated playlistActivities\n $activity->playlistActivities()->delete();\n });\n }\n\n public function getOrganizerAttribute(): ?Participant\n {\n $participant = $this->participants()->where('user_id', $this->user_id)->first();\n\n if (! $participant instanceof Participant && $participant !== null) {\n throw new RuntimeException(sprintf('$participant must be an instance of \"%s\" or null', Participant::class));\n }\n\n return $participant;\n }\n\n public function getFormattedValueAttribute()\n {\n $currencyCode = 'USD';\n if ($this->opportunity) {\n $currencyCode = $this->opportunity->getCurrencyCode();\n }\n\n $formatter = new CurrencyFormatter();\n $formatter->setTextAttribute(NumberFormatter::CURRENCY_CODE, $currencyCode);\n $formatter->setAttribute(NumberFormatter::MAX_FRACTION_DIGITS, 0);\n\n return $formatter->format($this->value, $currencyCode);\n }\n\n public function getProspectNameAttribute(): ?string\n {\n $prospectName = null;\n\n if ($this->lead_id) {\n $prospectName = $this->lead->name;\n } elseif ($this->contact_id) {\n $prospectName = $this->contact->name;\n } elseif ($this->account_id) {\n $prospectName = $this->account->name;\n }\n\n return $prospectName;\n }\n\n public function getProspectName(): ?string\n {\n /** @var string|null */\n return $this->getAttribute('prospect_name');\n }\n\n /**\n * Get activity title depending on prospect or title\n */\n public function getActivityTitleAttribute(): ?string\n {\n $activityTitle = null;\n if ($this->prospect && $this->prospect->getName()) {\n if ($this->account_id) {\n $activityTitle = $this->account->name;\n } elseif ($this->lead_id) {\n $activityTitle = $this->lead->company;\n } elseif ($this->contact_id) {\n $activityTitle = $this->contact->account ? $this->contact->account->name : $this->contact->name;\n }\n } elseif ($this->title) {\n $activityTitle = $this->title;\n }\n\n return $activityTitle;\n }\n\n public function wasRecentlyCreated(): bool\n {\n return $this->wasRecentlyCreated;\n }\n\n public function getProspectTypeAttribute()\n {\n $prospectType = null;\n\n if ($this->lead_id) {\n $prospectType = 'Lead';\n } elseif ($this->contact_id) {\n $prospectType = 'Contact';\n } elseif ($this->account_id) {\n $prospectType = 'Account';\n }\n\n return $prospectType;\n }\n\n /**\n * Return the best match for prospect. Results are in the following order of priority:\n * 1. Lead\n * 2. Contact\n * 3. Account\n * 4. NULL\n */\n public function getProspectAttribute(): ?ProspectInterface\n {\n if ($this->hasLead()) {\n return $this->getLead();\n }\n\n if ($this->hasContact()) {\n return $this->getContact();\n }\n\n if ($this->hasAccount()) {\n return $this->getAccount();\n }\n\n return null;\n }\n\n public function getTitleAttribute($value): ?string\n {\n return \\getActivityTitleAttribute(\n $this->user->name,\n $this->getType(),\n $value,\n $this->prospect->name ?? null,\n $this->from->national_phone_number ?? null\n );\n }\n\n public function getTitle(): ?string\n {\n return $this->getAttribute('title');\n }\n\n public function getSummary(): ?string\n {\n return $this->getAttribute('summary');\n }\n\n public function isInternal(): bool\n {\n return $this->getAttribute('is_internal');\n }\n\n public function getIsPrivate(): bool\n {\n return $this->getAttribute('is_private');\n }\n\n public function getDescription(): ?string\n {\n return $this->getAttribute('description');\n }\n\n public function hasTitle(): bool\n {\n return $this->getOriginal('title') !== null;\n }\n\n public function getPlayCountAttribute()\n {\n return $this->getPlaysCountAttribute();\n }\n\n public function getPlaysCountAttribute()\n {\n if (! isset($this->attributes['plays_count'])) {\n $this->loadCount('plays');\n }\n\n return $this->attributes['plays_count'];\n }\n\n public function getCommentCountAttribute()\n {\n return $this->getCommentsCountAttribute();\n }\n\n public function getCommentsCountAttribute()\n {\n if (! isset($this->attributes['comments_count'])) {\n $this->loadCount('comments');\n }\n\n return $this->attributes['comments_count'];\n }\n\n public function getVisibleCommentsCountAttribute()\n {\n if (! isset($this->attributes['visible_comments_count'])) {\n $activityCommentsService = app(ActivityCommentService::class);\n $user = Auth::user() instanceof User ? Auth::user() : null;\n $this->attributes['visible_comments_count'] = $activityCommentsService\n ->getVisibleCommentsCount($this, $user);\n }\n\n return $this->attributes['visible_comments_count'];\n }\n\n public function getShareCountAttribute()\n {\n return $this->getSharesCountAttribute();\n }\n\n public function getSharesCountAttribute()\n {\n if (! isset($this->attributes['shares_count'])) {\n $this->loadCount('shares');\n }\n\n return $this->attributes['shares_count'];\n }\n\n\n /**\n * Get the count of favorites playlists this activity appears in\n */\n public function getFavoriteCountAttribute(): int\n {\n return $this->getFavoritesCountAttribute();\n }\n\n public function getFavoritesCountAttribute()\n {\n if (! isset($this->attributes['favorites_count'])) {\n $this->loadCount('favorites');\n }\n\n return $this->attributes['favorites_count'];\n }\n\n public function getActiveParticipantsCountAttribute()\n {\n if (! isset($this->attributes['active_participants_count'])) {\n $this->loadCount('activeParticipants');\n }\n\n return $this->attributes['active_participants_count'];\n }\n\n public function getTracksWithTelephonyCountAttribute()\n {\n if (! isset($this->attributes['tracks_with_telephony_count'])) {\n $this->loadCount('tracksWithTelephony');\n }\n\n return $this->attributes['tracks_with_telephony_count'];\n }\n\n /**\n * @TEMP\n * $this->loadCount('tracksWithTelephony') throws null pointer exception\n */\n public function countTracksWithTelephony(): int\n {\n return $this->tracks()->whereNotNull('telephony_provider_id')->count();\n }\n\n public function getDuration(): float\n {\n return $this->getAttribute('duration');\n }\n\n public function getDurationForHumansAttribute()\n {\n return Carbon::now()->subSeconds($this->duration)->diffForHumans(now(), true);\n }\n\n public function getDurationForHumansShortAttribute(): string\n {\n return Carbon::now()->subSeconds($this->duration)->diffForHumans(now(), true, true);\n }\n\n public function hasRecordingPreference(): bool\n {\n return $this->getAttribute('recording_preference') !== null;\n }\n\n public function getRecordingPreference()\n {\n return $this->getAttribute('recording_preference');\n }\n\n /** @return BelongsTo<User, self> */\n public function user(): BelongsTo\n {\n return $this->belongsTo(User::class)->with('group');\n }\n\n public function device()\n {\n return $this->belongsTo(Device::class);\n }\n\n public function category()\n {\n return $this->belongsTo(PlaybookCategory::class, 'playbook_category_id');\n }\n\n public function getCategory(): ?PlaybookCategory\n {\n return $this->getAttribute('category');\n }\n\n public function getPlaybookCategoryId(): ?int\n {\n return $this->getAttribute('playbook_category_id');\n }\n\n public function hasStats(): bool\n {\n return $this->getAttribute('stats') !== null;\n }\n\n public function getStats(): ?Stats\n {\n return $this->getAttribute('stats');\n }\n\n public function stats(): HasOne\n {\n return $this->hasOne(Stats::class);\n }\n\n public function participantStats(): Eloquent\\Relations\\HasManyThrough\n {\n return $this->hasManyThrough(\n Models\\Participant\\ParticipantStats::class,\n Participant::class,\n 'activity_id',\n 'participant_id'\n );\n }\n\n public function getParticipantStats(): Eloquent\\Collection\n {\n return $this->getAttribute('participantStats');\n }\n\n public function account()\n {\n return $this->belongsTo(Account::class);\n }\n\n public function contact()\n {\n return $this->belongsTo(Contact::class)->with(['account']);\n }\n\n public function lead()\n {\n return $this->belongsTo(Lead::class)->with(['stage', 'recordType']);\n }\n\n /**\n * @return BelongsTo<Opportunity, self>\n */\n public function opportunity(): BelongsTo\n {\n /** @var BelongsTo<Opportunity, self> */\n return $this->belongsTo(Opportunity::class);\n }\n\n public function stage()\n {\n return $this->belongsTo(Stage::class);\n }\n\n /**\n * @return HasMany<Session>\n */\n public function sessions(): HasMany\n {\n return $this->hasMany(Session::class);\n }\n\n /**\n * @return HasMany|ParticipantSpeech[]|Eloquent\\Collection\n */\n public function participantSpeeches()\n {\n return $this->hasMany(ParticipantSpeech::class);\n }\n\n public function getParticipantSpeeches(): Eloquent\\Collection\n {\n return $this->getAttribute('participantSpeeches');\n }\n\n /**\n * @return HasMany|Log[]|Eloquent\\Collection\n */\n public function logs()\n {\n return $this->hasMany(Log::class);\n }\n\n /**\n * @return HasMany|Moment[]|Eloquent\\Collection\n */\n public function moments()\n {\n return $this->hasMany(Moment::class);\n }\n\n /**\n * @return HasMany|Note[]|Eloquent\\Collection\n */\n public function notes()\n {\n return $this->hasMany(Note::class);\n }\n\n /**\n * @return Eloquent\\Collection|Note[]\n */\n public function getNotes(): Eloquent\\Collection\n {\n return $this->getAttribute('notes');\n }\n\n /**\n * @return HasMany|Message[]|Eloquent\\Collection\n */\n public function messages()\n {\n return $this->hasMany(Message::class);\n }\n\n public function coachingMessages(): HasMany\n {\n return $this->hasMany(Message::class)\n ->where('is_private', 1);\n }\n\n public function getCoachingMessages(): Eloquent\\Collection\n {\n return $this->getAttribute('coachingMessages');\n }\n\n public function participants(): HasMany\n {\n return $this->hasMany(Participant::class);\n }\n\n public function getSnapshots(): Eloquent\\Collection\n {\n return $this->getAttribute('snapshots');\n }\n\n /** @return HasMany<Track> */\n public function tracks(): HasMany\n {\n return $this->hasMany(Track::class);\n }\n\n public function tracksWithTelephony(): HasMany\n {\n return $this->hasMany(Track::class)->whereNotNull('telephony_provider_id');\n }\n\n public function getTracksWithTelephony(): Eloquent\\Collection\n {\n return $this->getAttribute('tracksWithTelephony');\n }\n\n /** @return Collection|Track[] */\n public function getTracks(): Eloquent\\Collection\n {\n return $this->getAttribute('tracks');\n }\n\n public function masterTrack(): HasOne\n {\n return $this->hasOne(Track::class)->where('is_master', 1)\n ->whereIn('format', [Track::FORMAT_WAV, Track::FORMAT_M3U8])\n ->latest();\n }\n\n public function getMasterTrack(): ?Track\n {\n /** @var Track|null */\n return $this->getAttribute('masterTrack');\n }\n\n public function transcription(): Eloquent\\Relations\\BelongsTo\n {\n return $this->belongsTo(Transcription::class, 'transcription_id');\n }\n\n public function findTranscriptionPromptSummaries(): Collection\n {\n $transcriptionId = $this->getTranscriptionId();\n if (is_null($transcriptionId)) {\n return new Collection();\n }\n\n return Models\\AiPrompt::query()\n ->where('transcription_id', $transcriptionId)\n ->get();\n }\n\n public function getTranscription(): Transcription\n {\n return $this->getAttribute('transcription');\n }\n\n public function hasTranscription(): bool\n {\n return $this->getAttribute('transcription') !== null;\n }\n\n public function setTranscriptionId(int $transcriptionId): Activity\n {\n $this->setAttribute('transcription_id', $transcriptionId);\n\n return $this;\n }\n\n public function unsetTranscriptionId(): self\n {\n $this->setAttribute('transcription_id', null);\n\n return $this;\n }\n\n public function getTranscriptionId(): ?int\n {\n return $this->getAttribute('transcription_id');\n }\n\n /** @deprecated */\n public function hasTranscriptionId(): bool\n {\n return $this->getAttribute('transcription_id') !== null;\n }\n\n public function coachRequests()\n {\n return $this->hasMany(CoachRequest::class);\n }\n\n public function availabilityNotifications()\n {\n return $this->hasMany(AvailabilityNotification::class);\n }\n\n public function processingStates()\n {\n return $this->hasMany(Models\\Activity\\ActivityProcessingState::class);\n }\n\n public function uploadSettings()\n {\n return $this->hasMany(ActivityUploadSetting::class);\n }\n\n public function comments()\n {\n return $this->hasMany(Comment::class);\n }\n\n public function getComments(): Eloquent\\Collection\n {\n return $this->getAttribute('comments');\n }\n\n public function visibleComments()\n {\n $rel = $this->hasMany(Comment::class);\n // Doesn't have auth()->user() in some tests, breaks the build\n if ($user = auth()->user()) {\n return $rel->visibleThreads($user->id);\n }\n\n return $rel;\n }\n\n public function snapshots(): HasMany\n {\n return $this->hasMany(Snapshot::class);\n }\n\n public function calendarEvent()\n {\n return $this->belongsTo(CalendarEvent::class);\n }\n\n public function getCalendarEvent(): ?CalendarEvent\n {\n return $this->getAttribute('calendarEvent');\n }\n\n public function latestCoachingFeedbacks(): HasMany\n {\n return $this->hasMany(CoachingFeedback::class)->latest();\n }\n\n public function playlists(): BelongsToMany\n {\n return $this->belongsToMany(Playlist::class, 'playlist_activities')\n ->withPivot('id', 'uuid', 'user_id', 'start_time', 'end_time')\n ->using(PlaylistActivity::class)\n ->withTimestamps();\n }\n\n public function coachingFeedbacks(): HasMany\n {\n return $this->hasMany(CoachingFeedback::class);\n }\n\n /**\n * @return Eloquent\\Collection|CoachingFeedback[]\n */\n public function getCoachingFeedback(?int $visibility = null): Eloquent\\Collection\n {\n $feedbacks = $this->coachingFeedbacks();\n if ($visibility !== null) {\n $feedbacks = $feedbacks->where('visibility', $visibility);\n }\n\n return $feedbacks->get();\n }\n\n /** @return Eloquent\\Collection<int, PlaylistActivity> */\n public function favoritedBy(User $user): Eloquent\\Collection\n {\n return $this->favorites()->where('user_id', $user->getId())->get();\n }\n\n /**\n * Checks whether consumer has added this activity to their favorites playlist\n * In addition a default playlist gets created if not already present\n */\n public function wasFavoritedBy(User $user): bool\n {\n $playlist = $user->favoritePlaylist();\n\n return $playlist\n ->activities()\n ->where('activity_id', '=', $this->getId())\n ->exists();\n }\n\n /**\n * @return HasMany<PlaylistActivity>\n */\n public function playlistActivities(): HasMany\n {\n return $this->hasMany(PlaylistActivity::class);\n }\n\n /**\n * @return HasManyThrough<Playlist>\n */\n public function favoritePlaylists(): HasManyThrough\n {\n return $this->hasManyThrough(\n Playlist::class,\n PlaylistActivity::class,\n 'activity_id',\n 'id',\n 'id',\n 'playlist_id'\n )->where('is_default', 1);\n }\n\n /**\n * @return Eloquent\\Collection<int, Playlist>\n */\n public function getFavoritePlaylists(): Eloquent\\Collection\n {\n return $this->getAttribute('favoritePlaylists');\n }\n\n /**\n * Get activities from the default/favorite playlist\n *\n * @return Eloquent\\Builder|static\n */\n public function favorites()\n {\n return $this->playlistActivities()->whereHas('playlist', function ($query) {\n $query->where('is_default', 1);\n });\n }\n\n /**\n * @return Model|SubscriptionSet|null|object\n */\n public function subscribedBy(User $user)\n {\n if ($this->prospect === null) {\n return null;\n }\n\n return SubscriptionSet::select('activity_subscription_sets.*')\n ->where('user_id', $user->id)\n ->join('activity_subscriptions', function ($join) {\n $join\n ->on('subscription_set_id', '=', 'activity_subscription_sets.id');\n\n if ($this->account_id) {\n if ($this->opportunity_id) {\n $join\n ->where('followable_type', 'opportunity')\n ->where('followable_id', $this->opportunity_id);\n } else {\n $join\n ->where('followable_type', 'account')\n ->where('followable_id', $this->account_id);\n }\n } elseif ($this->contact_id) {\n $join\n ->where('followable_type', 'contact')\n ->where('followable_id', $this->contact_id);\n } elseif ($this->lead_id) {\n $join\n ->where('followable_type', 'lead')\n ->where('followable_id', $this->lead_id);\n }\n })\n ->first();\n }\n\n /**\n * @return array|Eloquent\\Builder[]|Eloquent\\Collection|SubscriptionSet[]\n */\n public function subscribers()\n {\n if ($this->prospect === null) {\n return [];\n }\n\n return SubscriptionSet::with(['subscriptions', 'user'])\n ->whereHas('subscriptions', function ($query) {\n if ($this->account_id) {\n if ($this->opportunity_id) {\n $query\n ->where('followable_type', 'opportunity')\n ->where('followable_id', $this->opportunity_id);\n } else {\n $query\n ->where('followable_type', 'account')\n ->where('followable_id', $this->account_id);\n }\n } elseif ($this->contact_id) {\n $query\n ->where('followable_type', 'contact')\n ->where('followable_id', $this->contact_id);\n } elseif ($this->lead_id) {\n $query\n ->where('followable_type', 'lead')\n ->where('followable_id', $this->lead_id);\n } else {\n // Nothing to join on?\n // refactor - use Jiminny specific exception\n throw new InvalidArgumentException('Cannot join on a specific customer filter.');\n }\n })\n ->whereHas('user', function ($query) {\n $query\n ->where('team_id', $this->user->team_id)\n ->where('status', User::STATUS_ACTIVE);\n })\n ->get();\n }\n\n /**\n * @return HasMany|Builder|Eloquent\\Collection|Play[]\n */\n public function plays()\n {\n return $this->hasMany(Play::class);\n }\n\n public function getPlays(): Eloquent\\Collection\n {\n return $this->getAttribute('plays');\n }\n\n public function playsBy(User $user)\n {\n /** @var Builder $builder */\n $builder = $this->plays()->where('user_id', $user->id);\n\n return $builder->get();\n }\n\n /**\n * Check if activity was played by a user\n */\n public function wasPlayedBy(User $user): bool\n {\n return $this->plays()->where('user_id', $user->id)->exists();\n }\n\n public function shares()\n {\n return $this->hasMany(Share::class);\n }\n\n /** @return BelongsTo<Participant, self> */\n public function from(): BelongsTo\n {\n return $this->belongsTo(Participant::class, 'from_participant_id');\n }\n\n /** @return BelongsTo<Participant, self> */\n public function to(): BelongsTo\n {\n return $this->belongsTo(Participant::class, 'to_participant_id');\n }\n\n /**\n * Get all of the connections through the participants.\n */\n public function connections()\n {\n return $this->hasManyThrough(\n Connection::class,\n Participant::class,\n 'activity_id',\n 'participant_id'\n );\n }\n\n public function getConnections(): Eloquent\\Collection\n {\n return $this->getAttribute('connections');\n }\n\n /**\n * Get all of the shares through the participants.\n */\n public function participantShares()\n {\n return $this->hasManyThrough(\n Participant\\Share::class,\n Participant::class,\n 'activity_id',\n 'participant_id'\n );\n }\n\n public function getParticipantShares(): Eloquent\\Collection\n {\n return $this->getAttribute('participantShares');\n }\n\n public function topicTriggers(): HasMany\n {\n return $this->hasMany(TopicTrigger::class);\n }\n\n public function activityScorecardRuleTriggers(): HasMany\n {\n return $this->hasMany(Models\\Scorecard\\ActivityScorecardRuleTrigger::class);\n }\n\n public function activityScorecardRules(): HasMany\n {\n return $this->hasMany(Models\\Scorecard\\ActivityScorecardRule::class);\n }\n\n public function questions(): HasMany\n {\n return $this->hasMany(Question::class);\n }\n\n /**\n * Get all the custom data attached to it.\n */\n public function data(): HasMany\n {\n return $this->hasMany(FieldData::class);\n }\n\n public function getData(): Eloquent\\Collection\n {\n /** @var Eloquent\\Collection */\n return $this->getAttribute('data');\n }\n\n #[Scope]\n protected function heldBetween($query, Carbon $start, Carbon $end)\n {\n // Sanity check.\n $from = min($start, $end);\n $until = max($start, $end);\n\n return $query\n ->where('actual_start_date', '>=', $from)\n ->where('actual_end_date', '<=', $until);\n }\n\n #[Scope]\n protected function scheduledBetween($query, Carbon $start, Carbon $end)\n {\n // Sanity check.\n $from = min($start, $end);\n $until = max($start, $end);\n\n return $query\n ->where('scheduled_start_date', '>=', $from)\n ->where('scheduled_end_date', '<=', $until);\n }\n\n /**\n * @param Builder<self> $query\n *\n * @return Builder<self>\n */\n #[Scope]\n protected function forTeam(Builder $query, int $teamId): Builder\n {\n /** @var Builder<self> */\n return $query->whereHas('user', static function (Builder $query) use ($teamId): void {\n $query->where('team_id', $teamId);\n });\n }\n\n /**\n * @param Builder<self> $query\n *\n * @return Builder<self>\n */\n #[Scope]\n protected function inOpenDeals(Builder $query): Builder\n {\n /** @var Builder<self> */\n return $query->whereHas(\n 'opportunity',\n static fn (Builder $query): Builder => $query\n ->where('is_closed', false)\n ->where('deleted_at', '=', null),\n );\n }\n\n /**\n * @param Builder<self> $query\n *\n * @return Builder<self>\n */\n #[Scope]\n protected function notInOpenDeals(Builder $query): Builder\n {\n /** @var Builder<self> */\n return $query->where(\n static fn (Builder $query): Builder => $query->whereNull('opportunity_id')\n ->orWhereHas(\n 'opportunity',\n static fn (Builder $query): Builder => $query->where('is_closed', true),\n )\n ->orWhereHas(\n 'opportunity',\n static fn (Builder $query): Builder => $query->withTrashed()->where('deleted_at', '!=', null),\n ),\n );\n }\n\n /**\n * Finds a participant and updates it with data. If participant doesn't exist creates a new participant from data.\n *\n * @param array $data participant data used to identify the participant and update it\n * @param bool $enterRoom true if participant is entering the room. false if we just want to update some participant data\n * @param Carbon|null $enterTime if $enterNow is true then this is the join time when the actual enter has occurred\n */\n public function updateOrCreateParticipant(\n array $data,\n bool $enterRoom = true,\n ?Carbon $enterTime = null,\n bool $nameMatching = false,\n ): Participant {\n $search = [];\n $participant = null;\n\n if (isset($data['user_id'])) {\n // Check if they already exist based on their ID.\n $search['user_id'] = $data['user_id'];\n } elseif (isset($data['provider_id'])) {\n $search['provider_id'] = $data['provider_id'];\n } elseif ($nameMatching && isset($data['name'])) {\n $search['name'] = $data['name'];\n }\n\n if (! empty($data['email'])) {\n $search['email'] = $data['email'];\n\n // If we have their email, this should be unique enough to lookup (e.g. calendar event based participant).\n unset($search['provider_id']);\n }\n\n // Search by phone number only in case nothing else is available to search by.\n if (array_key_exists('phone_number', $data) && empty($search)) {\n $search['phone_number'] = $data['phone_number'];\n }\n\n if (! empty($search)) {\n // Do a lookup now to see if we have a match on the provided data.\n $lookup = array_map(static function ($key, $value): array {\n return [$key, $value];\n }, array_keys($search), $search);\n\n $participant = $this->participants()->withTrashed()->where($lookup)->first();\n }\n\n // Do a partial match on the name and search in the team members.\n if (! $participant instanceof Participant && $nameMatching && ! empty($data['name'])) {\n $participantMatcher = app(MeetingBot\\Service\\ParticipantMatcher::class);\n\n if (! $participantMatcher instanceof MeetingBot\\Service\\ParticipantMatcher) {\n throw new LogicException('Expecting ParticipantMatcher service instance');\n }\n\n $participant = $participantMatcher->match($this, $data['name']);\n\n // If we've found good participant, avoid data overwrite in `$participant->fill($data)` below.\n if ($participant instanceof Models\\Participant && $participant->hasName()) {\n unset($data['name']); // Thoughts: should we unset also $data['user_id'] and $data['email'] ?\n }\n }\n\n if (! $participant instanceof Participant) {\n // If no match, create a new participant.\n if (empty($search)) {\n $participant = $this->participants()->create();\n } else {\n // If no match, create a new participant but avoid creating duplicates\n $participant = $this->participants()->withTrashed()->firstOrNew($search);\n }\n }\n\n // If we have just recycled a deleted participant\n if ($participant->trashed()) {\n $participant->deleted_at = null;\n }\n\n // Deal with the case when calendar syncs the event while it's in progress.\n // We should prevent change of the participant name, because speeches mapping will fail.\n if ($enterRoom === false\n && $this->isInProgress()\n && $participant->hasName()\n && isset($data['name'])\n && $data['name'] !== $participant->getName()\n ) {\n unset($data['name']);\n }\n\n // Upsert with new data.\n $participant->fill($data);\n\n if ($enterRoom) {\n if ($enterTime === null) {\n $enterTime = now();\n }\n\n // Participant enters room for the first time\n if ($participant->enter_time === null) {\n $participant->enter_time = $enterTime;\n }\n\n // If there is an exit time and it's prior to new enter_time\n if ($participant->exit_time && $participant->exit_time->lt($enterTime)) {\n // Participant has re-joined\n $participant->exit_time = null;\n }\n }\n\n $participant->save();\n\n return $participant;\n }\n\n /**\n * Updates participant CRM data\n *\n * @param array{\n * Lead|null,\n * Account|null,\n * Opportunity|null,\n * Contact|null,\n * Stage|null,\n * string|null\n *} $records\n * @param Participant $participant participant the CRM data is associated with\n */\n public function updateParticipantCrmData(array $records, Participant $participant): void\n {\n // Extract the records.\n [$lead, , , $contact] = $records;\n\n $resolver = $this->getUpdateCrmDataResolver();\n $strategy = $resolver->resolveForParticipant($lead, $contact);\n\n if ($strategy == UpdateCrmDataByStrategy::Lead) {\n if (! $participant->hasName()) {\n $participant->name = $lead->name;\n }\n\n if (! $participant->hasEmailAddress()) {\n $participant->email = $lead->email;\n }\n\n if (! $participant->hasPhoneNumber()) {\n $participant->phone_number = $lead->phone;\n }\n\n $participant->lead_id = $lead->id;\n $participant->save();\n } elseif ($strategy == UpdateCrmDataByStrategy::Contact) {\n if (! $participant->hasName()) {\n $participant->name = $contact->name;\n }\n\n if (! $participant->hasEmailAddress()) {\n $participant->email = $contact->email;\n }\n\n if (! $participant->hasPhoneNumber()) {\n $participant->phone_number = $contact->phone;\n }\n\n $participant->contact_id = $contact->id;\n $participant->save();\n }\n }\n\n /**\n * Updates activity CRM data\n *\n * @param array{\n * Lead|null,\n * Account|null,\n * Opportunity|null,\n * Contact|null,\n * Stage|null,\n * string|null\n *} $records\n */\n public function updateActivityCrmData(array $records): void\n {\n // Extract the records.\n [$lead, $account, $opportunity, $contact, $stage] = $records;\n\n $resolver = $this->getUpdateCrmDataResolver();\n $strategy = $resolver->resolveForActivity($lead, $contact, $account);\n\n if ($strategy == UpdateCrmDataByStrategy::Lead) {\n // Also update the parent activity if required, checking we don't create a mixed lead/account record.\n if ($this->account_id === null && $this->contact_id === null && $this->lead_id === null) {\n $this->lead_id = $lead->id;\n\n if ($this->stage_id === null && $stage) {\n $this->stage_id = $stage->id;\n }\n\n $this->save();\n }\n } elseif ($strategy == UpdateCrmDataByStrategy::Contact) {\n // Also update the parent activity if required, checking we don't create a mixed lead/account record.\n $this->lead_id = null;\n if ($this->stage && $this->stage->getType() === Stage::TYPE_LEAD) {\n $this->stage_id = null;\n }\n\n // Don't trust previous matched account_id as it might have been changed in the CRM\n if ($account && $account->id !== $this->account_id) {\n $this->account_id = $account->id;\n }\n\n if ($opportunity && $this->opportunity_id !== $opportunity->id) {\n $this->opportunity_id = $opportunity->id;\n // Reset stage_id when opportunity changes to avoid stale stage from previous opportunity match\n $this->stage_id = $stage?->id;\n }\n\n if ($this->stage_id === null && $stage) {\n $this->stage_id = $stage->id;\n }\n\n if ($opportunity && $this->value !== $opportunity->value) {\n $this->value = $opportunity->value;\n }\n\n // Always set contact_id when available, regardless of account_id status\n if ($this->contact_id === null && $contact) {\n $this->contact_id = $contact->id;\n }\n\n $this->save();\n } elseif ($strategy == UpdateCrmDataByStrategy::Account && $this->account_id === null) {\n // Also update the parent activity if required, checking we don't create a mixed lead/account record.\n $this->lead_id = null;\n if ($this->stage && $this->stage->getType() === Stage::TYPE_LEAD) {\n $this->stage_id = null;\n }\n\n // Update the account and opportunity on the activity record if possible.\n $this->account_id = $account->id;\n\n if ($this->stage_id === null && $stage) {\n $this->stage_id = $stage->id;\n }\n\n if ($this->opportunity_id === null && $opportunity) {\n $this->opportunity_id = $opportunity->id;\n $this->value = $opportunity->value;\n }\n\n $this->save();\n }\n }\n\n public function getActivityProspectData(): array\n {\n return [\n 'lead' => $this->lead_id,\n 'contact' => $this->contact_id,\n 'account' => $this->account_id,\n 'opportunity' => $this->opportunity_id,\n 'stage' => $this->stage_id,\n ];\n }\n\n public function isOrganizer(User $user): bool\n {\n return $this->user_id && $this->user_id === $user->id;\n }\n\n public function isJoinable(): bool\n {\n return \\in_array($this->status, [\n self::STATUS_SCHEDULED,\n self::STATUS_PENDING,\n self::STATUS_RINGING,\n self::STATUS_IN_PROGRESS,\n ], true);\n }\n\n public function isAttemptedForBotJoin(): bool\n {\n return in_array($this->getAttribute('status'), self::MEETING_BOT_JOIN_ATTEMPTED, true);\n }\n\n /**\n * Check if the activity can be saved to CRM (manual or autolog)\n */\n public function isLoggable(): bool\n {\n if ($this->getUser()->getTeam()->hasFeature(FeatureEnum::SIDEKICK_SETTINGS)) {\n $sidekickService = app(SidekickService::class);\n\n if (! $sidekickService->isSidekickEnabledForUser($this->getUser())) {\n return false;\n }\n }\n\n // If we don't know the activity type, don't try to log.\n if ($this->playbook_category_id === null) {\n return false;\n }\n\n if ($this->user->crm_required === false) {\n return false;\n }\n\n // Don't prompt for internal meetings.\n if ($this->is_internal) {\n return false;\n }\n\n // If we don't know who we are trying to log to, don't try to log.\n if ($this->prospect === null) {\n return false;\n }\n\n $validStatus = false;\n switch ($this->type) {\n case self::TYPE_SOFTPHONE:\n case self::TYPE_SOFTPHONE_INBOUND:\n $validStatus = true;\n\n break;\n case self::TYPE_CONFERENCE:\n $validStatus = in_array($this->status, [\n self::STATUS_BUSY,\n self::STATUS_NO_ANSWER,\n self::STATUS_COMPLETED,\n self::STATUS_CANCELLED,\n ], true);\n\n break;\n case self::TYPE_SMS_INBOUND:\n case self::TYPE_SMS_OUTBOUND:\n $validStatus = in_array($this->status, [\n self::STATUS_QUEUED,\n self::STATUS_SENT,\n self::STATUS_UNDELIVERED,\n self::STATUS_DELIVERED,\n self::STATUS_RECEIVED,\n ], true);\n\n break;\n }\n\n // Depending on the activity channel, we should not try to log.\n return $validStatus;\n }\n\n public function isScheduled(): bool\n {\n return $this->status === self::STATUS_SCHEDULED;\n }\n\n public function scheduledDuration(): int\n {\n if ($this->scheduled_start_time && $this->scheduled_end_time) {\n return $this->scheduled_end_time->timestamp - $this->scheduled_start_time->timestamp;\n }\n\n return 0;\n }\n\n public function isPending(): bool\n {\n return $this->status === self::STATUS_PENDING;\n }\n\n public function isCompleted(): bool\n {\n return $this->status === self::STATUS_COMPLETED;\n }\n\n public function isRinging(): bool\n {\n return $this->status === self::STATUS_RINGING;\n }\n\n public function isInProgress(): bool\n {\n return $this->status === self::STATUS_IN_PROGRESS;\n }\n\n public function isBusy(): bool\n {\n return $this->status === self::STATUS_BUSY;\n }\n\n public function isNoAnswer(): bool\n {\n return $this->status === self::STATUS_NO_ANSWER;\n }\n\n public function isFailed(): bool\n {\n return $this->status === self::STATUS_FAILED;\n }\n\n public function isCancelled(): bool\n {\n return $this->status === self::STATUS_CANCELLED;\n }\n\n public function hasEnded(int $gracePeriodMinutes = 15): bool\n {\n if ($this->isCompleted()) {\n return true;\n }\n\n if (($this->isFailed() || $this->isCancelled()) && $this->hasScheduledEndTime()) {\n return $this->getScheduledEndTime()->addMinutes($gracePeriodMinutes)->isPast();\n }\n\n return false;\n }\n\n public function hasStarted(): bool\n {\n return $this->hasActualStartTime();\n }\n\n public function isOngoing(): bool\n {\n return $this->hasActualStartTime() && ! $this->hasActualEndTime();\n }\n\n public function isTypeSmsInbound(): bool\n {\n return $this->getType() === self::TYPE_SMS_INBOUND;\n }\n\n public function isTypeSmsOutbound(): bool\n {\n return $this->getType() === self::TYPE_SMS_OUTBOUND;\n }\n\n public function isTypeSoftPhone(): bool\n {\n return $this->getType() === self::TYPE_SOFTPHONE;\n }\n\n public function isTypeSoftphoneInbound(): bool\n {\n return $this->getType() === self::TYPE_SOFTPHONE_INBOUND;\n }\n\n public function isTypeConference(): bool\n {\n return $this->getType() === self::TYPE_CONFERENCE;\n }\n\n /**\n * Get a conference elapsed time in seconds.\n *\n * @return int seconds count\n */\n public function secondsTimeElapsed(): int\n {\n if (empty($this->actual_start_time)) {\n return 0;\n }\n\n // Get number of seconds since conference actual start time\n return (int) abs(Carbon::now()->diffInRealSeconds($this->actual_start_time));\n }\n\n /**\n * Get a conference elapsed time formatted as \"1:30:20\" if more than 1 hour or \"30:20\" otherwise.\n */\n public function formattedTimeElapsed(): string\n {\n // Get number of seconds since conference actual start time.\n $elapsedSeconds = $this->secondsTimeElapsed();\n $elapsedTime = Carbon::createFromTimestampUTC($elapsedSeconds);\n\n // Format conference start time.\n return $elapsedTime->format($elapsedSeconds < 3600 ? 'i:s' : 'G:i:s');\n }\n\n public function wasScheduled(): bool\n {\n return $this->calendarEvent !== null || in_array($this->getSource(), [self::SOURCE_OUTLOOK, self::SOURCE_GOOGLE]);\n }\n\n public function isInstant(): bool\n {\n return ! $this->wasScheduled();\n }\n\n /**\n * GETTERS AND SETTERS FOLLOW BELOW\n */\n\n public function getUuid(): string\n {\n return $this->getAttribute('id_string');\n }\n\n public function getId(): int\n {\n return $this->getAttribute('id');\n }\n\n public function getFromParticipantId(): ?int\n {\n return $this->getAttribute('from_participant_id');\n }\n\n public function getFromParticipant(): ?Participant\n {\n return $this->getAttribute('from');\n }\n\n public function getToParticipantId(): ?int\n {\n return $this->getAttribute('to_participant_id');\n }\n\n public function getToParticipant(): ?Participant\n {\n return $this->getAttribute('to');\n }\n\n public function hasScheduledStartTime(): bool\n {\n return $this->getAttribute('scheduled_start_time') !== null;\n }\n\n public function getScheduledStartTime(): ?Carbon\n {\n return $this->getAttribute('scheduled_start_time');\n }\n\n public function setScheduledStartTime(DateTimeInterface $dateTime): self\n {\n $this->setAttribute('scheduled_start_time', $dateTime);\n\n return $this;\n }\n\n public function getScheduledEndTime(): ?DateTimeInterface\n {\n return $this->getAttribute('scheduled_end_time');\n }\n\n public function hasScheduledEndTime(): bool\n {\n return $this->getAttribute('scheduled_end_time') !== null;\n }\n\n public function setScheduledEndTime(DateTimeInterface $dateTime): self\n {\n $this->setAttribute('scheduled_end_time', $dateTime);\n\n return $this;\n }\n\n public function getActualStartTime(): ?Carbon\n {\n return $this->getAttribute('actual_start_time');\n }\n\n public function hasActualStartTime(): bool\n {\n return $this->getAttribute('actual_start_time') !== null;\n }\n\n public function getActualEndTime(): ?Carbon\n {\n return $this->getAttribute('actual_end_time');\n }\n\n public function hasActualEndTime(): bool\n {\n return $this->getAttribute('actual_end_time') !== null;\n }\n\n public function getType(): ?string\n {\n return $this->getAttribute('type');\n }\n\n public function getStatus(): string\n {\n return $this->getAttribute('status');\n }\n\n public function setStatus(string $status): self\n {\n $this->setAttribute('status', $status);\n\n return $this;\n }\n\n public function setActualStartTime(DateTimeInterface $dateTime): self\n {\n $this->setAttribute('actual_start_time', $dateTime);\n\n return $this;\n }\n\n public function setActualEndTime(DateTimeInterface $dateTime, bool $shouldUpdateDuration = true): self\n {\n $this->setAttribute('actual_end_time', $dateTime);\n\n if (! $shouldUpdateDuration) {\n return $this;\n }\n\n return $this->updateDuration();\n }\n\n public function updateDuration(): self\n {\n if (! $this->hasActualStartTime() || ! $this->hasActualEndTime()) {\n return $this;\n }\n\n return $this->setDuration(\n (int) abs($this->getActualStartTime()->diffInRealSeconds($this->getActualEndTime()))\n );\n }\n\n public function setDuration(int $duration): self\n {\n $this->setAttribute('duration', $duration);\n\n return $this;\n }\n\n public function getRecordingState(): string\n {\n return $this->getAttribute('recording_state');\n }\n\n public function isRecordingState(string $recordingState): bool\n {\n return $this->getRecordingState() === $recordingState;\n }\n\n public function setRecordingState(string $recordingState): self\n {\n $this->setAttribute('recording_state', $recordingState);\n\n return $this;\n }\n\n public function hasActivityType(): bool\n {\n return $this->getAttribute('category') !== null;\n }\n\n public function getActivityType(): ?PlaybookCategory\n {\n return $this->getAttribute('category');\n }\n\n public function setActivityType(int $playbookCategoryId): self\n {\n $this->setAttribute('playbook_category_id', $playbookCategoryId);\n\n return $this;\n }\n\n public function hasStage(): bool\n {\n return $this->getAttribute('stage') !== null;\n }\n\n public function getStage(): ?Stage\n {\n return $this->getAttribute('stage');\n }\n\n public function getStageId(): ?int\n {\n return $this->getAttribute('stage_id');\n }\n\n public function setStageId(?int $stageId): void\n {\n $this->setAttribute('stage_id', $stageId);\n }\n\n public function hasOpportunity(): bool\n {\n return $this->getAttribute('opportunity') !== null;\n }\n\n public function getOpportunity(): ?Opportunity\n {\n return $this->getAttribute('opportunity');\n }\n\n public function getOpportunityId(): ?int\n {\n return $this->getAttribute('opportunity_id');\n }\n\n public function setOpportunityId(?int $opportunityId): void\n {\n $this->setAttribute('opportunity_id', $opportunityId);\n }\n\n public function hasContact(): bool\n {\n return $this->getAttribute('contact') !== null;\n }\n\n public function getContact(): ?Contact\n {\n return $this->getAttribute('contact');\n }\n\n public function getContactId(): ?int\n {\n return $this->getAttribute('contact_id');\n }\n\n public function setContactId(?int $contactId): void\n {\n $this->setAttribute('contact_id', $contactId);\n }\n\n public function hasLead(): bool\n {\n return $this->getAttribute('lead') !== null;\n }\n\n public function getLead(): ?Lead\n {\n return $this->getAttribute('lead');\n }\n\n public function getLeadId(): ?int\n {\n return $this->getAttribute('lead_id');\n }\n\n public function setLeadId(?int $leadId): void\n {\n $this->setAttribute('lead_id', $leadId);\n }\n\n public function hasAccount(): bool\n {\n return $this->getAttribute('account') !== null;\n }\n\n public function getAccount(): ?Account\n {\n return $this->getAttribute('account');\n }\n\n public function getAccountId(): ?int\n {\n return $this->getAttribute('account_id');\n }\n\n public function setAccountId(?int $accountId): void\n {\n $this->setAttribute('account_id', $accountId);\n }\n\n /**\n * This method exists to avoid confusion using ->participants() or ->participants. Use the getter instead.\n *\n * @return Collection<int, Participant>|Participant[]\n */\n public function getParticipants(): Collection\n {\n return $this->participants;\n }\n\n /**\n * @deprecated use ParticipantRepository::findParticipantRoomOwner() instead\n */\n public function findParticipantRoomOwner(): ?Participant\n {\n $roomOwnerId = $this->getUserId();\n\n return $this->getParticipants()\n ->filter(static fn (Participant $participant): bool => $participant->isSameUserId($roomOwnerId))\n ->first();\n }\n\n public function hasCrmProviderId(): bool\n {\n return $this->getAttribute('crm_provider_id') !== null;\n }\n\n public function getCrmProviderId(): ?string\n {\n return $this->getAttribute('crm_provider_id');\n }\n\n public function setCrmProviderId(?string $crmProviderId): void\n {\n $this->setAttribute('crm_provider_id', $crmProviderId);\n }\n\n public function getUserId(): ?int\n {\n return $this->getAttribute('user_id');\n }\n\n public function hasUser(): bool\n {\n return $this->user()->exists();\n }\n\n public function getUser(): User\n {\n return $this->getAttribute('user');\n }\n\n public function getCreatedAt(): Carbon\n {\n return $this->getAttribute('created_at');\n }\n\n public function isInFiniteState(): bool\n {\n return $this->isFiniteState($this->getStatus());\n }\n\n public function isFiniteState(string $status): bool\n {\n $finiteStates = self::FINITE_STATES[$this->getType()] ?? [];\n\n return in_array($status, $finiteStates, true);\n }\n\n public function getParticipant(Authenticatable $user): Participant\n {\n return $this->findParticipant($user);\n }\n\n public function findParticipant(Authenticatable $user): ?Participant\n {\n if ($user instanceof User) {\n /** @var User $user */\n return $this->participants()->where('user_id', '=', $user->getId())->first();\n }\n\n throw new LogicException(sprintf('Unsupported Authenticatable implementation %s', get_class($user)));\n }\n\n public function hasLanguageCode(): bool\n {\n return $this->getAttribute('language') !== null;\n }\n\n public function getLanguageCode(): ?string\n {\n /** @var string|null */\n return $this->getAttribute('language');\n }\n\n public function getLanguageCodeHyphenated(): string\n {\n return str_replace('_', '-', $this->getLanguageCode() ?? '');\n }\n\n public function getLanguageCodeLocale(): string\n {\n [ $language ] = explode('_', $this->getLanguageCode() ?? '');\n\n return $language;\n }\n\n public function setLanguageCode(string $value): self\n {\n return $this->setAttribute('language', $value);\n }\n\n public function hasSource(): bool\n {\n return $this->getAttribute('source') !== null;\n }\n\n public function setSource(?string $source): self\n {\n return $this->setAttribute('source', $source);\n }\n\n public function isSource(string $source): bool\n {\n return $this->getAttribute('source') === $source;\n }\n\n public function getSource(): ?string\n {\n return $this->getAttribute('source');\n }\n\n public function isSourceGong(): bool\n {\n return $this->isSource(self::SOURCE_GONG);\n }\n\n public function getExternalId(): ?string\n {\n return $this->getAttribute('external_id');\n }\n\n public function setExternalId(?string $externalId): self\n {\n return $this->setAttribute('external_id', $externalId);\n }\n\n public function hasExternalId(): bool\n {\n return $this->getAttribute('external_id') !== null;\n }\n\n public function getProvider(): string\n {\n return $this->getAttribute('provider');\n }\n\n public function hasTelephonyProviderId(): bool\n {\n return $this->getAttribute('telephony_provider_id') !== null;\n }\n\n public function getTelephonyProviderId(): ?string\n {\n return $this->getAttribute('telephony_provider_id');\n }\n\n public function setTelephonyProviderId(?string $telephonyProviderId): self\n {\n return $this->setAttribute('telephony_provider_id', $telephonyProviderId);\n }\n\n public function getLocation(): ?string\n {\n return $this->getAttribute('location');\n }\n\n public function setLocation(?string $location): self\n {\n return $this->setAttribute('location', $location);\n }\n\n public function isDeleted(): bool\n {\n return $this->getAttribute('deleted_at') !== null;\n }\n\n /**\n * Check if activity recording is on and activity status is not one of the failed statuses.\n */\n public function canReviewActivity(): bool\n {\n $failedStatuses = self::$enumFailedStatuses;\n\n return (! in_array($this->recording_state, [self::RECORDING_OFF, self::RECORDING_STOPPED], true) &&\n ! in_array($this->status, $failedStatuses, true));\n }\n\n public function hasReasonCodeBotKicked(): bool\n {\n return $this->getFlag('recording_reason_code', self::FLAG_RECORDING_REASON_MEETING_BOT_KICKED);\n }\n\n public function hasReasonCodeNotCompliant(): bool\n {\n return $this->getFlag('recording_reason_code', self::FLAG_RECORDING_REASON_CONSENT_DENIED);\n }\n\n public function hasTopicTriggers(): bool\n {\n return $this->topicTriggers()->count() !== 0;\n }\n\n public function getTopicTriggers(): Collection\n {\n return $this->topicTriggers;\n }\n\n public function getTopicTriggersSorted(): Collection\n {\n $this->loadMissing([\n 'topicTriggers.participant',\n 'topicTriggers.playbackThemeTopicTrigger',\n 'topicTriggers.playbackThemeTopicTrigger',\n 'topicTriggers.playbackThemeTopicTrigger.playbackThemeTopic',\n 'topicTriggers.playbackThemeTopicTrigger.playbackThemeTopic.playbackTheme',\n ]);\n\n return $this->topicTriggers\n ->sortBy([\n 'playbackThemeTopicTrigger.playbackThemeTopic.playbackTheme.sort',\n 'playbackThemeTopicTrigger.playbackThemeTopic.sort',\n 'playbackThemeTopicTrigger.sort',\n ]);\n }\n\n public function hasQuestions(): bool\n {\n return $this->questions()->exists();\n }\n\n public function getQuestions(): Collection\n {\n return $this->questions;\n }\n\n public function hasValue(): bool\n {\n return $this->getAttribute('value') !== null;\n }\n\n public function getValue(): ?float\n {\n return $this->getAttribute('value');\n }\n\n public function setValue(?float $value): void\n {\n $this->setAttribute('value', $value);\n }\n\n public function transitionTo(string $newState, callable $callback, ?int $timeout = null): self\n {\n $newState = $this->getWorkflowStateFor(\n $this->getType(),\n $newState\n );\n\n return $this->traitTransitionTo($newState, $callback, $timeout);\n }\n\n public function getWorkflowState(): string\n {\n return $this->getWorkflowStateFor(\n $this->getType(),\n $this->getStatus()\n );\n }\n\n public function getActivityProviderDisplayName(): string\n {\n return \\Cache::remember('activity_provider_display_name-' . $this->getProvider(), 60 * 60 * 24, function () {\n $activityProviderRegistry = app()->make(ActivityProviderRegistry::class);\n\n try {\n return $activityProviderRegistry->get($this->getProvider())->getDisplayName();\n } catch (Exception $exception) {\n return ucfirst($this->getProvider());\n }\n });\n }\n\n private function getWorkflowStateFor(string $activityChannel, string $activityStatus): string\n {\n return sprintf(\n '%s::%s',\n $activityChannel,\n $activityStatus\n );\n }\n\n public function getWorkflow(): array\n {\n $map = [\n self::TYPE_SOFTPHONE => [\n self::STATUS_SCHEDULED => [\n self::STATUS_PENDING,\n self::STATUS_IN_PROGRESS,\n self::STATUS_FAILED,\n self::STATUS_BUSY,\n ],\n self::STATUS_PENDING => [\n self::STATUS_IN_PROGRESS,\n self::STATUS_FAILED,\n self::STATUS_BUSY,\n ],\n self::STATUS_RINGING => [\n self::STATUS_CANCELLED,\n self::STATUS_FAILED,\n self::STATUS_IN_PROGRESS,\n self::STATUS_BUSY,\n ],\n self::STATUS_IN_PROGRESS => [\n self::STATUS_COMPLETED,\n ],\n ],\n self::TYPE_SOFTPHONE_INBOUND => [\n self::STATUS_RINGING => [\n self::STATUS_IN_PROGRESS,\n self::STATUS_NO_ANSWER,\n self::STATUS_CANCELLED,\n self::STATUS_FAILED,\n self::STATUS_BUSY,\n ],\n self::STATUS_IN_PROGRESS => [\n self::STATUS_COMPLETED,\n ],\n ],\n ];\n\n return collect($map)\n ->mapWithKeys(function (array $currentStates, string $activityChannel): array {\n return [\n $activityChannel => collect($currentStates)\n ->mapWithKeys(function (array $possibleStates, $currentState) use ($activityChannel): array {\n $transitionName = $this->getWorkflowStateFor($activityChannel, $currentState);\n\n return [\n $transitionName => array_map(function (string $newState) use ($activityChannel) {\n return $this->getWorkflowStateFor($activityChannel, $newState);\n }, $possibleStates),\n ];\n }),\n ];\n })\n ->reduce(static function (array $carry, Collection $item): array {\n return array_merge($carry, $item->all());\n }, []);\n }\n\n public function hasPosterPath(): bool\n {\n return $this->getAttribute('poster_path') !== null;\n }\n\n public function getPosterPath(): ?string\n {\n return $this->getAttribute('poster_path');\n }\n\n /**\n * Take into account all recording settings and determine if we need to record this activity or not.\n */\n public function shouldRecord(): bool\n {\n return $this->determineRecordingReasonCode() === null;\n }\n\n public function determineRecordingReasonCode(): ?int\n {\n // Conference specific decisions.\n if ($this->isTypeConference()) {\n // If they have manually overridden the recording setting to not record.\n if ($this->hasRecordingPreference() && $this->getRecordingPreference() === false) {\n return self::FLAG_RECORDING_REASON_PREFERENCE_OVERRIDE;\n }\n\n // If they have manually overridden the recording setting to record.\n if ($this->hasRecordingPreference() && $this->getRecordingPreference() === true) {\n return null;\n }\n\n // If their team has disabled recording meetings, don't record.\n if ($this->user->team->isConferenceRecordPreferenceDisabled()) {\n return self::FLAG_RECORDING_REASON_TEAM_AUTOMATIC_DISABLED;\n }\n\n // If the host has disabled recording meetings, don't record.\n if ($this->user->checkConferenceRecordPreference() === false) {\n return self::FLAG_RECORDING_REASON_USER_AUTOMATIC_DISABLED;\n }\n\n // If it was marked internal...\n if ($this->is_internal) {\n // and their team has disabled recording internal meetings, don't record.\n if (\n $this->user->team->isConferenceRecordPreferenceEnabled()\n && ! $this->user->team->isConferenceRecordInternalPreferenceEnabled()\n ) {\n return self::FLAG_RECORDING_REASON_TEAM_INTERNAL_DISABLED;\n }\n\n // and the host has disabled recording internal meetings, don't record.\n if ($this->user->checkConferenceRecordInternalPreference() === false) {\n return self::FLAG_RECORDING_REASON_USER_INTERNAL_DISABLED;\n }\n }\n\n // If it was not scheduled and they disabled internal meetings, we cannot determine if it was internal.\n if ($this->wasScheduled() === false && $this->user->checkConferenceRecordInternalPreference() === false) {\n return self::FLAG_RECORDING_REASON_USER_INTERNAL_DISABLED_UNSCHEDULED;\n }\n }\n\n return null;\n }\n\n public function getRecordingReasonCode(): int\n {\n return $this->getAttribute('recording_reason_code');\n }\n\n public function setRecordingReasonCode(int $recordingReasonCode): self\n {\n $this->setAttribute('recording_reason_code', $recordingReasonCode);\n\n return $this;\n }\n\n // Not used today.\n public function getRecordingReasonString(): ?string\n {\n if ($this->hasRecordingReasonCompliancePrompted()) {\n return Team::COMPLIANCE_MODE_RECORDING_PROMPT;\n }\n\n if ($this->hasRecordingReasonComplianceRestricted()) {\n return Team::COMPLIANCE_MODE_RECORDING_RESTRICT;\n }\n\n if ($this->hasRecordingReasonComplianceRestrictedToOneSideRecording()) {\n return Team::COMPLIANCE_MODE_RECORDING_RESTRICT_ONE_SIDE;\n }\n\n return null;\n }\n\n public function hasRecordingReasonComplianceRestricted(): bool\n {\n return $this->getFlag('recording_reason_code', self::FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT);\n }\n\n public function hasRecordingReasonCompliancePrompted(): bool\n {\n return $this->getFlag('recording_reason_code', self::FLAG_RECORDING_REASON_COMPLIANCE_PROMPT);\n }\n\n public function hasRecordingReasonComplianceRestrictedToOneSideRecording(): bool\n {\n return $this->getFlag('recording_reason_code', self::FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT_ONE_SIDE);\n }\n\n public function getAudioTrack(): ?Track\n {\n /** @var Track|null */\n return $this->tracks()\n ->where('type', '=', Track::TYPE_AUDIO)\n ->first();\n }\n\n public function activeParticipants(): HasMany\n {\n return $this->hasMany(Participant::class)->active();\n }\n\n public function getActiveParticipants(): Eloquent\\Collection\n {\n return $this->getAttribute('activeParticipants');\n }\n\n public function crm(): Eloquent\\Relations\\BelongsTo\n {\n return $this->belongsTo(Configuration::class, 'crm_configuration_id');\n }\n\n public function activitySummaryLogs(): HasMany\n {\n return $this->hasMany(ActivitySummaryLog::class);\n }\n\n public function getCrm(): ?Configuration\n {\n return $this->getAttribute('crm');\n }\n\n public function hasCrmConfiguration(): bool\n {\n return $this->getAttribute('crm') !== null;\n }\n\n public function isProcessed(): ?bool\n {\n return $this->getAttribute('is_processed');\n }\n\n public function hasRecordingPrompt(): bool\n {\n return $this->getAttribute('has_recording_prompt') === true;\n }\n\n public function isOnAir(): bool\n {\n return $this->getAttribute('on_air') === self::ON_AIR_READY || $this->getAttribute('on_air') === self::ON_AIR_STREAMING;\n }\n\n public function setOnAir(int $onAir): self\n {\n $this->setAttribute('on_air', $onAir);\n\n return $this;\n }\n\n public function getOnAir(): ?int\n {\n return $this->getAttribute('on_air');\n }\n\n public function setTitleFromCallData(Call $call): void\n {\n $direction = $call->isOutbound() ? 'to' : 'from';\n\n $party = $this->prospect_name\n ?? $call->getContactName()\n ?? $call->getOtherPartyPhoneNumber()\n ;\n\n $this->update(['title' => sprintf('Call %s %s', $direction, $party)]);\n }\n\n /**\n * @param array{}|array{channels:string|null, format:string|null, type:string|null, status:string|null} $audioParams\n */\n public function createAudioTrack(\n string $telephonyProviderId,\n string $recordingUrl,\n array $audioParams = []\n ): Track {\n return $this->tracks()->updateOrCreate([\n 'telephony_provider_id' => $telephonyProviderId,\n ], [\n 'type' => $audioParams['type'] ?? Track::TYPE_AUDIO,\n 'status' => $audioParams['status'] ?? Track::STATUS_PENDING,\n 'format' => $audioParams['format'] ?? Track::FORMAT_WAV,\n 'provider_content_url' => $recordingUrl,\n 'start_time' => $this->actual_start_time,\n 'end_time' => $this->actual_end_time,\n ]);\n }\n\n public function createTrack(string $telephonyProviderId, array $params): Track\n {\n return $this->tracks()->updateOrCreate(\n [\n 'telephony_provider_id' => $telephonyProviderId,\n ],\n $params\n );\n }\n\n public function createOrganiserParticipant(Call $call): Participant\n {\n $user = $this->getUser();\n\n return $this->updateOrCreateParticipant([\n 'is_ghost' => 0,\n 'name' => $user->name,\n 'email' => $user->email,\n 'phone_number' => phone_e164(null, $call->getUserPhoneNumber()),\n 'enter_time' => $this->actual_start_time,\n 'exit_time' => $this->actual_end_time,\n 'user_id' => $user->id,\n ], false);\n }\n\n public function createProspectParticipant(Call $call): Participant\n {\n // not null 'name' is mandatory here to create a separate participant with 'nameMatching'\n // in case of the same phone_number with the Organiser\n $useNameMatching = $call->getUserPhoneNumber() === $call->getOtherPartyPhoneNumber();\n $defaultName = $useNameMatching ? '' : null;\n\n return $this->updateOrCreateParticipant(data: [\n 'is_ghost' => 0,\n 'name' => $this->prospect->name ?? $defaultName,\n 'email' => $this->prospect->email ?? null,\n 'phone_number' => phone_e164(null, $call->getOtherPartyPhoneNumber()),\n 'enter_time' => $this->actual_start_time,\n 'exit_time' => $this->actual_end_time,\n 'contact_id' => $this->contact_id ?? null,\n 'lead_id' => $this->lead_id ?? null,\n ], enterRoom: false, nameMatching: $useNameMatching);\n }\n\n public function updateParticipants(Participant $organiserParticipant, Participant $prospectParticipant): void\n {\n $this->update([\n 'from_participant_id' => $this->isTypeSoftPhone() ? $organiserParticipant->id : $prospectParticipant->id,\n 'to_participant_id' => $this->isTypeSoftPhone() ? $prospectParticipant->id : $organiserParticipant->id,\n ]);\n }\n\n public function hasProspect(): bool\n {\n return $this->getProspectAttribute() !== null;\n }\n\n public function isPrivate(): bool\n {\n return $this->getAttribute('is_private');\n }\n\n /** Create a new factory instance for the model. */\n protected static function newFactory(): Factory\n {\n return ActivityFactory::new();\n }\n\n public function getUpdatedAt(): Carbon\n {\n return $this->getAttribute('updated_at');\n }\n\n public function getActivitySummaryLogs(): Eloquent\\Collection\n {\n return $this->getAttribute('activitySummaryLogs');\n }\n\n public function hasProspectActivitySummaryLog(): bool\n {\n return $this->getActivitySummaryLogs()->contains(\n 'relation_type',\n ActivitySummaryLog::RELATION_OBJECT_TYPE_PROSPECT\n );\n }\n\n public function getTeam(): Team\n {\n return $this->getUser()->getTeam();\n }\n\n private function getUpdateCrmDataResolver(): UpdateCrmDataResolverInterface\n {\n $factory = app(UpdateCrmDataResolverFactory::class);\n\n return $factory->create($this);\n }\n\n public function getMeetingTrackProviderId(string $type): string\n {\n $label = match ($type) {\n Track::TYPE_VIDEO => 'v',\n Track::TYPE_AUDIO => 'a',\n default => throw new InvalidArgumentJiminnyException('Invalid track type'),\n };\n\n $startTimestamp = $this->getScheduledStartTime()?->getTimestamp();\n $teamId = $this->getTeam()->getId();\n\n return $this->getTelephonyProviderId() . ':' . $label . ':' . $startTimestamp . ':' . $teamId;\n }\n\n /**\n * Get all consent records associated with this activity\n *\n * @return \\Illuminate\\Database\\Eloquent\\Relations\\HasMany\n */\n public function participantConsents(): HasMany\n {\n return $this->hasMany(Participant\\Consent::class);\n }\n\n public function isDiallerCall(): bool\n {\n if ($this->getProvider() === Activity::PROVIDER_UPLOADER) {\n return false;\n }\n\n if (! in_array($this->getType(), [self::TYPE_SOFTPHONE, self::TYPE_SOFTPHONE_INBOUND])) {\n return false;\n }\n\n return $this->getProvider() !== self::PROVIDER_TWILIO;\n }\n\n public function getActivityDateWithFallback(): Carbon\n {\n if ($this->getActualStartTime() !== null) {\n return $this->getActualStartTime();\n }\n\n if ($this->getScheduledStartTime() !== null) {\n return $this->getScheduledStartTime();\n }\n\n return $this->getCreatedAt();\n }\n\n public function getCrmType(): ?string\n {\n // Treat uploader activities as conferences\n if ($this->getProvider() === Activity::PROVIDER_UPLOADER) {\n return Activity::TYPE_CONFERENCE;\n }\n\n return $this->getType();\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\nnamespace Jiminny\\Models;\n\nuse Carbon\\Carbon;\nuse Database\\Factories\\ActivityFactory;\nuse DateTimeInterface;\nuse Exception;\nuse Illuminate\\Contracts\\Auth\\Authenticatable;\nuse Illuminate\\Database\\Eloquent;\nuse Illuminate\\Database\\Eloquent\\Attributes\\Scope;\nuse Illuminate\\Database\\Eloquent\\Builder;\nuse Illuminate\\Database\\Eloquent\\Factories\\Factory;\nuse Illuminate\\Database\\Eloquent\\Factories\\HasFactory;\nuse Illuminate\\Database\\Eloquent\\Relations\\BelongsTo;\nuse Illuminate\\Database\\Eloquent\\Relations\\BelongsToMany;\nuse Illuminate\\Database\\Eloquent\\Relations\\HasMany;\nuse Illuminate\\Database\\Eloquent\\Relations\\HasManyThrough;\nuse Illuminate\\Database\\Eloquent\\Relations\\HasOne;\nuse Illuminate\\Database\\Eloquent\\SoftDeletes;\nuse Illuminate\\Support\\Collection;\nuse Illuminate\\Support\\Facades\\Auth;\nuse InvalidArgumentException;\nuse Jiminny\\Component\\ElasticSearch;\nuse Jiminny\\Component\\MeetingBot;\nuse Jiminny\\Component\\Model\\BitwiseFlagTrait;\nuse Jiminny\\Component\\PlaybackPage\\Comments\\Services\\ActivityCommentService;\nuse Jiminny\\Component\\Sidekick\\SidekickService;\nuse Jiminny\\Component\\Uuid\\UuidAwareInterface;\nuse Jiminny\\Component\\Workflow;\nuse Jiminny\\Contracts;\nuse Jiminny\\Contracts\\Crm\\ProspectInterface;\nuse Jiminny\\DTO\\ImportCall\\Call;\nuse Jiminny\\Events\\Activities\\ActivityTypeUpdated;\nuse Jiminny\\Events\\Activities\\ActivityUpdated;\nuse Jiminny\\Events\\Activities\\ProspectUpdated;\nuse Jiminny\\Events\\Activities\\StageUpdated;\nuse Jiminny\\Events\\Activities\\StatusUpdated;\nuse Jiminny\\Events\\Activities\\TitleUpdated;\nuse Jiminny\\Exceptions\\InvalidArgumentException as InvalidArgumentJiminnyException;\nuse Jiminny\\Exceptions\\LogicException;\nuse Jiminny\\Exceptions\\RuntimeException;\nuse Jiminny\\Models;\nuse Jiminny\\Models\\Activity\\ActivitySummaryLog;\nuse Jiminny\\Models\\Activity\\ActivityUploadSetting;\nuse Jiminny\\Models\\Activity\\AvailabilityNotification;\nuse Jiminny\\Models\\Activity\\CoachRequest;\nuse Jiminny\\Models\\Activity\\Comment;\nuse Jiminny\\Models\\Activity\\Log;\nuse Jiminny\\Models\\Activity\\Message;\nuse Jiminny\\Models\\Activity\\Moment;\nuse Jiminny\\Models\\Activity\\Note;\nuse Jiminny\\Models\\Activity\\ParticipantSpeech;\nuse Jiminny\\Models\\Activity\\Play;\nuse Jiminny\\Models\\Activity\\Question;\nuse Jiminny\\Models\\Activity\\Share;\nuse Jiminny\\Models\\Activity\\Snapshot;\nuse Jiminny\\Models\\Activity\\Stats;\nuse Jiminny\\Models\\Activity\\SubscriptionSet;\nuse Jiminny\\Models\\Activity\\TopicTrigger;\nuse Jiminny\\Models\\Activity\\Transcription;\nuse Jiminny\\Models\\Calendar\\CalendarEvent;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Models\\Crm\\FieldData;\nuse Jiminny\\Models\\ElasticSearch\\ActivityElasticSearchTrait;\nuse Jiminny\\Models\\Feature\\FeatureEnum;\nuse Jiminny\\Models\\Participant\\Connection;\nuse Jiminny\\Models\\Playlist\\Activity as PlaylistActivity;\nuse Jiminny\\Services\\Activity\\ActivityProviderRegistry;\nuse Jiminny\\Services\\Activity\\Import\\DataResolvers\\UpdateCrmDataByStrategy;\nuse Jiminny\\Services\\Activity\\Import\\DataResolvers\\UpdateCrmDataResolverFactory;\nuse Jiminny\\Services\\Activity\\Import\\DataResolvers\\UpdateCrmDataResolverInterface;\nuse Jiminny\\Traits\\Enums;\nuse Jiminny\\Traits\\RequiresUUID;\nuse Jiminny\\Utils\\CurrencyFormatter;\nuse NumberFormatter;\n\nuse function in_array;\n\n/**\n * Jiminny\\Models\\Activity\n *\n * @property null|int $auto_score filled from ES hydrator, not in DB!\n * @property-read Account|null $account\n * @property-read CalendarEvent|null $calendarEvent\n * @property-read Contact|null $contact\n * @property-read Lead|null $lead\n * @property-read Opportunity|null $opportunity\n * @property-read Stage|null $stage\n * @property int $id\n * @property mixed|null $uuid\n * @property string|null $source\n * @property string|null $external_id\n * @property string $provider\n * @property string|null $location\n * @property string|null $telephony_provider_id\n * @property int|null $from_participant_id\n * @property int|null $to_participant_id\n * @property int|null $device_id\n * @property string|null $type\n * @property int|null $playbook_category_id\n * @property int $user_id\n * @property int|null $lead_id\n * @property int|null $account_id\n * @property int|null $contact_id\n * @property int|null $opportunity_id\n * @property int|null $stage_id\n * @property string|null $value\n * @property int|null $crm_configuration_id\n * @property string|null $crm_provider_id\n * @property string|null $language\n * @property int|null $transcription_id\n * @property int $duration\n * @property string $status\n * @property int|null $on_air\n * @property int|null $calendar_event_id\n * @property string $recording_state\n * @property bool|null $recording_preference\n * @property int $recording_reason_code\n * @property int $summary_reminder_sent\n * @property \\Illuminate\\Support\\Carbon|null $log_reminder_sent_at\n * @property \\Illuminate\\Support\\Carbon|null $organizer_notified_at\n * @property bool|null $has_recording_prompt\n * @property bool $is_internal\n * @property int $is_locked\n * @property int $is_recording\n * @property bool|null $is_processed\n * @property bool $is_private\n * @property bool $is_instant_invite\n * @property string|null $poster_path\n * @property string|null $summary\n * @property string|null $title\n * @property string|null $description\n * @property \\Illuminate\\Support\\Carbon|null $scheduled_start_time\n * @property \\Illuminate\\Support\\Carbon|null $scheduled_end_time\n * @property \\Illuminate\\Support\\Carbon|null $actual_start_time\n * @property \\Illuminate\\Support\\Carbon|null $actual_end_time\n * @property int|null $uploaded_by\n * @property \\Illuminate\\Support\\Carbon|null $deleted_at\n * @property \\Illuminate\\Support\\Carbon|null $created_at\n * @property \\Illuminate\\Support\\Carbon|null $updated_at\n * @property string|null $average_score\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Participant> $activeParticipants\n * @property-read int|null $active_participants_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Scorecard\\ActivityScorecardRuleTrigger> $activityScorecardRuleTriggers\n * @property-read int|null $activity_scorecard_rule_triggers_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Scorecard\\ActivityScorecardRule> $activityScorecardRules\n * @property-read int|null $activity_scorecard_rules_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, AvailabilityNotification> $availabilityNotifications\n * @property-read int|null $availability_notifications_count\n * @property-read \\Jiminny\\Models\\PlaybookCategory|null $category\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, CoachRequest> $coachRequests\n * @property-read int|null $coach_requests_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\CoachingFeedback> $coachingFeedbacks\n * @property-read int|null $coaching_feedbacks_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Message> $coachingMessages\n * @property-read int|null $coaching_messages_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Comment> $comments\n * @property-read int|null $comments_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Connection> $connections\n * @property-read int|null $connections_count\n * @property-read Configuration|null $crm\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, FieldData> $data\n * @property-read int|null $data_count\n * @property-read \\Jiminny\\Models\\Device|null $device\n * @property-read \\Kalnoy\\Nestedset\\Collection<int, \\Jiminny\\Models\\Playlist> $favoritePlaylists\n * @property-read int|null $favorite_playlists_count\n * @property-read \\Jiminny\\Models\\Participant|null $from\n * @property-read string|null $activity_title\n * @property-read mixed $comment_count\n * @property-read mixed $duration_for_humans\n * @property-read string $duration_for_humans_short\n * @property-read int $favorite_count\n * @property-read mixed $favorites_count\n * @property-read mixed $formatted_value\n * @property-read string $id_string\n * @property-read \\Jiminny\\Models\\Participant|null $organizer\n * @property-read mixed $play_count\n * @property-read int|null $plays_count\n * @property-read ?ProspectInterface $prospect\n * @property-read string|null $prospect_name\n * @property-read mixed $prospect_type\n * @property-read mixed $share_count\n * @property-read int|null $shares_count\n * @property-read int|null $tracks_with_telephony_count\n * @property-read int|null $visible_comments_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\CoachingFeedback> $latestCoachingFeedbacks\n * @property-read int|null $latest_coaching_feedbacks_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Log> $logs\n * @property-read int|null $logs_count\n * @property-read \\Jiminny\\Models\\Track|null $masterTrack\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Message> $messages\n * @property-read int|null $messages_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Moment> $moments\n * @property-read int|null $moments_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Note> $notes\n * @property-read int|null $notes_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Participant\\Share> $participantShares\n * @property-read int|null $participant_shares_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, ParticipantSpeech> $participantSpeeches\n * @property-read int|null $participant_speeches_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Participant\\ParticipantStats> $participantStats\n * @property-read int|null $participant_stats_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Participant> $participants\n * @property-read int|null $participants_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, PlaylistActivity> $playlistActivities\n * @property-read int|null $playlist_activities_count\n * @property-read \\Kalnoy\\Nestedset\\Collection<int, \\Jiminny\\Models\\Playlist> $playlists\n * @property-read int|null $playlists_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Play> $plays\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Question> $questions\n * @property-read int|null $questions_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Session> $sessions\n * @property-read int|null $sessions_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Share> $shares\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Snapshot> $snapshots\n * @property-read int|null $snapshots_count\n * @property-read Stats|null $stats\n * @property-read \\Jiminny\\Models\\Participant|null $to\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, TopicTrigger> $topicTriggers\n * @property-read int|null $topic_triggers_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Track> $tracks\n * @property-read int|null $tracks_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Track> $tracksWithTelephony\n * @property-read Transcription|null $transcription\n * @property-read \\Jiminny\\Models\\User $user\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Comment> $visibleComments\n *\n * @method static \\Illuminate\\Database\\Eloquent\\Collection<int, static> all($columns = ['*'])\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity chunkByIdDesc($count, callable $callback, $column = null, $alias = null)\n * @method static \\Database\\Factories\\ActivityFactory factory(...$parameters)\n * @method static \\Illuminate\\Database\\Eloquent\\Collection<int, static> get($columns = ['*'])\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity heldBetween(\\Carbon\\Carbon $start, \\Carbon\\Carbon $end)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity idOrUuId($idOrUuid, bool $first = true)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity newModelQuery()\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity newQuery()\n * @method static Builder|Activity onlyTrashed()\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity query()\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity scheduledBetween(\\Carbon\\Carbon $start, \\Carbon\\Carbon $end)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity inOpenDeals()\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity notInOpenDeals()\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity forTeam(int $teamId)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity search(callable $searchQuery, $key = null, $sortByResults = true)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity uuid(string $uuid, bool $first = true)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereAccountId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereActualEndTime($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereActualStartTime($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereAverageScore($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereCalendarEventId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereContactId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereCreatedAt($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereCrmConfigurationId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereCrmProviderId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereDeletedAt($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereDescription($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereDeviceId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereDuration($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereFromParticipantId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereHasRecordingPrompt($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereIsInstantInvite($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereIsInternal($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereIsLocked($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereIsPrivate($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereIsProcessed($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereIsRecording($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereLanguage($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereLeadId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereLocation($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereLogReminderSentAt($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereOnAir($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereOpportunityId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereOrganizerNotifiedAt($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity wherePlaybookCategoryId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity wherePosterPath($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereProvider($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereRecordingPreference($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereRecordingReasonCode($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereRecordingState($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereScheduledEndTime($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereScheduledStartTime($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereSource($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereExternalId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereStageId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereStatus($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereSummary($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereSummaryReminderSent($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereTelephonyProviderId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereTitle($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereToParticipantId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereTranscriptionId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereType($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereUpdatedAt($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereUploadedBy($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereUserId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereUuid($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereValue($value)\n * @method static Builder|Activity withTrashed()\n * @method static Builder|Activity withoutTrashed()\n *\n * @mixin \\Eloquent\n */\nclass Activity extends Model implements\n ElasticSearch\\Contract\\Searchable,\n Workflow\\Workflow\\WorkflowAwareInterface,\n Models\\Contracts\\ActivityContract,\n Contracts\\Model\\ActivityInterface,\n UuidAwareInterface\n{\n use HasFactory;\n\n use Enums;\n use SoftDeletes;\n use RequiresUUID;\n use BitwiseFlagTrait;\n use ElasticSearch\\Model\\Searchable;\n use ActivityElasticSearchTrait;\n\n use Workflow\\Workflow\\WorkflowAware {\n transitionTo as traitTransitionTo;\n }\n\n public const int FLAG_RECORDING_REASON_DEFAULT = 0;\n\n // Recording Prompted but never started\n public const int FLAG_RECORDING_REASON_COMPLIANCE_PROMPT = 1;\n public const int FLAG_RECORDING_REASON_COMPLIANCE_RESUMED = 2;\n public const int FLAG_RECORDING_REASON_NO_AUDIO = 3;\n\n // Recording Disabled by Organization\n public const int FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT = 4;\n\n // Recording was restricted to one-side recordings only\n public const int FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT_ONE_SIDE = 8;\n\n // Recording was not started because it was internal and team setting disabled that.\n public const int FLAG_RECORDING_REASON_TEAM_INTERNAL_DISABLED = 16;\n\n // Recording was not started because it was internal and user setting disabled that.\n public const int FLAG_RECORDING_REASON_USER_INTERNAL_DISABLED = 32;\n\n // Recording was not started because user setting disabled automatic recording.\n public const int FLAG_RECORDING_REASON_USER_AUTOMATIC_DISABLED = 64;\n\n // Recording was not started because team setting disabled automatic recording.\n public const int FLAG_RECORDING_REASON_TEAM_AUTOMATIC_DISABLED = 128;\n\n // Recording was not started because user has overriden default.\n public const int FLAG_RECORDING_REASON_PREFERENCE_OVERRIDE = 256;\n\n // Recording was not started because they don't want internal, and this meeting was not scheduled/imported in time.\n public const int FLAG_RECORDING_REASON_USER_INTERNAL_DISABLED_UNSCHEDULED = 512;\n\n // Recording was not started because their team setting does excludes the meeting type.\n public const int FLAG_RECORDING_REASON_UNSUPPORTED_TYPE = 1024;\n\n // Recording was not started because the external provider disabled it (or recording is missing etc).\n public const int FLAG_RECORDING_REASON_EXTERNALLY_DISABLED = 2048;\n\n // Recording was stopped externally (\"exit-meeting\" Pusher event)\n public const int FLAG_RECORDING_REASON_STOPPED_EXTERNALLY = 384;\n\n // Recording couldn't be started due to Zoom hosting conflict error\n public const int FLAG_RECORDING_REASON_HOSTING_CONFLICT = 448;\n\n // meeting.failed event with reason code BOT_DENIED_FROM_LOBBY\n public const int FLAG_RECORDING_REASON_MEETING_BOT_DENIED_FROM_LOBBY = 4096;\n\n // meeting.failed event with reason code LOBBY_TIMEOUT\n public const int FLAG_RECORDING_REASON_MEETING_BOT_LOBBY_TIMEOUT = 8192;\n\n // meeting.failed event with reason code BOT_KICKED\n public const int FLAG_RECORDING_REASON_MEETING_BOT_KICKED = 16384;\n\n // meeting.failed event with reason code UNKNOWN\n public const int FLAG_RECORDING_REASON_MEETING_BOT_UNKNOWN = 32768;\n\n public const int FLAG_RECORDING_REASON_CONSENT_DENIED = 65536;\n\n // Invalid meeting (e.g. URL is invalid, or the meeting is not found)\n public const int FLAG_RECORDING_REASON_MEETING_BOT_INVALID = 131072;\n\n // The host stopped the recording.\n public const int FLAG_RECORDING_REASON_USER_STOPPED = 262144;\n\n // Recording was not started because an alternative vendor disabled it (or overrode it).\n public const int FLAG_RECORDING_REASON_VENDOR_OVERRIDE = 1048576;\n\n // Login required meeting.failed code\n public const int FLAG_RECORDING_REASON_LOGIN_REQUIRED = 524288;\n\n // Password for meeting was not provided - meeting.failed code\n public const int FLAG_RECORDING_REASON_MEETING_PASSWORD_NOT_PROVIDED = 2097152;\n\n // meeting.failed - when the meeting is locked\n public const int FLAG_RECORDING_REASON_MEETING_IS_LOCKED = 4194304;\n\n // max recording duration reached\n public const int FLAG_RECORDING_REASON_MAX_DURATION_REACHED = 8388608;\n\n // recording size is too small\n public const int FLAG_RECORDING_REASON_EMPTY_RECORDING = 16777216;\n\n // meeting.failed - when bot is redirected to sign in page multiple times\n public const int FLAG_RECORDING_REASON_MAX_RESTART_COUNT_IS_REACHED = 33554432;\n\n // meeting.failed event with reason code CONNECTION_LOST\n public const int FLAG_RECORDING_REASON_MEETING_BOT_CONNECTION_LOST = 67108864;\n\n // recording is corrupted.\n public const int FLAG_RECORDING_REASON_MEDIA_FILE_UNSUPPORTED_MIME_TYPE = 134217728;\n\n // meeting ended in lobby\n public const int FLAG_RECORDING_REASON_MEETING_ENDED_IN_LOBBY = 268435456;\n\n // meeting not started\n public const int FLAG_RECORDING_REASON_REASON_MEETING_NOT_STARTED = 536870912;\n\n // unfinished zoom custom disclaimer\n public const int FLAG_RECORDING_REASON_FEATURE_RULE_NOT_FOUND_ERROR = 1073741824;\n\n // recording download failed - server error\n public const int FLAG_RECORDING_REASON_SERVER_ERROR = 2147483648;\n\n // recording download failed - client code 404\n public const int FLAG_RECORDING_REASON_NOT_FOUND = 2147483649;\n\n // recording download failed - client code 401, 403\n public const int FLAG_RECORDING_REASON_ACCESS_DENIED = 2147483650;\n\n // recording download failed - client code 429\n public const int FLAG_RECORDING_REASON_TOO_MANY_REQUESTS = 2147483651;\n\n // recording download failed - unknown client error\n public const int FLAG_RECORDING_REASON_CLIENT_ERROR = 2147483652;\n\n // recording download failed - unknown error\n public const int FLAG_RECORDING_REASON_UNKNOWN_ERROR = 2147483653;\n\n // It has been setup ahead of time through calendar\n public const string STATUS_SCHEDULED = 'scheduled';\n\n // It is awaiting audio.\n public const string STATUS_PENDING = 'pending';\n\n // Participant(s) dialed in, awaiting organizer.\n public const string STATUS_RINGING = 'ringing';\n\n // Call is in progress.\n public const string STATUS_IN_PROGRESS = 'in-progress';\n\n // It has ended.\n public const string STATUS_COMPLETED = 'completed';\n\n // Cancelled prior to starting.\n public const string STATUS_CANCELLED = 'canceled';\n\n public const string STATUS_DUPLICATED = 'duplicated'; // duplicated conference\n\n public const string STATUS_STARTING_SOON = 'starting-soon';\n\n public const string STATUS_BOT_CREATE_SENT = 'bot-create-sent';\n\n public const string STATUS_BOT_INSTANCE_WORKER_ASSIGNED = 'worker-assigned';\n\n public const string STATUS_BOT_INSTANCE_STARTED = 'bot-started';\n\n // When bot instance is waiting in lobby\n public const string STATUS_BOT_INSTANCE_WAITING_LOBBY = 'bot-waiting';\n\n public const string STATUS_BUSY = 'busy';\n public const string STATUS_NO_ANSWER = 'no-answer';\n public const string STATUS_FAILED = 'failed'; // Used by SMS too\n\n // SMS related\n public const string STATUS_ACCEPTED = 'accepted';\n public const string STATUS_QUEUED = 'queued';\n public const string STATUS_SENDING = 'sending';\n public const string STATUS_SENT = 'sent';\n public const string STATUS_DELIVERED = 'delivered';\n public const string STATUS_UNDELIVERED = 'undelivered';\n public const string STATUS_RECEIVING = 'receiving';\n public const string STATUS_RECEIVED = 'received';\n public const string STATUS_RESENT = 'resent';\n\n public const array SMS_STATUSES = [\n Activity::STATUS_RECEIVED,\n Activity::STATUS_SENT,\n Activity::STATUS_DELIVERED,\n ];\n\n public const array SOFT_PHONE_CONFERENCE_STATUSES = [\n Activity::STATUS_IN_PROGRESS,\n Activity::STATUS_COMPLETED,\n ];\n\n // @todo refactor prefix from `TYPE_` to `CHANNEL_`\n public const string TYPE_SOFTPHONE = 'softphone';\n public const string TYPE_SOFTPHONE_INBOUND = 'softphone-inbound';\n public const string TYPE_CONFERENCE = 'conference';\n public const string TYPE_SMS_INBOUND = 'sms-inbound';\n public const string TYPE_SMS_OUTBOUND = 'sms-outbound';\n public const string TYPE_EMAIL_INBOUND = 'email-inbound';\n public const string TYPE_EMAIL_OUTBOUND = 'email-outbound';\n\n public const array CHANNELS = [\n self::TYPE_SOFTPHONE,\n self::TYPE_SOFTPHONE_INBOUND,\n self::TYPE_CONFERENCE,\n self::TYPE_SMS_INBOUND,\n self::TYPE_SMS_OUTBOUND,\n self::TYPE_EMAIL_INBOUND,\n self::TYPE_EMAIL_OUTBOUND,\n ];\n\n public const array PLAYABLE_CHANNELS = [\n self::TYPE_SOFTPHONE,\n self::TYPE_SOFTPHONE_INBOUND,\n self::TYPE_CONFERENCE,\n ];\n\n // Recording States\n public const string RECORDING_OFF = 'off'; // Default state\n public const string RECORDING_IN_PROGRESS = 'in-progress';\n public const string RECORDING_PAUSED = 'paused';\n public const string RECORDING_STOPPED = 'stopped'; // To never be resumed.\n public const string RECORDING_RECORDED = 'recorded'; // At least some portion of it was recorded.\n public const string RECORDING_FAILED = 'failed'; // Recording was attempted but failed for some reason.\n\n // Live Stream States\n public const int ON_AIR_DEFAULT = 0;\n public const int ON_AIR_READY = 1;\n public const int ON_AIR_PREPARING = 2;\n public const int ON_AIR_STREAMING = 3;\n public const int ON_AIR_FINISHED = 4;\n public const int ON_AIR_NOT_STREAMED = 5;\n public const int ON_AIR_ERROR = -1;\n\n public const string SOURCE_GONG = 'gong';\n public const string SOURCE_CHORUS = 'chorus';\n public const string SOURCE_OUTLOOK = 'outlook';\n public const string SOURCE_GOOGLE = 'google';\n\n // Activity Providers\n public const string PROVIDER_TWILIO = 'twilio'; // XXX: This is run via the Jiminny Provider.\n public const string PROVIDER_OUTREACH = 'outreach';\n public const string PROVIDER_ZOOM_BOT = 'zoom-bot';\n public const string PROVIDER_SALESLOFT = 'salesloft';\n public const string PROVIDER_GOOGLE = 'google';\n public const string PROVIDER_AIRCALL = 'aircall';\n public const string PROVIDER_JUSTCALL = 'justcall';\n public const string PROVIDER_GOOGLE_MEET = 'google-meet';\n public const string PROVIDER_GONG = 'gong';\n public const string PROVIDER_HUBSPOT = 'hubspot';\n public const string PROVIDER_CLOSE = 'close';\n public const string PROVIDER_TEAMS = 'ms-teams';\n public const string PROVIDER_SALESFORCE = 'salesforce';\n public const string PROVIDER_GROOVE = 'groove';\n public const string PROVIDER_XANT = 'xant';\n public const string PROVIDER_OFFICE = 'office';\n public const string PROVIDER_NATTERBOX = 'natterbox';\n public const string PROVIDER_RINGCENTRAL = 'ringcentral';\n public const string PROVIDER_RINGCENTRAL_VIDEO = 'ringcentral-video';\n public const string PROVIDER_GOTOMEETING = 'go-to-meeting';\n public const string PROVIDER_DEMODESK = 'demo-desk';\n public const string PROVIDER_DIALPAD = 'dialpad';\n public const string PROVIDER_ZOOM_PHONE = 'zoom-phone';\n public const string PROVIDER_CLOUDCALL = 'cloudcall';\n public const string PROVIDER_CLOUDCALL_US = 'cloudcall-us';\n public const string PROVIDER_EIGHT_BY_EIGHT = 'eight-by-eight'; // \"8x8\" UK\n public const string PROVIDER_EIGHT_BY_EIGHT_CA = 'eight-by-eight-ca'; // \"8x8\" Canada\n public const string PROVIDER_EIGHT_BY_EIGHT_AP = 'eight-by-eight-ap'; // \"8x8\" Australia\n public const string PROVIDER_EIGHT_BY_EIGHT_US_EAST = 'eight-by-eight-use'; // \"8x8\" US East\n public const string PROVIDER_EIGHT_BY_EIGHT_US_WEST = 'eight-by-eight-usw'; // \"8x8\" US West\n public const string PROVIDER_CONNECT_AND_SELL = 'connect-and-sell';\n public const string PROVIDER_CLOUD_TALK = 'cloud-talk';\n public const string PROVIDER_AMAZON_CONNECT = 'amazon-connect';\n public const string PROVIDER_VONAGE = 'vonage';\n public const string PROVIDER_MIGRATOR = 'migrator';\n public const string PROVIDER_UPLOADER = 'uploader';\n public const string PROVIDER_TALKDESK = 'talkdesk';\n public const string PROVIDER_TWILIO_FLEX = 'twilio-flex';\n public const string PROVIDER_TWILIO_FLEX_DIRECT = 'twilio-flex-direct';\n public const string PROVIDER_TWILIO_VIDEO = 'twilio-video';\n public const string PROVIDER_AVAYA = 'avaya';\n public const string PROVIDER_TELUS = 'telus';\n public const string PROVIDER_FIVE_NINE = 'five-nine';\n public const string PROVIDER_APOLLO = 'apollo';\n public const string PROVIDER_ORUM = 'orum';\n public const string PROVIDER_BLOOBIRDS = 'bloobirds';\n\n /**\n * @const API_PROVIDERS\n * A list of integrations that import calls via API instead of webhooks\n */\n public const array API_PROVIDERS = [\n self::PROVIDER_OUTREACH,\n self::PROVIDER_SALESLOFT,\n self::PROVIDER_HUBSPOT,\n self::PROVIDER_GROOVE,\n self::PROVIDER_XANT,\n self::PROVIDER_NATTERBOX,\n self::PROVIDER_CLOUDCALL,\n self::PROVIDER_CLOUDCALL_US,\n self::PROVIDER_EIGHT_BY_EIGHT,\n self::PROVIDER_EIGHT_BY_EIGHT_CA,\n self::PROVIDER_EIGHT_BY_EIGHT_AP,\n self::PROVIDER_EIGHT_BY_EIGHT_US_EAST,\n self::PROVIDER_EIGHT_BY_EIGHT_US_WEST,\n self::PROVIDER_CONNECT_AND_SELL,\n self::PROVIDER_CLOUD_TALK,\n self::PROVIDER_AMAZON_CONNECT,\n self::PROVIDER_VONAGE,\n self::PROVIDER_TALKDESK,\n self::PROVIDER_TWILIO_VIDEO,\n self::PROVIDER_TWILIO_FLEX,\n self::PROVIDER_TWILIO_FLEX_DIRECT,\n self::PROVIDER_FIVE_NINE,\n self::PROVIDER_APOLLO,\n self::PROVIDER_ORUM,\n self::PROVIDER_BLOOBIRDS,\n self::PROVIDER_RINGCENTRAL,\n self::PROVIDER_AVAYA,\n self::PROVIDER_TELUS,\n ];\n\n public const array FINITE_STATES = [\n self::TYPE_SOFTPHONE => [\n self::STATUS_COMPLETED,\n self::STATUS_FAILED,\n self::STATUS_NO_ANSWER,\n self::STATUS_BUSY,\n ],\n self::TYPE_SOFTPHONE_INBOUND => [\n self::STATUS_COMPLETED,\n self::STATUS_FAILED,\n self::STATUS_NO_ANSWER,\n self::STATUS_BUSY,\n ],\n self::TYPE_CONFERENCE => self::FINITE_STATES_CONFERENCE,\n ];\n\n public const array FINITE_STATES_CONFERENCE = [\n self::STATUS_COMPLETED,\n self::STATUS_FAILED,\n self::STATUS_CANCELLED,\n ];\n\n public const array MEETING_BOT_JOIN_ATTEMPTED = [\n self::STATUS_BOT_INSTANCE_WAITING_LOBBY,\n self::STATUS_BOT_INSTANCE_STARTED,\n ];\n\n public static array $enumStatuses = [\n self::STATUS_SCHEDULED,\n self::STATUS_PENDING,\n self::STATUS_RINGING,\n self::STATUS_IN_PROGRESS,\n self::STATUS_COMPLETED,\n self::STATUS_CANCELLED,\n self::STATUS_BUSY,\n self::STATUS_NO_ANSWER,\n self::STATUS_FAILED,\n self::STATUS_ACCEPTED,\n self::STATUS_QUEUED,\n self::STATUS_SENDING,\n self::STATUS_SENT,\n self::STATUS_RESENT,\n self::STATUS_DELIVERED,\n self::STATUS_UNDELIVERED,\n self::STATUS_RECEIVING,\n self::STATUS_RECEIVED,\n self::STATUS_BOT_INSTANCE_WAITING_LOBBY,\n self::STATUS_STARTING_SOON,\n self::STATUS_BOT_INSTANCE_WORKER_ASSIGNED,\n self::STATUS_BOT_INSTANCE_STARTED,\n self::STATUS_DUPLICATED,\n ];\n\n public static array $enumProviders = [\n self::PROVIDER_TWILIO,\n self::PROVIDER_OUTREACH,\n self::PROVIDER_ZOOM_BOT,\n self::PROVIDER_SALESLOFT,\n self::PROVIDER_AIRCALL,\n self::PROVIDER_JUSTCALL,\n self::PROVIDER_GOOGLE_MEET,\n self::PROVIDER_GONG,\n self::PROVIDER_HUBSPOT,\n self::PROVIDER_CLOSE,\n self::PROVIDER_TEAMS,\n self::PROVIDER_SALESFORCE,\n self::PROVIDER_GROOVE,\n self::PROVIDER_XANT,\n self::PROVIDER_GOOGLE,\n self::PROVIDER_OFFICE,\n self::PROVIDER_NATTERBOX,\n self::PROVIDER_RINGCENTRAL,\n self::PROVIDER_RINGCENTRAL_VIDEO,\n self::PROVIDER_GOTOMEETING,\n self::PROVIDER_DEMODESK,\n self::PROVIDER_DIALPAD,\n self::PROVIDER_ZOOM_PHONE,\n self::PROVIDER_CLOUDCALL,\n self::PROVIDER_CLOUDCALL_US,\n self::PROVIDER_EIGHT_BY_EIGHT,\n self::PROVIDER_EIGHT_BY_EIGHT_CA,\n self::PROVIDER_EIGHT_BY_EIGHT_AP,\n self::PROVIDER_EIGHT_BY_EIGHT_US_EAST,\n self::PROVIDER_EIGHT_BY_EIGHT_US_WEST,\n self::PROVIDER_CONNECT_AND_SELL,\n self::PROVIDER_CLOUD_TALK,\n self::PROVIDER_AMAZON_CONNECT,\n self::PROVIDER_VONAGE,\n self::PROVIDER_TALKDESK,\n self::PROVIDER_TWILIO_FLEX,\n self::PROVIDER_TWILIO_FLEX_DIRECT,\n self::PROVIDER_TWILIO_VIDEO,\n self::PROVIDER_AVAYA,\n self::PROVIDER_TELUS,\n self::PROVIDER_FIVE_NINE,\n self::PROVIDER_APOLLO,\n self::PROVIDER_ORUM,\n self::PROVIDER_BLOOBIRDS,\n ];\n\n public static $enumRecordingStates = [\n self::RECORDING_OFF, // Default state\n self::RECORDING_IN_PROGRESS,\n self::RECORDING_PAUSED,\n self::RECORDING_STOPPED,\n self::RECORDING_RECORDED,\n self::RECORDING_FAILED,\n ];\n\n // @Important:\n // This collection is not used anywhere, and is fully duplicated by the Channels const.\n // Validate if it is referred somehow via the enum trait, and if not, remove it entirely.\n // An even better strategy will be to move all those constants to a dedicated class\n protected array $enumTypes = [\n self::TYPE_SOFTPHONE,\n self::TYPE_SOFTPHONE_INBOUND,\n self::TYPE_CONFERENCE,\n self::TYPE_SMS_INBOUND,\n self::TYPE_SMS_OUTBOUND,\n self::TYPE_EMAIL_INBOUND,\n self::TYPE_EMAIL_OUTBOUND,\n ];\n\n protected static $enumFailedStatuses = [\n self::STATUS_NO_ANSWER,\n self::STATUS_FAILED,\n self::STATUS_BUSY,\n self::STATUS_CANCELLED,\n ];\n\n protected $table = 'activities';\n\n protected $fillable = [\n // Type of activity.\n 'type', // @todo refactor to `channel`\n // The activity type.\n 'playbook_category_id',\n // User who hosts the activity.\n 'user_id',\n // Related Lead record (if applicable)\n 'lead_id',\n // Related Account record (if applicable)\n 'account_id',\n // Related Contact record (if applicable)\n 'contact_id',\n // Related Opportunity record (if applicable)\n 'opportunity_id',\n // Stage of activity.\n 'stage_id',\n // Value of opportunity.\n 'value',\n // If the activity relates to a CRM task.\n 'crm_provider_id',\n // If the activity was created through an external device.\n 'device_id',\n // the activity's language code\n 'language',\n // transcription id\n 'transcription_id',\n // Duration of the call, with microseconds precision.\n 'duration',\n // One of enumStatuses above.\n 'status',\n // Have we reminded them to log the call?\n 'log_reminder_sent_at',\n // If activity is private or inter-org, flagged here.\n 'is_internal',\n // Managers and above can mark a call as private, to exclude it from other team members\n 'is_private',\n 'is_processed',\n // Boolean for this activity being instant invite handled.\n 'is_instant_invite',\n // If activity is in recording state, flagged here.\n 'recording_state',\n // If activity recording is overidden from default.\n 'recording_preference',\n // if recording did (not) happen, why that is\n 'recording_reason_code',\n // Average score, updated during\n 'average_score',\n // Summary that the organizer has taken after the call.\n 'summary',\n // Subject of the activity, usually taken from calendar event.\n 'title',\n // Description of the activity, usually taken from calendar event.\n 'description',\n // Start time, usually taken from calendar event.\n 'scheduled_start_time',\n // End time, usually taken from calendar event.\n 'scheduled_end_time',\n // When the call actually started.\n 'actual_start_time',\n // When the call actually ended.\n 'actual_end_time',\n // SMS: Message reference\n 'telephony_provider_id',\n // SMS: Participant who sent message\n 'from_participant_id',\n // SMS: Participant who should receive the message\n 'to_participant_id',\n // When an external guest joins an organizers meeting room and the organizer is not present,\n // send them an SMS notification that someone has joined.\n 'organizer_notified_at',\n // where was the activity imported from\n 'source',\n // The id in the source system (e.g. the bot id in Recall.ai)\n 'external_id',\n // The provider, by default it is twilio.\n 'provider',\n // Meeting location url\n 'location',\n // The snapshot for displaying a poster image.\n 'poster_path',\n 'crm_configuration_id',\n // If there is an automated message that the conversation is being recorded\n 'has_recording_prompt',\n // If the activity is being live-streamed\n 'on_air',\n 'calendar_event_id',\n ];\n\n protected $appends = [\n 'id_string',\n 'organizer',\n ];\n\n protected $hidden = [\n 'uuid',\n ];\n\n protected $visible = [\n 'id_string',\n 'type',\n 'duration',\n 'average_score',\n 'status',\n 'log_reminder_sent_at',\n 'title',\n 'description',\n 'is_internal',\n 'scheduled_start_time',\n 'scheduled_end_time',\n 'actual_start_time',\n 'actual_end_time',\n 'user',\n 'category',\n 'account',\n 'contact',\n 'opportunity',\n 'lead',\n 'stage',\n 'stats',\n 'participants',\n 'playlists',\n 'tracks',\n 'comments',\n 'plays',\n 'coachingFeedbacks',\n 'shares',\n 'favorites',\n 'language',\n 'transcription',\n 'is_private',\n 'is_instant_invite',\n 'on_air',\n 'calendar_event_id',\n ];\n\n protected function casts(): array\n {\n return [\n 'scheduled_start_time' => 'datetime',\n 'scheduled_end_time' => 'datetime',\n 'actual_start_time' => 'datetime',\n 'actual_end_time' => 'datetime',\n 'organizer_notified_at' => 'datetime',\n 'log_reminder_sent_at' => 'datetime',\n 'is_internal' => 'boolean',\n 'duration' => 'integer',\n 'average_score' => 'decimal:2',\n 'is_private' => 'boolean',\n 'is_processed' => 'boolean',\n 'is_instant_invite' => 'boolean',\n 'value' => 'decimal:2',\n 'recording_preference' => 'boolean',\n 'recording_reason_code' => 'integer',\n 'has_recording_prompt' => 'boolean',\n 'on_air' => 'integer',\n ];\n }\n\n protected static function boot()\n {\n parent::boot();\n\n static::updated(static function (Activity $activity) {\n // If activity is about to start (pending, ringing, in-progress) or event is scheduled in less than 1 week\n if (in_array($activity->status, [Activity::STATUS_PENDING, Activity::STATUS_RINGING, Activity::STATUS_IN_PROGRESS], true) ||\n ($activity->scheduled_start_time && (int) $activity->scheduled_start_time->diffInWeeks(new Carbon(), true) < 1)) {\n if ($activity->isDirty('status')) {\n event(new StatusUpdated($activity));\n }\n\n if ($activity->isDirty('stage_id')) {\n event(new StageUpdated($activity));\n }\n\n if ($activity->isDirty(['lead_id', 'account_id', 'contact_id'])) {\n event(new ProspectUpdated($activity));\n }\n\n if ($activity->isDirty('opportunity_id')) {\n event(new ActivityUpdated($activity, 'activity.opportunity-updated', Auth::user()));\n }\n\n if ($activity->isDirty('title')) {\n event(new TitleUpdated($activity));\n }\n }\n\n if ($activity->isDirty('playbook_category_id')) {\n event(new ActivityTypeUpdated($activity));\n }\n });\n\n static::deleted(static function (Activity $activity) {\n // Hard delete associated playlistActivities\n $activity->playlistActivities()->delete();\n });\n }\n\n public function getOrganizerAttribute(): ?Participant\n {\n $participant = $this->participants()->where('user_id', $this->user_id)->first();\n\n if (! $participant instanceof Participant && $participant !== null) {\n throw new RuntimeException(sprintf('$participant must be an instance of \"%s\" or null', Participant::class));\n }\n\n return $participant;\n }\n\n public function getFormattedValueAttribute()\n {\n $currencyCode = 'USD';\n if ($this->opportunity) {\n $currencyCode = $this->opportunity->getCurrencyCode();\n }\n\n $formatter = new CurrencyFormatter();\n $formatter->setTextAttribute(NumberFormatter::CURRENCY_CODE, $currencyCode);\n $formatter->setAttribute(NumberFormatter::MAX_FRACTION_DIGITS, 0);\n\n return $formatter->format($this->value, $currencyCode);\n }\n\n public function getProspectNameAttribute(): ?string\n {\n $prospectName = null;\n\n if ($this->lead_id) {\n $prospectName = $this->lead->name;\n } elseif ($this->contact_id) {\n $prospectName = $this->contact->name;\n } elseif ($this->account_id) {\n $prospectName = $this->account->name;\n }\n\n return $prospectName;\n }\n\n public function getProspectName(): ?string\n {\n /** @var string|null */\n return $this->getAttribute('prospect_name');\n }\n\n /**\n * Get activity title depending on prospect or title\n */\n public function getActivityTitleAttribute(): ?string\n {\n $activityTitle = null;\n if ($this->prospect && $this->prospect->getName()) {\n if ($this->account_id) {\n $activityTitle = $this->account->name;\n } elseif ($this->lead_id) {\n $activityTitle = $this->lead->company;\n } elseif ($this->contact_id) {\n $activityTitle = $this->contact->account ? $this->contact->account->name : $this->contact->name;\n }\n } elseif ($this->title) {\n $activityTitle = $this->title;\n }\n\n return $activityTitle;\n }\n\n public function wasRecentlyCreated(): bool\n {\n return $this->wasRecentlyCreated;\n }\n\n public function getProspectTypeAttribute()\n {\n $prospectType = null;\n\n if ($this->lead_id) {\n $prospectType = 'Lead';\n } elseif ($this->contact_id) {\n $prospectType = 'Contact';\n } elseif ($this->account_id) {\n $prospectType = 'Account';\n }\n\n return $prospectType;\n }\n\n /**\n * Return the best match for prospect. Results are in the following order of priority:\n * 1. Lead\n * 2. Contact\n * 3. Account\n * 4. NULL\n */\n public function getProspectAttribute(): ?ProspectInterface\n {\n if ($this->hasLead()) {\n return $this->getLead();\n }\n\n if ($this->hasContact()) {\n return $this->getContact();\n }\n\n if ($this->hasAccount()) {\n return $this->getAccount();\n }\n\n return null;\n }\n\n public function getTitleAttribute($value): ?string\n {\n return \\getActivityTitleAttribute(\n $this->user->name,\n $this->getType(),\n $value,\n $this->prospect->name ?? null,\n $this->from->national_phone_number ?? null\n );\n }\n\n public function getTitle(): ?string\n {\n return $this->getAttribute('title');\n }\n\n public function getSummary(): ?string\n {\n return $this->getAttribute('summary');\n }\n\n public function isInternal(): bool\n {\n return $this->getAttribute('is_internal');\n }\n\n public function getIsPrivate(): bool\n {\n return $this->getAttribute('is_private');\n }\n\n public function getDescription(): ?string\n {\n return $this->getAttribute('description');\n }\n\n public function hasTitle(): bool\n {\n return $this->getOriginal('title') !== null;\n }\n\n public function getPlayCountAttribute()\n {\n return $this->getPlaysCountAttribute();\n }\n\n public function getPlaysCountAttribute()\n {\n if (! isset($this->attributes['plays_count'])) {\n $this->loadCount('plays');\n }\n\n return $this->attributes['plays_count'];\n }\n\n public function getCommentCountAttribute()\n {\n return $this->getCommentsCountAttribute();\n }\n\n public function getCommentsCountAttribute()\n {\n if (! isset($this->attributes['comments_count'])) {\n $this->loadCount('comments');\n }\n\n return $this->attributes['comments_count'];\n }\n\n public function getVisibleCommentsCountAttribute()\n {\n if (! isset($this->attributes['visible_comments_count'])) {\n $activityCommentsService = app(ActivityCommentService::class);\n $user = Auth::user() instanceof User ? Auth::user() : null;\n $this->attributes['visible_comments_count'] = $activityCommentsService\n ->getVisibleCommentsCount($this, $user);\n }\n\n return $this->attributes['visible_comments_count'];\n }\n\n public function getShareCountAttribute()\n {\n return $this->getSharesCountAttribute();\n }\n\n public function getSharesCountAttribute()\n {\n if (! isset($this->attributes['shares_count'])) {\n $this->loadCount('shares');\n }\n\n return $this->attributes['shares_count'];\n }\n\n\n /**\n * Get the count of favorites playlists this activity appears in\n */\n public function getFavoriteCountAttribute(): int\n {\n return $this->getFavoritesCountAttribute();\n }\n\n public function getFavoritesCountAttribute()\n {\n if (! isset($this->attributes['favorites_count'])) {\n $this->loadCount('favorites');\n }\n\n return $this->attributes['favorites_count'];\n }\n\n public function getActiveParticipantsCountAttribute()\n {\n if (! isset($this->attributes['active_participants_count'])) {\n $this->loadCount('activeParticipants');\n }\n\n return $this->attributes['active_participants_count'];\n }\n\n public function getTracksWithTelephonyCountAttribute()\n {\n if (! isset($this->attributes['tracks_with_telephony_count'])) {\n $this->loadCount('tracksWithTelephony');\n }\n\n return $this->attributes['tracks_with_telephony_count'];\n }\n\n /**\n * @TEMP\n * $this->loadCount('tracksWithTelephony') throws null pointer exception\n */\n public function countTracksWithTelephony(): int\n {\n return $this->tracks()->whereNotNull('telephony_provider_id')->count();\n }\n\n public function getDuration(): float\n {\n return $this->getAttribute('duration');\n }\n\n public function getDurationForHumansAttribute()\n {\n return Carbon::now()->subSeconds($this->duration)->diffForHumans(now(), true);\n }\n\n public function getDurationForHumansShortAttribute(): string\n {\n return Carbon::now()->subSeconds($this->duration)->diffForHumans(now(), true, true);\n }\n\n public function hasRecordingPreference(): bool\n {\n return $this->getAttribute('recording_preference') !== null;\n }\n\n public function getRecordingPreference()\n {\n return $this->getAttribute('recording_preference');\n }\n\n /** @return BelongsTo<User, self> */\n public function user(): BelongsTo\n {\n return $this->belongsTo(User::class)->with('group');\n }\n\n public function device()\n {\n return $this->belongsTo(Device::class);\n }\n\n public function category()\n {\n return $this->belongsTo(PlaybookCategory::class, 'playbook_category_id');\n }\n\n public function getCategory(): ?PlaybookCategory\n {\n return $this->getAttribute('category');\n }\n\n public function getPlaybookCategoryId(): ?int\n {\n return $this->getAttribute('playbook_category_id');\n }\n\n public function hasStats(): bool\n {\n return $this->getAttribute('stats') !== null;\n }\n\n public function getStats(): ?Stats\n {\n return $this->getAttribute('stats');\n }\n\n public function stats(): HasOne\n {\n return $this->hasOne(Stats::class);\n }\n\n public function participantStats(): Eloquent\\Relations\\HasManyThrough\n {\n return $this->hasManyThrough(\n Models\\Participant\\ParticipantStats::class,\n Participant::class,\n 'activity_id',\n 'participant_id'\n );\n }\n\n public function getParticipantStats(): Eloquent\\Collection\n {\n return $this->getAttribute('participantStats');\n }\n\n public function account()\n {\n return $this->belongsTo(Account::class);\n }\n\n public function contact()\n {\n return $this->belongsTo(Contact::class)->with(['account']);\n }\n\n public function lead()\n {\n return $this->belongsTo(Lead::class)->with(['stage', 'recordType']);\n }\n\n /**\n * @return BelongsTo<Opportunity, self>\n */\n public function opportunity(): BelongsTo\n {\n /** @var BelongsTo<Opportunity, self> */\n return $this->belongsTo(Opportunity::class);\n }\n\n public function stage()\n {\n return $this->belongsTo(Stage::class);\n }\n\n /**\n * @return HasMany<Session>\n */\n public function sessions(): HasMany\n {\n return $this->hasMany(Session::class);\n }\n\n /**\n * @return HasMany|ParticipantSpeech[]|Eloquent\\Collection\n */\n public function participantSpeeches()\n {\n return $this->hasMany(ParticipantSpeech::class);\n }\n\n public function getParticipantSpeeches(): Eloquent\\Collection\n {\n return $this->getAttribute('participantSpeeches');\n }\n\n /**\n * @return HasMany|Log[]|Eloquent\\Collection\n */\n public function logs()\n {\n return $this->hasMany(Log::class);\n }\n\n /**\n * @return HasMany|Moment[]|Eloquent\\Collection\n */\n public function moments()\n {\n return $this->hasMany(Moment::class);\n }\n\n /**\n * @return HasMany|Note[]|Eloquent\\Collection\n */\n public function notes()\n {\n return $this->hasMany(Note::class);\n }\n\n /**\n * @return Eloquent\\Collection|Note[]\n */\n public function getNotes(): Eloquent\\Collection\n {\n return $this->getAttribute('notes');\n }\n\n /**\n * @return HasMany|Message[]|Eloquent\\Collection\n */\n public function messages()\n {\n return $this->hasMany(Message::class);\n }\n\n public function coachingMessages(): HasMany\n {\n return $this->hasMany(Message::class)\n ->where('is_private', 1);\n }\n\n public function getCoachingMessages(): Eloquent\\Collection\n {\n return $this->getAttribute('coachingMessages');\n }\n\n public function participants(): HasMany\n {\n return $this->hasMany(Participant::class);\n }\n\n public function getSnapshots(): Eloquent\\Collection\n {\n return $this->getAttribute('snapshots');\n }\n\n /** @return HasMany<Track> */\n public function tracks(): HasMany\n {\n return $this->hasMany(Track::class);\n }\n\n public function tracksWithTelephony(): HasMany\n {\n return $this->hasMany(Track::class)->whereNotNull('telephony_provider_id');\n }\n\n public function getTracksWithTelephony(): Eloquent\\Collection\n {\n return $this->getAttribute('tracksWithTelephony');\n }\n\n /** @return Collection|Track[] */\n public function getTracks(): Eloquent\\Collection\n {\n return $this->getAttribute('tracks');\n }\n\n public function masterTrack(): HasOne\n {\n return $this->hasOne(Track::class)->where('is_master', 1)\n ->whereIn('format', [Track::FORMAT_WAV, Track::FORMAT_M3U8])\n ->latest();\n }\n\n public function getMasterTrack(): ?Track\n {\n /** @var Track|null */\n return $this->getAttribute('masterTrack');\n }\n\n public function transcription(): Eloquent\\Relations\\BelongsTo\n {\n return $this->belongsTo(Transcription::class, 'transcription_id');\n }\n\n public function findTranscriptionPromptSummaries(): Collection\n {\n $transcriptionId = $this->getTranscriptionId();\n if (is_null($transcriptionId)) {\n return new Collection();\n }\n\n return Models\\AiPrompt::query()\n ->where('transcription_id', $transcriptionId)\n ->get();\n }\n\n public function getTranscription(): Transcription\n {\n return $this->getAttribute('transcription');\n }\n\n public function hasTranscription(): bool\n {\n return $this->getAttribute('transcription') !== null;\n }\n\n public function setTranscriptionId(int $transcriptionId): Activity\n {\n $this->setAttribute('transcription_id', $transcriptionId);\n\n return $this;\n }\n\n public function unsetTranscriptionId(): self\n {\n $this->setAttribute('transcription_id', null);\n\n return $this;\n }\n\n public function getTranscriptionId(): ?int\n {\n return $this->getAttribute('transcription_id');\n }\n\n /** @deprecated */\n public function hasTranscriptionId(): bool\n {\n return $this->getAttribute('transcription_id') !== null;\n }\n\n public function coachRequests()\n {\n return $this->hasMany(CoachRequest::class);\n }\n\n public function availabilityNotifications()\n {\n return $this->hasMany(AvailabilityNotification::class);\n }\n\n public function processingStates()\n {\n return $this->hasMany(Models\\Activity\\ActivityProcessingState::class);\n }\n\n public function uploadSettings()\n {\n return $this->hasMany(ActivityUploadSetting::class);\n }\n\n public function comments()\n {\n return $this->hasMany(Comment::class);\n }\n\n public function getComments(): Eloquent\\Collection\n {\n return $this->getAttribute('comments');\n }\n\n public function visibleComments()\n {\n $rel = $this->hasMany(Comment::class);\n // Doesn't have auth()->user() in some tests, breaks the build\n if ($user = auth()->user()) {\n return $rel->visibleThreads($user->id);\n }\n\n return $rel;\n }\n\n public function snapshots(): HasMany\n {\n return $this->hasMany(Snapshot::class);\n }\n\n public function calendarEvent()\n {\n return $this->belongsTo(CalendarEvent::class);\n }\n\n public function getCalendarEvent(): ?CalendarEvent\n {\n return $this->getAttribute('calendarEvent');\n }\n\n public function latestCoachingFeedbacks(): HasMany\n {\n return $this->hasMany(CoachingFeedback::class)->latest();\n }\n\n public function playlists(): BelongsToMany\n {\n return $this->belongsToMany(Playlist::class, 'playlist_activities')\n ->withPivot('id', 'uuid', 'user_id', 'start_time', 'end_time')\n ->using(PlaylistActivity::class)\n ->withTimestamps();\n }\n\n public function coachingFeedbacks(): HasMany\n {\n return $this->hasMany(CoachingFeedback::class);\n }\n\n /**\n * @return Eloquent\\Collection|CoachingFeedback[]\n */\n public function getCoachingFeedback(?int $visibility = null): Eloquent\\Collection\n {\n $feedbacks = $this->coachingFeedbacks();\n if ($visibility !== null) {\n $feedbacks = $feedbacks->where('visibility', $visibility);\n }\n\n return $feedbacks->get();\n }\n\n /** @return Eloquent\\Collection<int, PlaylistActivity> */\n public function favoritedBy(User $user): Eloquent\\Collection\n {\n return $this->favorites()->where('user_id', $user->getId())->get();\n }\n\n /**\n * Checks whether consumer has added this activity to their favorites playlist\n * In addition a default playlist gets created if not already present\n */\n public function wasFavoritedBy(User $user): bool\n {\n $playlist = $user->favoritePlaylist();\n\n return $playlist\n ->activities()\n ->where('activity_id', '=', $this->getId())\n ->exists();\n }\n\n /**\n * @return HasMany<PlaylistActivity>\n */\n public function playlistActivities(): HasMany\n {\n return $this->hasMany(PlaylistActivity::class);\n }\n\n /**\n * @return HasManyThrough<Playlist>\n */\n public function favoritePlaylists(): HasManyThrough\n {\n return $this->hasManyThrough(\n Playlist::class,\n PlaylistActivity::class,\n 'activity_id',\n 'id',\n 'id',\n 'playlist_id'\n )->where('is_default', 1);\n }\n\n /**\n * @return Eloquent\\Collection<int, Playlist>\n */\n public function getFavoritePlaylists(): Eloquent\\Collection\n {\n return $this->getAttribute('favoritePlaylists');\n }\n\n /**\n * Get activities from the default/favorite playlist\n *\n * @return Eloquent\\Builder|static\n */\n public function favorites()\n {\n return $this->playlistActivities()->whereHas('playlist', function ($query) {\n $query->where('is_default', 1);\n });\n }\n\n /**\n * @return Model|SubscriptionSet|null|object\n */\n public function subscribedBy(User $user)\n {\n if ($this->prospect === null) {\n return null;\n }\n\n return SubscriptionSet::select('activity_subscription_sets.*')\n ->where('user_id', $user->id)\n ->join('activity_subscriptions', function ($join) {\n $join\n ->on('subscription_set_id', '=', 'activity_subscription_sets.id');\n\n if ($this->account_id) {\n if ($this->opportunity_id) {\n $join\n ->where('followable_type', 'opportunity')\n ->where('followable_id', $this->opportunity_id);\n } else {\n $join\n ->where('followable_type', 'account')\n ->where('followable_id', $this->account_id);\n }\n } elseif ($this->contact_id) {\n $join\n ->where('followable_type', 'contact')\n ->where('followable_id', $this->contact_id);\n } elseif ($this->lead_id) {\n $join\n ->where('followable_type', 'lead')\n ->where('followable_id', $this->lead_id);\n }\n })\n ->first();\n }\n\n /**\n * @return array|Eloquent\\Builder[]|Eloquent\\Collection|SubscriptionSet[]\n */\n public function subscribers()\n {\n if ($this->prospect === null) {\n return [];\n }\n\n return SubscriptionSet::with(['subscriptions', 'user'])\n ->whereHas('subscriptions', function ($query) {\n if ($this->account_id) {\n if ($this->opportunity_id) {\n $query\n ->where('followable_type', 'opportunity')\n ->where('followable_id', $this->opportunity_id);\n } else {\n $query\n ->where('followable_type', 'account')\n ->where('followable_id', $this->account_id);\n }\n } elseif ($this->contact_id) {\n $query\n ->where('followable_type', 'contact')\n ->where('followable_id', $this->contact_id);\n } elseif ($this->lead_id) {\n $query\n ->where('followable_type', 'lead')\n ->where('followable_id', $this->lead_id);\n } else {\n // Nothing to join on?\n // refactor - use Jiminny specific exception\n throw new InvalidArgumentException('Cannot join on a specific customer filter.');\n }\n })\n ->whereHas('user', function ($query) {\n $query\n ->where('team_id', $this->user->team_id)\n ->where('status', User::STATUS_ACTIVE);\n })\n ->get();\n }\n\n /**\n * @return HasMany|Builder|Eloquent\\Collection|Play[]\n */\n public function plays()\n {\n return $this->hasMany(Play::class);\n }\n\n public function getPlays(): Eloquent\\Collection\n {\n return $this->getAttribute('plays');\n }\n\n public function playsBy(User $user)\n {\n /** @var Builder $builder */\n $builder = $this->plays()->where('user_id', $user->id);\n\n return $builder->get();\n }\n\n /**\n * Check if activity was played by a user\n */\n public function wasPlayedBy(User $user): bool\n {\n return $this->plays()->where('user_id', $user->id)->exists();\n }\n\n public function shares()\n {\n return $this->hasMany(Share::class);\n }\n\n /** @return BelongsTo<Participant, self> */\n public function from(): BelongsTo\n {\n return $this->belongsTo(Participant::class, 'from_participant_id');\n }\n\n /** @return BelongsTo<Participant, self> */\n public function to(): BelongsTo\n {\n return $this->belongsTo(Participant::class, 'to_participant_id');\n }\n\n /**\n * Get all of the connections through the participants.\n */\n public function connections()\n {\n return $this->hasManyThrough(\n Connection::class,\n Participant::class,\n 'activity_id',\n 'participant_id'\n );\n }\n\n public function getConnections(): Eloquent\\Collection\n {\n return $this->getAttribute('connections');\n }\n\n /**\n * Get all of the shares through the participants.\n */\n public function participantShares()\n {\n return $this->hasManyThrough(\n Participant\\Share::class,\n Participant::class,\n 'activity_id',\n 'participant_id'\n );\n }\n\n public function getParticipantShares(): Eloquent\\Collection\n {\n return $this->getAttribute('participantShares');\n }\n\n public function topicTriggers(): HasMany\n {\n return $this->hasMany(TopicTrigger::class);\n }\n\n public function activityScorecardRuleTriggers(): HasMany\n {\n return $this->hasMany(Models\\Scorecard\\ActivityScorecardRuleTrigger::class);\n }\n\n public function activityScorecardRules(): HasMany\n {\n return $this->hasMany(Models\\Scorecard\\ActivityScorecardRule::class);\n }\n\n public function questions(): HasMany\n {\n return $this->hasMany(Question::class);\n }\n\n /**\n * Get all the custom data attached to it.\n */\n public function data(): HasMany\n {\n return $this->hasMany(FieldData::class);\n }\n\n public function getData(): Eloquent\\Collection\n {\n /** @var Eloquent\\Collection */\n return $this->getAttribute('data');\n }\n\n #[Scope]\n protected function heldBetween($query, Carbon $start, Carbon $end)\n {\n // Sanity check.\n $from = min($start, $end);\n $until = max($start, $end);\n\n return $query\n ->where('actual_start_date', '>=', $from)\n ->where('actual_end_date', '<=', $until);\n }\n\n #[Scope]\n protected function scheduledBetween($query, Carbon $start, Carbon $end)\n {\n // Sanity check.\n $from = min($start, $end);\n $until = max($start, $end);\n\n return $query\n ->where('scheduled_start_date', '>=', $from)\n ->where('scheduled_end_date', '<=', $until);\n }\n\n /**\n * @param Builder<self> $query\n *\n * @return Builder<self>\n */\n #[Scope]\n protected function forTeam(Builder $query, int $teamId): Builder\n {\n /** @var Builder<self> */\n return $query->whereHas('user', static function (Builder $query) use ($teamId): void {\n $query->where('team_id', $teamId);\n });\n }\n\n /**\n * @param Builder<self> $query\n *\n * @return Builder<self>\n */\n #[Scope]\n protected function inOpenDeals(Builder $query): Builder\n {\n /** @var Builder<self> */\n return $query->whereHas(\n 'opportunity',\n static fn (Builder $query): Builder => $query\n ->where('is_closed', false)\n ->where('deleted_at', '=', null),\n );\n }\n\n /**\n * @param Builder<self> $query\n *\n * @return Builder<self>\n */\n #[Scope]\n protected function notInOpenDeals(Builder $query): Builder\n {\n /** @var Builder<self> */\n return $query->where(\n static fn (Builder $query): Builder => $query->whereNull('opportunity_id')\n ->orWhereHas(\n 'opportunity',\n static fn (Builder $query): Builder => $query->where('is_closed', true),\n )\n ->orWhereHas(\n 'opportunity',\n static fn (Builder $query): Builder => $query->withTrashed()->where('deleted_at', '!=', null),\n ),\n );\n }\n\n /**\n * Finds a participant and updates it with data. If participant doesn't exist creates a new participant from data.\n *\n * @param array $data participant data used to identify the participant and update it\n * @param bool $enterRoom true if participant is entering the room. false if we just want to update some participant data\n * @param Carbon|null $enterTime if $enterNow is true then this is the join time when the actual enter has occurred\n */\n public function updateOrCreateParticipant(\n array $data,\n bool $enterRoom = true,\n ?Carbon $enterTime = null,\n bool $nameMatching = false,\n ): Participant {\n $search = [];\n $participant = null;\n\n if (isset($data['user_id'])) {\n // Check if they already exist based on their ID.\n $search['user_id'] = $data['user_id'];\n } elseif (isset($data['provider_id'])) {\n $search['provider_id'] = $data['provider_id'];\n } elseif ($nameMatching && isset($data['name'])) {\n $search['name'] = $data['name'];\n }\n\n if (! empty($data['email'])) {\n $search['email'] = $data['email'];\n\n // If we have their email, this should be unique enough to lookup (e.g. calendar event based participant).\n unset($search['provider_id']);\n }\n\n // Search by phone number only in case nothing else is available to search by.\n if (array_key_exists('phone_number', $data) && empty($search)) {\n $search['phone_number'] = $data['phone_number'];\n }\n\n if (! empty($search)) {\n // Do a lookup now to see if we have a match on the provided data.\n $lookup = array_map(static function ($key, $value): array {\n return [$key, $value];\n }, array_keys($search), $search);\n\n $participant = $this->participants()->withTrashed()->where($lookup)->first();\n }\n\n // Do a partial match on the name and search in the team members.\n if (! $participant instanceof Participant && $nameMatching && ! empty($data['name'])) {\n $participantMatcher = app(MeetingBot\\Service\\ParticipantMatcher::class);\n\n if (! $participantMatcher instanceof MeetingBot\\Service\\ParticipantMatcher) {\n throw new LogicException('Expecting ParticipantMatcher service instance');\n }\n\n $participant = $participantMatcher->match($this, $data['name']);\n\n // If we've found good participant, avoid data overwrite in `$participant->fill($data)` below.\n if ($participant instanceof Models\\Participant && $participant->hasName()) {\n unset($data['name']); // Thoughts: should we unset also $data['user_id'] and $data['email'] ?\n }\n }\n\n if (! $participant instanceof Participant) {\n // If no match, create a new participant.\n if (empty($search)) {\n $participant = $this->participants()->create();\n } else {\n // If no match, create a new participant but avoid creating duplicates\n $participant = $this->participants()->withTrashed()->firstOrNew($search);\n }\n }\n\n // If we have just recycled a deleted participant\n if ($participant->trashed()) {\n $participant->deleted_at = null;\n }\n\n // Deal with the case when calendar syncs the event while it's in progress.\n // We should prevent change of the participant name, because speeches mapping will fail.\n if ($enterRoom === false\n && $this->isInProgress()\n && $participant->hasName()\n && isset($data['name'])\n && $data['name'] !== $participant->getName()\n ) {\n unset($data['name']);\n }\n\n // Upsert with new data.\n $participant->fill($data);\n\n if ($enterRoom) {\n if ($enterTime === null) {\n $enterTime = now();\n }\n\n // Participant enters room for the first time\n if ($participant->enter_time === null) {\n $participant->enter_time = $enterTime;\n }\n\n // If there is an exit time and it's prior to new enter_time\n if ($participant->exit_time && $participant->exit_time->lt($enterTime)) {\n // Participant has re-joined\n $participant->exit_time = null;\n }\n }\n\n $participant->save();\n\n return $participant;\n }\n\n /**\n * Updates participant CRM data\n *\n * @param array{\n * Lead|null,\n * Account|null,\n * Opportunity|null,\n * Contact|null,\n * Stage|null,\n * string|null\n *} $records\n * @param Participant $participant participant the CRM data is associated with\n */\n public function updateParticipantCrmData(array $records, Participant $participant): void\n {\n // Extract the records.\n [$lead, , , $contact] = $records;\n\n $resolver = $this->getUpdateCrmDataResolver();\n $strategy = $resolver->resolveForParticipant($lead, $contact);\n\n if ($strategy == UpdateCrmDataByStrategy::Lead) {\n if (! $participant->hasName()) {\n $participant->name = $lead->name;\n }\n\n if (! $participant->hasEmailAddress()) {\n $participant->email = $lead->email;\n }\n\n if (! $participant->hasPhoneNumber()) {\n $participant->phone_number = $lead->phone;\n }\n\n $participant->lead_id = $lead->id;\n $participant->save();\n } elseif ($strategy == UpdateCrmDataByStrategy::Contact) {\n if (! $participant->hasName()) {\n $participant->name = $contact->name;\n }\n\n if (! $participant->hasEmailAddress()) {\n $participant->email = $contact->email;\n }\n\n if (! $participant->hasPhoneNumber()) {\n $participant->phone_number = $contact->phone;\n }\n\n $participant->contact_id = $contact->id;\n $participant->save();\n }\n }\n\n /**\n * Updates activity CRM data\n *\n * @param array{\n * Lead|null,\n * Account|null,\n * Opportunity|null,\n * Contact|null,\n * Stage|null,\n * string|null\n *} $records\n */\n public function updateActivityCrmData(array $records): void\n {\n // Extract the records.\n [$lead, $account, $opportunity, $contact, $stage] = $records;\n\n $resolver = $this->getUpdateCrmDataResolver();\n $strategy = $resolver->resolveForActivity($lead, $contact, $account);\n\n if ($strategy == UpdateCrmDataByStrategy::Lead) {\n // Also update the parent activity if required, checking we don't create a mixed lead/account record.\n if ($this->account_id === null && $this->contact_id === null && $this->lead_id === null) {\n $this->lead_id = $lead->id;\n\n if ($this->stage_id === null && $stage) {\n $this->stage_id = $stage->id;\n }\n\n $this->save();\n }\n } elseif ($strategy == UpdateCrmDataByStrategy::Contact) {\n // Also update the parent activity if required, checking we don't create a mixed lead/account record.\n $this->lead_id = null;\n if ($this->stage && $this->stage->getType() === Stage::TYPE_LEAD) {\n $this->stage_id = null;\n }\n\n // Don't trust previous matched account_id as it might have been changed in the CRM\n if ($account && $account->id !== $this->account_id) {\n $this->account_id = $account->id;\n }\n\n if ($opportunity && $this->opportunity_id !== $opportunity->id) {\n $this->opportunity_id = $opportunity->id;\n // Reset stage_id when opportunity changes to avoid stale stage from previous opportunity match\n $this->stage_id = $stage?->id;\n }\n\n if ($this->stage_id === null && $stage) {\n $this->stage_id = $stage->id;\n }\n\n if ($opportunity && $this->value !== $opportunity->value) {\n $this->value = $opportunity->value;\n }\n\n // Always set contact_id when available, regardless of account_id status\n if ($this->contact_id === null && $contact) {\n $this->contact_id = $contact->id;\n }\n\n $this->save();\n } elseif ($strategy == UpdateCrmDataByStrategy::Account && $this->account_id === null) {\n // Also update the parent activity if required, checking we don't create a mixed lead/account record.\n $this->lead_id = null;\n if ($this->stage && $this->stage->getType() === Stage::TYPE_LEAD) {\n $this->stage_id = null;\n }\n\n // Update the account and opportunity on the activity record if possible.\n $this->account_id = $account->id;\n\n if ($this->stage_id === null && $stage) {\n $this->stage_id = $stage->id;\n }\n\n if ($this->opportunity_id === null && $opportunity) {\n $this->opportunity_id = $opportunity->id;\n $this->value = $opportunity->value;\n }\n\n $this->save();\n }\n }\n\n public function getActivityProspectData(): array\n {\n return [\n 'lead' => $this->lead_id,\n 'contact' => $this->contact_id,\n 'account' => $this->account_id,\n 'opportunity' => $this->opportunity_id,\n 'stage' => $this->stage_id,\n ];\n }\n\n public function isOrganizer(User $user): bool\n {\n return $this->user_id && $this->user_id === $user->id;\n }\n\n public function isJoinable(): bool\n {\n return \\in_array($this->status, [\n self::STATUS_SCHEDULED,\n self::STATUS_PENDING,\n self::STATUS_RINGING,\n self::STATUS_IN_PROGRESS,\n ], true);\n }\n\n public function isAttemptedForBotJoin(): bool\n {\n return in_array($this->getAttribute('status'), self::MEETING_BOT_JOIN_ATTEMPTED, true);\n }\n\n /**\n * Check if the activity can be saved to CRM (manual or autolog)\n */\n public function isLoggable(): bool\n {\n if ($this->getUser()->getTeam()->hasFeature(FeatureEnum::SIDEKICK_SETTINGS)) {\n $sidekickService = app(SidekickService::class);\n\n if (! $sidekickService->isSidekickEnabledForUser($this->getUser())) {\n return false;\n }\n }\n\n // If we don't know the activity type, don't try to log.\n if ($this->playbook_category_id === null) {\n return false;\n }\n\n if ($this->user->crm_required === false) {\n return false;\n }\n\n // Don't prompt for internal meetings.\n if ($this->is_internal) {\n return false;\n }\n\n // If we don't know who we are trying to log to, don't try to log.\n if ($this->prospect === null) {\n return false;\n }\n\n $validStatus = false;\n switch ($this->type) {\n case self::TYPE_SOFTPHONE:\n case self::TYPE_SOFTPHONE_INBOUND:\n $validStatus = true;\n\n break;\n case self::TYPE_CONFERENCE:\n $validStatus = in_array($this->status, [\n self::STATUS_BUSY,\n self::STATUS_NO_ANSWER,\n self::STATUS_COMPLETED,\n self::STATUS_CANCELLED,\n ], true);\n\n break;\n case self::TYPE_SMS_INBOUND:\n case self::TYPE_SMS_OUTBOUND:\n $validStatus = in_array($this->status, [\n self::STATUS_QUEUED,\n self::STATUS_SENT,\n self::STATUS_UNDELIVERED,\n self::STATUS_DELIVERED,\n self::STATUS_RECEIVED,\n ], true);\n\n break;\n }\n\n // Depending on the activity channel, we should not try to log.\n return $validStatus;\n }\n\n public function isScheduled(): bool\n {\n return $this->status === self::STATUS_SCHEDULED;\n }\n\n public function scheduledDuration(): int\n {\n if ($this->scheduled_start_time && $this->scheduled_end_time) {\n return $this->scheduled_end_time->timestamp - $this->scheduled_start_time->timestamp;\n }\n\n return 0;\n }\n\n public function isPending(): bool\n {\n return $this->status === self::STATUS_PENDING;\n }\n\n public function isCompleted(): bool\n {\n return $this->status === self::STATUS_COMPLETED;\n }\n\n public function isRinging(): bool\n {\n return $this->status === self::STATUS_RINGING;\n }\n\n public function isInProgress(): bool\n {\n return $this->status === self::STATUS_IN_PROGRESS;\n }\n\n public function isBusy(): bool\n {\n return $this->status === self::STATUS_BUSY;\n }\n\n public function isNoAnswer(): bool\n {\n return $this->status === self::STATUS_NO_ANSWER;\n }\n\n public function isFailed(): bool\n {\n return $this->status === self::STATUS_FAILED;\n }\n\n public function isCancelled(): bool\n {\n return $this->status === self::STATUS_CANCELLED;\n }\n\n public function hasEnded(int $gracePeriodMinutes = 15): bool\n {\n if ($this->isCompleted()) {\n return true;\n }\n\n if (($this->isFailed() || $this->isCancelled()) && $this->hasScheduledEndTime()) {\n return $this->getScheduledEndTime()->addMinutes($gracePeriodMinutes)->isPast();\n }\n\n return false;\n }\n\n public function hasStarted(): bool\n {\n return $this->hasActualStartTime();\n }\n\n public function isOngoing(): bool\n {\n return $this->hasActualStartTime() && ! $this->hasActualEndTime();\n }\n\n public function isTypeSmsInbound(): bool\n {\n return $this->getType() === self::TYPE_SMS_INBOUND;\n }\n\n public function isTypeSmsOutbound(): bool\n {\n return $this->getType() === self::TYPE_SMS_OUTBOUND;\n }\n\n public function isTypeSoftPhone(): bool\n {\n return $this->getType() === self::TYPE_SOFTPHONE;\n }\n\n public function isTypeSoftphoneInbound(): bool\n {\n return $this->getType() === self::TYPE_SOFTPHONE_INBOUND;\n }\n\n public function isTypeConference(): bool\n {\n return $this->getType() === self::TYPE_CONFERENCE;\n }\n\n /**\n * Get a conference elapsed time in seconds.\n *\n * @return int seconds count\n */\n public function secondsTimeElapsed(): int\n {\n if (empty($this->actual_start_time)) {\n return 0;\n }\n\n // Get number of seconds since conference actual start time\n return (int) abs(Carbon::now()->diffInRealSeconds($this->actual_start_time));\n }\n\n /**\n * Get a conference elapsed time formatted as \"1:30:20\" if more than 1 hour or \"30:20\" otherwise.\n */\n public function formattedTimeElapsed(): string\n {\n // Get number of seconds since conference actual start time.\n $elapsedSeconds = $this->secondsTimeElapsed();\n $elapsedTime = Carbon::createFromTimestampUTC($elapsedSeconds);\n\n // Format conference start time.\n return $elapsedTime->format($elapsedSeconds < 3600 ? 'i:s' : 'G:i:s');\n }\n\n public function wasScheduled(): bool\n {\n return $this->calendarEvent !== null || in_array($this->getSource(), [self::SOURCE_OUTLOOK, self::SOURCE_GOOGLE]);\n }\n\n public function isInstant(): bool\n {\n return ! $this->wasScheduled();\n }\n\n /**\n * GETTERS AND SETTERS FOLLOW BELOW\n */\n\n public function getUuid(): string\n {\n return $this->getAttribute('id_string');\n }\n\n public function getId(): int\n {\n return $this->getAttribute('id');\n }\n\n public function getFromParticipantId(): ?int\n {\n return $this->getAttribute('from_participant_id');\n }\n\n public function getFromParticipant(): ?Participant\n {\n return $this->getAttribute('from');\n }\n\n public function getToParticipantId(): ?int\n {\n return $this->getAttribute('to_participant_id');\n }\n\n public function getToParticipant(): ?Participant\n {\n return $this->getAttribute('to');\n }\n\n public function hasScheduledStartTime(): bool\n {\n return $this->getAttribute('scheduled_start_time') !== null;\n }\n\n public function getScheduledStartTime(): ?Carbon\n {\n return $this->getAttribute('scheduled_start_time');\n }\n\n public function setScheduledStartTime(DateTimeInterface $dateTime): self\n {\n $this->setAttribute('scheduled_start_time', $dateTime);\n\n return $this;\n }\n\n public function getScheduledEndTime(): ?DateTimeInterface\n {\n return $this->getAttribute('scheduled_end_time');\n }\n\n public function hasScheduledEndTime(): bool\n {\n return $this->getAttribute('scheduled_end_time') !== null;\n }\n\n public function setScheduledEndTime(DateTimeInterface $dateTime): self\n {\n $this->setAttribute('scheduled_end_time', $dateTime);\n\n return $this;\n }\n\n public function getActualStartTime(): ?Carbon\n {\n return $this->getAttribute('actual_start_time');\n }\n\n public function hasActualStartTime(): bool\n {\n return $this->getAttribute('actual_start_time') !== null;\n }\n\n public function getActualEndTime(): ?Carbon\n {\n return $this->getAttribute('actual_end_time');\n }\n\n public function hasActualEndTime(): bool\n {\n return $this->getAttribute('actual_end_time') !== null;\n }\n\n public function getType(): ?string\n {\n return $this->getAttribute('type');\n }\n\n public function getStatus(): string\n {\n return $this->getAttribute('status');\n }\n\n public function setStatus(string $status): self\n {\n $this->setAttribute('status', $status);\n\n return $this;\n }\n\n public function setActualStartTime(DateTimeInterface $dateTime): self\n {\n $this->setAttribute('actual_start_time', $dateTime);\n\n return $this;\n }\n\n public function setActualEndTime(DateTimeInterface $dateTime, bool $shouldUpdateDuration = true): self\n {\n $this->setAttribute('actual_end_time', $dateTime);\n\n if (! $shouldUpdateDuration) {\n return $this;\n }\n\n return $this->updateDuration();\n }\n\n public function updateDuration(): self\n {\n if (! $this->hasActualStartTime() || ! $this->hasActualEndTime()) {\n return $this;\n }\n\n return $this->setDuration(\n (int) abs($this->getActualStartTime()->diffInRealSeconds($this->getActualEndTime()))\n );\n }\n\n public function setDuration(int $duration): self\n {\n $this->setAttribute('duration', $duration);\n\n return $this;\n }\n\n public function getRecordingState(): string\n {\n return $this->getAttribute('recording_state');\n }\n\n public function isRecordingState(string $recordingState): bool\n {\n return $this->getRecordingState() === $recordingState;\n }\n\n public function setRecordingState(string $recordingState): self\n {\n $this->setAttribute('recording_state', $recordingState);\n\n return $this;\n }\n\n public function hasActivityType(): bool\n {\n return $this->getAttribute('category') !== null;\n }\n\n public function getActivityType(): ?PlaybookCategory\n {\n return $this->getAttribute('category');\n }\n\n public function setActivityType(int $playbookCategoryId): self\n {\n $this->setAttribute('playbook_category_id', $playbookCategoryId);\n\n return $this;\n }\n\n public function hasStage(): bool\n {\n return $this->getAttribute('stage') !== null;\n }\n\n public function getStage(): ?Stage\n {\n return $this->getAttribute('stage');\n }\n\n public function getStageId(): ?int\n {\n return $this->getAttribute('stage_id');\n }\n\n public function setStageId(?int $stageId): void\n {\n $this->setAttribute('stage_id', $stageId);\n }\n\n public function hasOpportunity(): bool\n {\n return $this->getAttribute('opportunity') !== null;\n }\n\n public function getOpportunity(): ?Opportunity\n {\n return $this->getAttribute('opportunity');\n }\n\n public function getOpportunityId(): ?int\n {\n return $this->getAttribute('opportunity_id');\n }\n\n public function setOpportunityId(?int $opportunityId): void\n {\n $this->setAttribute('opportunity_id', $opportunityId);\n }\n\n public function hasContact(): bool\n {\n return $this->getAttribute('contact') !== null;\n }\n\n public function getContact(): ?Contact\n {\n return $this->getAttribute('contact');\n }\n\n public function getContactId(): ?int\n {\n return $this->getAttribute('contact_id');\n }\n\n public function setContactId(?int $contactId): void\n {\n $this->setAttribute('contact_id', $contactId);\n }\n\n public function hasLead(): bool\n {\n return $this->getAttribute('lead') !== null;\n }\n\n public function getLead(): ?Lead\n {\n return $this->getAttribute('lead');\n }\n\n public function getLeadId(): ?int\n {\n return $this->getAttribute('lead_id');\n }\n\n public function setLeadId(?int $leadId): void\n {\n $this->setAttribute('lead_id', $leadId);\n }\n\n public function hasAccount(): bool\n {\n return $this->getAttribute('account') !== null;\n }\n\n public function getAccount(): ?Account\n {\n return $this->getAttribute('account');\n }\n\n public function getAccountId(): ?int\n {\n return $this->getAttribute('account_id');\n }\n\n public function setAccountId(?int $accountId): void\n {\n $this->setAttribute('account_id', $accountId);\n }\n\n /**\n * This method exists to avoid confusion using ->participants() or ->participants. Use the getter instead.\n *\n * @return Collection<int, Participant>|Participant[]\n */\n public function getParticipants(): Collection\n {\n return $this->participants;\n }\n\n /**\n * @deprecated use ParticipantRepository::findParticipantRoomOwner() instead\n */\n public function findParticipantRoomOwner(): ?Participant\n {\n $roomOwnerId = $this->getUserId();\n\n return $this->getParticipants()\n ->filter(static fn (Participant $participant): bool => $participant->isSameUserId($roomOwnerId))\n ->first();\n }\n\n public function hasCrmProviderId(): bool\n {\n return $this->getAttribute('crm_provider_id') !== null;\n }\n\n public function getCrmProviderId(): ?string\n {\n return $this->getAttribute('crm_provider_id');\n }\n\n public function setCrmProviderId(?string $crmProviderId): void\n {\n $this->setAttribute('crm_provider_id', $crmProviderId);\n }\n\n public function getUserId(): ?int\n {\n return $this->getAttribute('user_id');\n }\n\n public function hasUser(): bool\n {\n return $this->user()->exists();\n }\n\n public function getUser(): User\n {\n return $this->getAttribute('user');\n }\n\n public function getCreatedAt(): Carbon\n {\n return $this->getAttribute('created_at');\n }\n\n public function isInFiniteState(): bool\n {\n return $this->isFiniteState($this->getStatus());\n }\n\n public function isFiniteState(string $status): bool\n {\n $finiteStates = self::FINITE_STATES[$this->getType()] ?? [];\n\n return in_array($status, $finiteStates, true);\n }\n\n public function getParticipant(Authenticatable $user): Participant\n {\n return $this->findParticipant($user);\n }\n\n public function findParticipant(Authenticatable $user): ?Participant\n {\n if ($user instanceof User) {\n /** @var User $user */\n return $this->participants()->where('user_id', '=', $user->getId())->first();\n }\n\n throw new LogicException(sprintf('Unsupported Authenticatable implementation %s', get_class($user)));\n }\n\n public function hasLanguageCode(): bool\n {\n return $this->getAttribute('language') !== null;\n }\n\n public function getLanguageCode(): ?string\n {\n /** @var string|null */\n return $this->getAttribute('language');\n }\n\n public function getLanguageCodeHyphenated(): string\n {\n return str_replace('_', '-', $this->getLanguageCode() ?? '');\n }\n\n public function getLanguageCodeLocale(): string\n {\n [ $language ] = explode('_', $this->getLanguageCode() ?? '');\n\n return $language;\n }\n\n public function setLanguageCode(string $value): self\n {\n return $this->setAttribute('language', $value);\n }\n\n public function hasSource(): bool\n {\n return $this->getAttribute('source') !== null;\n }\n\n public function setSource(?string $source): self\n {\n return $this->setAttribute('source', $source);\n }\n\n public function isSource(string $source): bool\n {\n return $this->getAttribute('source') === $source;\n }\n\n public function getSource(): ?string\n {\n return $this->getAttribute('source');\n }\n\n public function isSourceGong(): bool\n {\n return $this->isSource(self::SOURCE_GONG);\n }\n\n public function getExternalId(): ?string\n {\n return $this->getAttribute('external_id');\n }\n\n public function setExternalId(?string $externalId): self\n {\n return $this->setAttribute('external_id', $externalId);\n }\n\n public function hasExternalId(): bool\n {\n return $this->getAttribute('external_id') !== null;\n }\n\n public function getProvider(): string\n {\n return $this->getAttribute('provider');\n }\n\n public function hasTelephonyProviderId(): bool\n {\n return $this->getAttribute('telephony_provider_id') !== null;\n }\n\n public function getTelephonyProviderId(): ?string\n {\n return $this->getAttribute('telephony_provider_id');\n }\n\n public function setTelephonyProviderId(?string $telephonyProviderId): self\n {\n return $this->setAttribute('telephony_provider_id', $telephonyProviderId);\n }\n\n public function getLocation(): ?string\n {\n return $this->getAttribute('location');\n }\n\n public function setLocation(?string $location): self\n {\n return $this->setAttribute('location', $location);\n }\n\n public function isDeleted(): bool\n {\n return $this->getAttribute('deleted_at') !== null;\n }\n\n /**\n * Check if activity recording is on and activity status is not one of the failed statuses.\n */\n public function canReviewActivity(): bool\n {\n $failedStatuses = self::$enumFailedStatuses;\n\n return (! in_array($this->recording_state, [self::RECORDING_OFF, self::RECORDING_STOPPED], true) &&\n ! in_array($this->status, $failedStatuses, true));\n }\n\n public function hasReasonCodeBotKicked(): bool\n {\n return $this->getFlag('recording_reason_code', self::FLAG_RECORDING_REASON_MEETING_BOT_KICKED);\n }\n\n public function hasReasonCodeNotCompliant(): bool\n {\n return $this->getFlag('recording_reason_code', self::FLAG_RECORDING_REASON_CONSENT_DENIED);\n }\n\n public function hasTopicTriggers(): bool\n {\n return $this->topicTriggers()->count() !== 0;\n }\n\n public function getTopicTriggers(): Collection\n {\n return $this->topicTriggers;\n }\n\n public function getTopicTriggersSorted(): Collection\n {\n $this->loadMissing([\n 'topicTriggers.participant',\n 'topicTriggers.playbackThemeTopicTrigger',\n 'topicTriggers.playbackThemeTopicTrigger',\n 'topicTriggers.playbackThemeTopicTrigger.playbackThemeTopic',\n 'topicTriggers.playbackThemeTopicTrigger.playbackThemeTopic.playbackTheme',\n ]);\n\n return $this->topicTriggers\n ->sortBy([\n 'playbackThemeTopicTrigger.playbackThemeTopic.playbackTheme.sort',\n 'playbackThemeTopicTrigger.playbackThemeTopic.sort',\n 'playbackThemeTopicTrigger.sort',\n ]);\n }\n\n public function hasQuestions(): bool\n {\n return $this->questions()->exists();\n }\n\n public function getQuestions(): Collection\n {\n return $this->questions;\n }\n\n public function hasValue(): bool\n {\n return $this->getAttribute('value') !== null;\n }\n\n public function getValue(): ?float\n {\n return $this->getAttribute('value');\n }\n\n public function setValue(?float $value): void\n {\n $this->setAttribute('value', $value);\n }\n\n public function transitionTo(string $newState, callable $callback, ?int $timeout = null): self\n {\n $newState = $this->getWorkflowStateFor(\n $this->getType(),\n $newState\n );\n\n return $this->traitTransitionTo($newState, $callback, $timeout);\n }\n\n public function getWorkflowState(): string\n {\n return $this->getWorkflowStateFor(\n $this->getType(),\n $this->getStatus()\n );\n }\n\n public function getActivityProviderDisplayName(): string\n {\n return \\Cache::remember('activity_provider_display_name-' . $this->getProvider(), 60 * 60 * 24, function () {\n $activityProviderRegistry = app()->make(ActivityProviderRegistry::class);\n\n try {\n return $activityProviderRegistry->get($this->getProvider())->getDisplayName();\n } catch (Exception $exception) {\n return ucfirst($this->getProvider());\n }\n });\n }\n\n private function getWorkflowStateFor(string $activityChannel, string $activityStatus): string\n {\n return sprintf(\n '%s::%s',\n $activityChannel,\n $activityStatus\n );\n }\n\n public function getWorkflow(): array\n {\n $map = [\n self::TYPE_SOFTPHONE => [\n self::STATUS_SCHEDULED => [\n self::STATUS_PENDING,\n self::STATUS_IN_PROGRESS,\n self::STATUS_FAILED,\n self::STATUS_BUSY,\n ],\n self::STATUS_PENDING => [\n self::STATUS_IN_PROGRESS,\n self::STATUS_FAILED,\n self::STATUS_BUSY,\n ],\n self::STATUS_RINGING => [\n self::STATUS_CANCELLED,\n self::STATUS_FAILED,\n self::STATUS_IN_PROGRESS,\n self::STATUS_BUSY,\n ],\n self::STATUS_IN_PROGRESS => [\n self::STATUS_COMPLETED,\n ],\n ],\n self::TYPE_SOFTPHONE_INBOUND => [\n self::STATUS_RINGING => [\n self::STATUS_IN_PROGRESS,\n self::STATUS_NO_ANSWER,\n self::STATUS_CANCELLED,\n self::STATUS_FAILED,\n self::STATUS_BUSY,\n ],\n self::STATUS_IN_PROGRESS => [\n self::STATUS_COMPLETED,\n ],\n ],\n ];\n\n return collect($map)\n ->mapWithKeys(function (array $currentStates, string $activityChannel): array {\n return [\n $activityChannel => collect($currentStates)\n ->mapWithKeys(function (array $possibleStates, $currentState) use ($activityChannel): array {\n $transitionName = $this->getWorkflowStateFor($activityChannel, $currentState);\n\n return [\n $transitionName => array_map(function (string $newState) use ($activityChannel) {\n return $this->getWorkflowStateFor($activityChannel, $newState);\n }, $possibleStates),\n ];\n }),\n ];\n })\n ->reduce(static function (array $carry, Collection $item): array {\n return array_merge($carry, $item->all());\n }, []);\n }\n\n public function hasPosterPath(): bool\n {\n return $this->getAttribute('poster_path') !== null;\n }\n\n public function getPosterPath(): ?string\n {\n return $this->getAttribute('poster_path');\n }\n\n /**\n * Take into account all recording settings and determine if we need to record this activity or not.\n */\n public function shouldRecord(): bool\n {\n return $this->determineRecordingReasonCode() === null;\n }\n\n public function determineRecordingReasonCode(): ?int\n {\n // Conference specific decisions.\n if ($this->isTypeConference()) {\n // If they have manually overridden the recording setting to not record.\n if ($this->hasRecordingPreference() && $this->getRecordingPreference() === false) {\n return self::FLAG_RECORDING_REASON_PREFERENCE_OVERRIDE;\n }\n\n // If they have manually overridden the recording setting to record.\n if ($this->hasRecordingPreference() && $this->getRecordingPreference() === true) {\n return null;\n }\n\n // If their team has disabled recording meetings, don't record.\n if ($this->user->team->isConferenceRecordPreferenceDisabled()) {\n return self::FLAG_RECORDING_REASON_TEAM_AUTOMATIC_DISABLED;\n }\n\n // If the host has disabled recording meetings, don't record.\n if ($this->user->checkConferenceRecordPreference() === false) {\n return self::FLAG_RECORDING_REASON_USER_AUTOMATIC_DISABLED;\n }\n\n // If it was marked internal...\n if ($this->is_internal) {\n // and their team has disabled recording internal meetings, don't record.\n if (\n $this->user->team->isConferenceRecordPreferenceEnabled()\n && ! $this->user->team->isConferenceRecordInternalPreferenceEnabled()\n ) {\n return self::FLAG_RECORDING_REASON_TEAM_INTERNAL_DISABLED;\n }\n\n // and the host has disabled recording internal meetings, don't record.\n if ($this->user->checkConferenceRecordInternalPreference() === false) {\n return self::FLAG_RECORDING_REASON_USER_INTERNAL_DISABLED;\n }\n }\n\n // If it was not scheduled and they disabled internal meetings, we cannot determine if it was internal.\n if ($this->wasScheduled() === false && $this->user->checkConferenceRecordInternalPreference() === false) {\n return self::FLAG_RECORDING_REASON_USER_INTERNAL_DISABLED_UNSCHEDULED;\n }\n }\n\n return null;\n }\n\n public function getRecordingReasonCode(): int\n {\n return $this->getAttribute('recording_reason_code');\n }\n\n public function setRecordingReasonCode(int $recordingReasonCode): self\n {\n $this->setAttribute('recording_reason_code', $recordingReasonCode);\n\n return $this;\n }\n\n // Not used today.\n public function getRecordingReasonString(): ?string\n {\n if ($this->hasRecordingReasonCompliancePrompted()) {\n return Team::COMPLIANCE_MODE_RECORDING_PROMPT;\n }\n\n if ($this->hasRecordingReasonComplianceRestricted()) {\n return Team::COMPLIANCE_MODE_RECORDING_RESTRICT;\n }\n\n if ($this->hasRecordingReasonComplianceRestrictedToOneSideRecording()) {\n return Team::COMPLIANCE_MODE_RECORDING_RESTRICT_ONE_SIDE;\n }\n\n return null;\n }\n\n public function hasRecordingReasonComplianceRestricted(): bool\n {\n return $this->getFlag('recording_reason_code', self::FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT);\n }\n\n public function hasRecordingReasonCompliancePrompted(): bool\n {\n return $this->getFlag('recording_reason_code', self::FLAG_RECORDING_REASON_COMPLIANCE_PROMPT);\n }\n\n public function hasRecordingReasonComplianceRestrictedToOneSideRecording(): bool\n {\n return $this->getFlag('recording_reason_code', self::FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT_ONE_SIDE);\n }\n\n public function getAudioTrack(): ?Track\n {\n /** @var Track|null */\n return $this->tracks()\n ->where('type', '=', Track::TYPE_AUDIO)\n ->first();\n }\n\n public function activeParticipants(): HasMany\n {\n return $this->hasMany(Participant::class)->active();\n }\n\n public function getActiveParticipants(): Eloquent\\Collection\n {\n return $this->getAttribute('activeParticipants');\n }\n\n public function crm(): Eloquent\\Relations\\BelongsTo\n {\n return $this->belongsTo(Configuration::class, 'crm_configuration_id');\n }\n\n public function activitySummaryLogs(): HasMany\n {\n return $this->hasMany(ActivitySummaryLog::class);\n }\n\n public function getCrm(): ?Configuration\n {\n return $this->getAttribute('crm');\n }\n\n public function hasCrmConfiguration(): bool\n {\n return $this->getAttribute('crm') !== null;\n }\n\n public function isProcessed(): ?bool\n {\n return $this->getAttribute('is_processed');\n }\n\n public function hasRecordingPrompt(): bool\n {\n return $this->getAttribute('has_recording_prompt') === true;\n }\n\n public function isOnAir(): bool\n {\n return $this->getAttribute('on_air') === self::ON_AIR_READY || $this->getAttribute('on_air') === self::ON_AIR_STREAMING;\n }\n\n public function setOnAir(int $onAir): self\n {\n $this->setAttribute('on_air', $onAir);\n\n return $this;\n }\n\n public function getOnAir(): ?int\n {\n return $this->getAttribute('on_air');\n }\n\n public function setTitleFromCallData(Call $call): void\n {\n $direction = $call->isOutbound() ? 'to' : 'from';\n\n $party = $this->prospect_name\n ?? $call->getContactName()\n ?? $call->getOtherPartyPhoneNumber()\n ;\n\n $this->update(['title' => sprintf('Call %s %s', $direction, $party)]);\n }\n\n /**\n * @param array{}|array{channels:string|null, format:string|null, type:string|null, status:string|null} $audioParams\n */\n public function createAudioTrack(\n string $telephonyProviderId,\n string $recordingUrl,\n array $audioParams = []\n ): Track {\n return $this->tracks()->updateOrCreate([\n 'telephony_provider_id' => $telephonyProviderId,\n ], [\n 'type' => $audioParams['type'] ?? Track::TYPE_AUDIO,\n 'status' => $audioParams['status'] ?? Track::STATUS_PENDING,\n 'format' => $audioParams['format'] ?? Track::FORMAT_WAV,\n 'provider_content_url' => $recordingUrl,\n 'start_time' => $this->actual_start_time,\n 'end_time' => $this->actual_end_time,\n ]);\n }\n\n public function createTrack(string $telephonyProviderId, array $params): Track\n {\n return $this->tracks()->updateOrCreate(\n [\n 'telephony_provider_id' => $telephonyProviderId,\n ],\n $params\n );\n }\n\n public function createOrganiserParticipant(Call $call): Participant\n {\n $user = $this->getUser();\n\n return $this->updateOrCreateParticipant([\n 'is_ghost' => 0,\n 'name' => $user->name,\n 'email' => $user->email,\n 'phone_number' => phone_e164(null, $call->getUserPhoneNumber()),\n 'enter_time' => $this->actual_start_time,\n 'exit_time' => $this->actual_end_time,\n 'user_id' => $user->id,\n ], false);\n }\n\n public function createProspectParticipant(Call $call): Participant\n {\n // not null 'name' is mandatory here to create a separate participant with 'nameMatching'\n // in case of the same phone_number with the Organiser\n $useNameMatching = $call->getUserPhoneNumber() === $call->getOtherPartyPhoneNumber();\n $defaultName = $useNameMatching ? '' : null;\n\n return $this->updateOrCreateParticipant(data: [\n 'is_ghost' => 0,\n 'name' => $this->prospect->name ?? $defaultName,\n 'email' => $this->prospect->email ?? null,\n 'phone_number' => phone_e164(null, $call->getOtherPartyPhoneNumber()),\n 'enter_time' => $this->actual_start_time,\n 'exit_time' => $this->actual_end_time,\n 'contact_id' => $this->contact_id ?? null,\n 'lead_id' => $this->lead_id ?? null,\n ], enterRoom: false, nameMatching: $useNameMatching);\n }\n\n public function updateParticipants(Participant $organiserParticipant, Participant $prospectParticipant): void\n {\n $this->update([\n 'from_participant_id' => $this->isTypeSoftPhone() ? $organiserParticipant->id : $prospectParticipant->id,\n 'to_participant_id' => $this->isTypeSoftPhone() ? $prospectParticipant->id : $organiserParticipant->id,\n ]);\n }\n\n public function hasProspect(): bool\n {\n return $this->getProspectAttribute() !== null;\n }\n\n public function isPrivate(): bool\n {\n return $this->getAttribute('is_private');\n }\n\n /** Create a new factory instance for the model. */\n protected static function newFactory(): Factory\n {\n return ActivityFactory::new();\n }\n\n public function getUpdatedAt(): Carbon\n {\n return $this->getAttribute('updated_at');\n }\n\n public function getActivitySummaryLogs(): Eloquent\\Collection\n {\n return $this->getAttribute('activitySummaryLogs');\n }\n\n public function hasProspectActivitySummaryLog(): bool\n {\n return $this->getActivitySummaryLogs()->contains(\n 'relation_type',\n ActivitySummaryLog::RELATION_OBJECT_TYPE_PROSPECT\n );\n }\n\n public function getTeam(): Team\n {\n return $this->getUser()->getTeam();\n }\n\n private function getUpdateCrmDataResolver(): UpdateCrmDataResolverInterface\n {\n $factory = app(UpdateCrmDataResolverFactory::class);\n\n return $factory->create($this);\n }\n\n public function getMeetingTrackProviderId(string $type): string\n {\n $label = match ($type) {\n Track::TYPE_VIDEO => 'v',\n Track::TYPE_AUDIO => 'a',\n default => throw new InvalidArgumentJiminnyException('Invalid track type'),\n };\n\n $startTimestamp = $this->getScheduledStartTime()?->getTimestamp();\n $teamId = $this->getTeam()->getId();\n\n return $this->getTelephonyProviderId() . ':' . $label . ':' . $startTimestamp . ':' . $teamId;\n }\n\n /**\n * Get all consent records associated with this activity\n *\n * @return \\Illuminate\\Database\\Eloquent\\Relations\\HasMany\n */\n public function participantConsents(): HasMany\n {\n return $this->hasMany(Participant\\Consent::class);\n }\n\n public function isDiallerCall(): bool\n {\n if ($this->getProvider() === Activity::PROVIDER_UPLOADER) {\n return false;\n }\n\n if (! in_array($this->getType(), [self::TYPE_SOFTPHONE, self::TYPE_SOFTPHONE_INBOUND])) {\n return false;\n }\n\n return $this->getProvider() !== self::PROVIDER_TWILIO;\n }\n\n public function getActivityDateWithFallback(): Carbon\n {\n if ($this->getActualStartTime() !== null) {\n return $this->getActualStartTime();\n }\n\n if ($this->getScheduledStartTime() !== null) {\n return $this->getScheduledStartTime();\n }\n\n return $this->getCreatedAt();\n }\n\n public function getCrmType(): ?string\n {\n // Treat uploader activities as conferences\n if ($this->getProvider() === Activity::PROVIDER_UPLOADER) {\n return Activity::TYPE_CONFERENCE;\n }\n\n return $this->getType();\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Execute","depth":4,"bounds":{"left":0.37799203,"top":0.09896249,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Explain Plan","depth":4,"bounds":{"left":0.38663563,"top":0.09896249,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Browse Query History","depth":4,"bounds":{"left":0.39760637,"top":0.09896249,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"View Parameters","depth":4,"bounds":{"left":0.40625,"top":0.09896249,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Open Query Execution Settings…","depth":4,"bounds":{"left":0.41489363,"top":0.09896249,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"In-Editor Results","depth":4,"bounds":{"left":0.42586437,"top":0.09896249,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Tx: Auto","depth":4,"bounds":{"left":0.4368351,"top":0.09896249,"width":0.024268618,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Cancel Running Statements","depth":4,"bounds":{"left":0.46343085,"top":0.09896249,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Playground","depth":4,"bounds":{"left":0.4744016,"top":0.09896249,"width":0.029587766,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"jiminny","depth":4,"bounds":{"left":0.64261967,"top":0.09896249,"width":0.02825798,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
3375247957313029282
|
8179532969745786684
|
idle
|
accessibility
|
NULL
|
Project: faVsco.js, menu
#12121 on JY-20963-fix-im Project: faVsco.js, menu
#12121 on JY-20963-fix-import-on-deleted-entity, menu
Start Listening for PHP Debug Connections
ServiceTest
Run 'ServiceTest'
Debug 'ServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
4
388
9
103
4
Previous Highlighted Error
Next Highlighted Error
<?php
namespace Jiminny\Models;
use Carbon\Carbon;
use Database\Factories\ActivityFactory;
use DateTimeInterface;
use Exception;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Database\Eloquent;
use Illuminate\Database\Eloquent\Attributes\Scope;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Auth;
use InvalidArgumentException;
use Jiminny\Component\ElasticSearch;
use Jiminny\Component\MeetingBot;
use Jiminny\Component\Model\BitwiseFlagTrait;
use Jiminny\Component\PlaybackPage\Comments\Services\ActivityCommentService;
use Jiminny\Component\Sidekick\SidekickService;
use Jiminny\Component\Uuid\UuidAwareInterface;
use Jiminny\Component\Workflow;
use Jiminny\Contracts;
use Jiminny\Contracts\Crm\ProspectInterface;
use Jiminny\DTO\ImportCall\Call;
use Jiminny\Events\Activities\ActivityTypeUpdated;
use Jiminny\Events\Activities\ActivityUpdated;
use Jiminny\Events\Activities\ProspectUpdated;
use Jiminny\Events\Activities\StageUpdated;
use Jiminny\Events\Activities\StatusUpdated;
use Jiminny\Events\Activities\TitleUpdated;
use Jiminny\Exceptions\InvalidArgumentException as InvalidArgumentJiminnyException;
use Jiminny\Exceptions\LogicException;
use Jiminny\Exceptions\RuntimeException;
use Jiminny\Models;
use Jiminny\Models\Activity\ActivitySummaryLog;
use Jiminny\Models\Activity\ActivityUploadSetting;
use Jiminny\Models\Activity\AvailabilityNotification;
use Jiminny\Models\Activity\CoachRequest;
use Jiminny\Models\Activity\Comment;
use Jiminny\Models\Activity\Log;
use Jiminny\Models\Activity\Message;
use Jiminny\Models\Activity\Moment;
use Jiminny\Models\Activity\Note;
use Jiminny\Models\Activity\ParticipantSpeech;
use Jiminny\Models\Activity\Play;
use Jiminny\Models\Activity\Question;
use Jiminny\Models\Activity\Share;
use Jiminny\Models\Activity\Snapshot;
use Jiminny\Models\Activity\Stats;
use Jiminny\Models\Activity\SubscriptionSet;
use Jiminny\Models\Activity\TopicTrigger;
use Jiminny\Models\Activity\Transcription;
use Jiminny\Models\Calendar\CalendarEvent;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Models\Crm\FieldData;
use Jiminny\Models\ElasticSearch\ActivityElasticSearchTrait;
use Jiminny\Models\Feature\FeatureEnum;
use Jiminny\Models\Participant\Connection;
use Jiminny\Models\Playlist\Activity as PlaylistActivity;
use Jiminny\Services\Activity\ActivityProviderRegistry;
use Jiminny\Services\Activity\Import\DataResolvers\UpdateCrmDataByStrategy;
use Jiminny\Services\Activity\Import\DataResolvers\UpdateCrmDataResolverFactory;
use Jiminny\Services\Activity\Import\DataResolvers\UpdateCrmDataResolverInterface;
use Jiminny\Traits\Enums;
use Jiminny\Traits\RequiresUUID;
use Jiminny\Utils\CurrencyFormatter;
use NumberFormatter;
use function in_array;
/**
* Jiminny\Models\Activity
*
* @property null|int $auto_score filled from ES hydrator, not in DB!
* @property-read Account|null $account
* @property-read CalendarEvent|null $calendarEvent
* @property-read Contact|null $contact
* @property-read Lead|null $lead
* @property-read Opportunity|null $opportunity
* @property-read Stage|null $stage
* @property int $id
* @property mixed|null $uuid
* @property string|null $source
* @property string|null $external_id
* @property string $provider
* @property string|null $location
* @property string|null $telephony_provider_id
* @property int|null $from_participant_id
* @property int|null $to_participant_id
* @property int|null $device_id
* @property string|null $type
* @property int|null $playbook_category_id
* @property int $user_id
* @property int|null $lead_id
* @property int|null $account_id
* @property int|null $contact_id
* @property int|null $opportunity_id
* @property int|null $stage_id
* @property string|null $value
* @property int|null $crm_configuration_id
* @property string|null $crm_provider_id
* @property string|null $language
* @property int|null $transcription_id
* @property int $duration
* @property string $status
* @property int|null $on_air
* @property int|null $calendar_event_id
* @property string $recording_state
* @property bool|null $recording_preference
* @property int $recording_reason_code
* @property int $summary_reminder_sent
* @property \Illuminate\Support\Carbon|null $log_reminder_sent_at
* @property \Illuminate\Support\Carbon|null $organizer_notified_at
* @property bool|null $has_recording_prompt
* @property bool $is_internal
* @property int $is_locked
* @property int $is_recording
* @property bool|null $is_processed
* @property bool $is_private
* @property bool $is_instant_invite
* @property string|null $poster_path
* @property string|null $summary
* @property string|null $title
* @property string|null $description
* @property \Illuminate\Support\Carbon|null $scheduled_start_time
* @property \Illuminate\Support\Carbon|null $scheduled_end_time
* @property \Illuminate\Support\Carbon|null $actual_start_time
* @property \Illuminate\Support\Carbon|null $actual_end_time
* @property int|null $uploaded_by
* @property \Illuminate\Support\Carbon|null $deleted_at
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property string|null $average_score
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Participant> $activeParticipants
* @property-read int|null $active_participants_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Scorecard\ActivityScorecardRuleTrigger> $activityScorecardRuleTriggers
* @property-read int|null $activity_scorecard_rule_triggers_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Scorecard\ActivityScorecardRule> $activityScorecardRules
* @property-read int|null $activity_scorecard_rules_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, AvailabilityNotification> $availabilityNotifications
* @property-read int|null $availability_notifications_count
* @property-read \Jiminny\Models\PlaybookCategory|null $category
* @property-read \Illuminate\Database\Eloquent\Collection<int, CoachRequest> $coachRequests
* @property-read int|null $coach_requests_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\CoachingFeedback> $coachingFeedbacks
* @property-read int|null $coaching_feedbacks_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, Message> $coachingMessages
* @property-read int|null $coaching_messages_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, Comment> $comments
* @property-read int|null $comments_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, Connection> $connections
* @property-read int|null $connections_count
* @property-read Configuration|null $crm
* @property-read \Illuminate\Database\Eloquent\Collection<int, FieldData> $data
* @property-read int|null $data_count
* @property-read \Jiminny\Models\Device|null $device
* @property-read \Kalnoy\Nestedset\Collection<int, \Jiminny\Models\Playlist> $favoritePlaylists
* @property-read int|null $favorite_playlists_count
* @property-read \Jiminny\Models\Participant|null $from
* @property-read string|null $activity_title
* @property-read mixed $comment_count
* @property-read mixed $duration_for_humans
* @property-read string $duration_for_humans_short
* @property-read int $favorite_count
* @property-read mixed $favorites_count
* @property-read mixed $formatted_value
* @property-read string $id_string
* @property-read \Jiminny\Models\Participant|null $organizer
* @property-read mixed $play_count
* @property-read int|null $plays_count
* @property-read ?ProspectInterface $prospect
* @property-read string|null $prospect_name
* @property-read mixed $prospect_type
* @property-read mixed $share_count
* @property-read int|null $shares_count
* @property-read int|null $tracks_with_telephony_count
* @property-read int|null $visible_comments_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\CoachingFeedback> $latestCoachingFeedbacks
* @property-read int|null $latest_coaching_feedbacks_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, Log> $logs
* @property-read int|null $logs_count
* @property-read \Jiminny\Models\Track|null $masterTrack
* @property-read \Illuminate\Database\Eloquent\Collection<int, Message> $messages
* @property-read int|null $messages_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, Moment> $moments
* @property-read int|null $moments_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, Note> $notes
* @property-read int|null $notes_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Participant\Share> $participantShares
* @property-read int|null $participant_shares_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, ParticipantSpeech> $participantSpeeches
* @property-read int|null $participant_speeches_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Participant\ParticipantStats> $participantStats
* @property-read int|null $participant_stats_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Participant> $participants
* @property-read int|null $participants_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, PlaylistActivity> $playlistActivities
* @property-read int|null $playlist_activities_count
* @property-read \Kalnoy\Nestedset\Collection<int, \Jiminny\Models\Playlist> $playlists
* @property-read int|null $playlists_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, Play> $plays
* @property-read \Illuminate\Database\Eloquent\Collection<int, Question> $questions
* @property-read int|null $questions_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Session> $sessions
* @property-read int|null $sessions_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, Share> $shares
* @property-read \Illuminate\Database\Eloquent\Collection<int, Snapshot> $snapshots
* @property-read int|null $snapshots_count
* @property-read Stats|null $stats
* @property-read \Jiminny\Models\Participant|null $to
* @property-read \Illuminate\Database\Eloquent\Collection<int, TopicTrigger> $topicTriggers
* @property-read int|null $topic_triggers_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Track> $tracks
* @property-read int|null $tracks_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Track> $tracksWithTelephony
* @property-read Transcription|null $transcription
* @property-read \Jiminny\Models\User $user
* @property-read \Illuminate\Database\Eloquent\Collection<int, Comment> $visibleComments
*
* @method static \Illuminate\Database\Eloquent\Collection<int, static> all($columns = ['*'])
* @method static \Jiminny\Component\Eloquent\Builder|Activity chunkByIdDesc($count, callable $callback, $column = null, $alias = null)
* @method static \Database\Factories\ActivityFactory factory(...$parameters)
* @method static \Illuminate\Database\Eloquent\Collection<int, static> get($columns = ['*'])
* @method static \Jiminny\Component\Eloquent\Builder|Activity heldBetween(\Carbon\Carbon $start, \Carbon\Carbon $end)
* @method static \Jiminny\Component\Eloquent\Builder|Activity idOrUuId($idOrUuid, bool $first = true)
* @method static \Jiminny\Component\Eloquent\Builder|Activity newModelQuery()
* @method static \Jiminny\Component\Eloquent\Builder|Activity newQuery()
* @method static Builder|Activity onlyTrashed()
* @method static \Jiminny\Component\Eloquent\Builder|Activity query()
* @method static \Jiminny\Component\Eloquent\Builder|Activity scheduledBetween(\Carbon\Carbon $start, \Carbon\Carbon $end)
* @method static \Jiminny\Component\Eloquent\Builder|Activity inOpenDeals()
* @method static \Jiminny\Component\Eloquent\Builder|Activity notInOpenDeals()
* @method static \Jiminny\Component\Eloquent\Builder|Activity forTeam(int $teamId)
* @method static \Jiminny\Component\Eloquent\Builder|Activity search(callable $searchQuery, $key = null, $sortByResults = true)
* @method static \Jiminny\Component\Eloquent\Builder|Activity uuid(string $uuid, bool $first = true)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereAccountId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereActualEndTime($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereActualStartTime($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereAverageScore($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereCalendarEventId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereContactId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereCreatedAt($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereCrmConfigurationId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereCrmProviderId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereDeletedAt($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereDescription($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereDeviceId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereDuration($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereFromParticipantId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereHasRecordingPrompt($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereIsInstantInvite($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereIsInternal($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereIsLocked($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereIsPrivate($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereIsProcessed($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereIsRecording($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereLanguage($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereLeadId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereLocation($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereLogReminderSentAt($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereOnAir($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereOpportunityId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereOrganizerNotifiedAt($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity wherePlaybookCategoryId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity wherePosterPath($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereProvider($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereRecordingPreference($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereRecordingReasonCode($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereRecordingState($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereScheduledEndTime($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereScheduledStartTime($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereSource($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereExternalId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereStageId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereStatus($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereSummary($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereSummaryReminderSent($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereTelephonyProviderId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereTitle($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereToParticipantId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereTranscriptionId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereType($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereUpdatedAt($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereUploadedBy($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereUserId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereUuid($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereValue($value)
* @method static Builder|Activity withTrashed()
* @method static Builder|Activity withoutTrashed()
*
* @mixin \Eloquent
*/
class Activity extends Model implements
ElasticSearch\Contract\Searchable,
Workflow\Workflow\WorkflowAwareInterface,
Models\Contracts\ActivityContract,
Contracts\Model\ActivityInterface,
UuidAwareInterface
{
use HasFactory;
use Enums;
use SoftDeletes;
use RequiresUUID;
use BitwiseFlagTrait;
use ElasticSearch\Model\Searchable;
use ActivityElasticSearchTrait;
use Workflow\Workflow\WorkflowAware {
transitionTo as traitTransitionTo;
}
public const int FLAG_RECORDING_REASON_DEFAULT = 0;
// Recording Prompted but never started
public const int FLAG_RECORDING_REASON_COMPLIANCE_PROMPT = 1;
public const int FLAG_RECORDING_REASON_COMPLIANCE_RESUMED = 2;
public const int FLAG_RECORDING_REASON_NO_AUDIO = 3;
// Recording Disabled by Organization
public const int FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT = 4;
// Recording was restricted to one-side recordings only
public const int FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT_ONE_SIDE = 8;
// Recording was not started because it was internal and team setting disabled that.
public const int FLAG_RECORDING_REASON_TEAM_INTERNAL_DISABLED = 16;
// Recording was not started because it was internal and user setting disabled that.
public const int FLAG_RECORDING_REASON_USER_INTERNAL_DISABLED = 32;
// Recording was not started because user setting disabled automatic recording.
public const int FLAG_RECORDING_REASON_USER_AUTOMATIC_DISABLED = 64;
// Recording was not started because team setting disabled automatic recording.
public const int FLAG_RECORDING_REASON_TEAM_AUTOMATIC_DISABLED = 128;
// Recording was not started because user has overriden default.
public const int FLAG_RECORDING_REASON_PREFERENCE_OVERRIDE = 256;
// Recording was not started because they don't want internal, and this meeting was not scheduled/imported in time.
public const int FLAG_RECORDING_REASON_USER_INTERNAL_DISABLED_UNSCHEDULED = 512;
// Recording was not started because their team setting does excludes the meeting type.
public const int FLAG_RECORDING_REASON_UNSUPPORTED_TYPE = 1024;
// Recording was not started because the external provider disabled it (or recording is missing etc).
public const int FLAG_RECORDING_REASON_EXTERNALLY_DISABLED = 2048;
// Recording was stopped externally ("exit-meeting" Pusher event)
public const int FLAG_RECORDING_REASON_STOPPED_EXTERNALLY = 384;
// Recording couldn't be started due to Zoom hosting conflict error
public const int FLAG_RECORDING_REASON_HOSTING_CONFLICT = 448;
// meeting.failed event with reason code BOT_DENIED_FROM_LOBBY
public const int FLAG_RECORDING_REASON_MEETING_BOT_DENIED_FROM_LOBBY = 4096;
// meeting.failed event with reason code LOBBY_TIMEOUT
public const int FLAG_RECORDING_REASON_MEETING_BOT_LOBBY_TIMEOUT = 8192;
// meeting.failed event with reason code BOT_KICKED
public const int FLAG_RECORDING_REASON_MEETING_BOT_KICKED = 16384;
// meeting.failed event with reason code UNKNOWN
public const int FLAG_RECORDING_REASON_MEETING_BOT_UNKNOWN = 32768;
public const int FLAG_RECORDING_REASON_CONSENT_DENIED = 65536;
// Invalid meeting (e.g. URL is invalid, or the meeting is not found)
public const int FLAG_RECORDING_REASON_MEETING_BOT_INVALID = 131072;
// The host stopped the recording.
public const int FLAG_RECORDING_REASON_USER_STOPPED = 262144;
// Recording was not started because an alternative vendor disabled it (or overrode it).
public const int FLAG_RECORDING_REASON_VENDOR_OVERRIDE = 1048576;
// Login required meeting.failed code
public const int FLAG_RECORDING_REASON_LOGIN_REQUIRED = 524288;
// Password for meeting was not provided - meeting.failed code
public const int FLAG_RECORDING_REASON_MEETING_PASSWORD_NOT_PROVIDED = 2097152;
// meeting.failed - when the meeting is locked
public const int FLAG_RECORDING_REASON_MEETING_IS_LOCKED = 4194304;
// max recording duration reached
public const int FLAG_RECORDING_REASON_MAX_DURATION_REACHED = 8388608;
// recording size is too small
public const int FLAG_RECORDING_REASON_EMPTY_RECORDING = 16777216;
// meeting.failed - when bot is redirected to sign in page multiple times
public const int FLAG_RECORDING_REASON_MAX_RESTART_COUNT_IS_REACHED = 33554432;
// meeting.failed event with reason code CONNECTION_LOST
public const int FLAG_RECORDING_REASON_MEETING_BOT_CONNECTION_LOST = 67108864;
// recording is corrupted.
public const int FLAG_RECORDING_REASON_MEDIA_FILE_UNSUPPORTED_MIME_TYPE = 134217728;
// meeting ended in lobby
public const int FLAG_RECORDING_REASON_MEETING_ENDED_IN_LOBBY = 268435456;
// meeting not started
public const int FLAG_RECORDING_REASON_REASON_MEETING_NOT_STARTED = 536870912;
// unfinished zoom custom disclaimer
public const int FLAG_RECORDING_REASON_FEATURE_RULE_NOT_FOUND_ERROR = 1073741824;
// recording download failed - server error
public const int FLAG_RECORDING_REASON_SERVER_ERROR = 2147483648;
// recording download failed - client code 404
public const int FLAG_RECORDING_REASON_NOT_FOUND = 2147483649;
// recording download failed - client code 401, 403
public const int FLAG_RECORDING_REASON_ACCESS_DENIED = 2147483650;
// recording download failed - client code 429
public const int FLAG_RECORDING_REASON_TOO_MANY_REQUESTS = 2147483651;
// recording download failed - unknown client error
public const int FLAG_RECORDING_REASON_CLIENT_ERROR = 2147483652;
// recording download failed - unknown error
public const int FLAG_RECORDING_REASON_UNKNOWN_ERROR = 2147483653;
// It has been setup ahead of time through calendar
public const string STATUS_SCHEDULED = 'scheduled';
// It is awaiting audio.
public const string STATUS_PENDING = 'pending';
// Participant(s) dialed in, awaiting organizer.
public const string STATUS_RINGING = 'ringing';
// Call is in progress.
public const string STATUS_IN_PROGRESS = 'in-progress';
// It has ended.
public const string STATUS_COMPLETED = 'completed';
// Cancelled prior to starting.
public const string STATUS_CANCELLED = 'canceled';
public const string STATUS_DUPLICATED = 'duplicated'; // duplicated conference
public const string STATUS_STARTING_SOON = 'starting-soon';
public const string STATUS_BOT_CREATE_SENT = 'bot-create-sent';
public const string STATUS_BOT_INSTANCE_WORKER_ASSIGNED = 'worker-assigned';
public const string STATUS_BOT_INSTANCE_STARTED = 'bot-started';
// When bot instance is waiting in lobby
public const string STATUS_BOT_INSTANCE_WAITING_LOBBY = 'bot-waiting';
public const string STATUS_BUSY = 'busy';
public const string STATUS_NO_ANSWER = 'no-answer';
public const string STATUS_FAILED = 'failed'; // Used by SMS too
// SMS related
public const string STATUS_ACCEPTED = 'accepted';
public const string STATUS_QUEUED = 'queued';
public const string STATUS_SENDING = 'sending';
public const string STATUS_SENT = 'sent';
public const string STATUS_DELIVERED = 'delivered';
public const string STATUS_UNDELIVERED = 'undelivered';
public const string STATUS_RECEIVING = 'receiving';
public const string STATUS_RECEIVED = 'received';
public const string STATUS_RESENT = 'resent';
public const array SMS_STATUSES = [
Activity::STATUS_RECEIVED,
Activity::STATUS_SENT,
Activity::STATUS_DELIVERED,
];
public const array SOFT_PHONE_CONFERENCE_STATUSES = [
Activity::STATUS_IN_PROGRESS,
Activity::STATUS_COMPLETED,
];
// @todo refactor prefix from `TYPE_` to `CHANNEL_`
public const string TYPE_SOFTPHONE = 'softphone';
public const string TYPE_SOFTPHONE_INBOUND = 'softphone-inbound';
public const string TYPE_CONFERENCE = 'conference';
public const string TYPE_SMS_INBOUND = 'sms-inbound';
public const string TYPE_SMS_OUTBOUND = 'sms-outbound';
public const string TYPE_EMAIL_INBOUND = 'email-inbound';
public const string TYPE_EMAIL_OUTBOUND = 'email-outbound';
public const array CHANNELS = [
self::TYPE_SOFTPHONE,
self::TYPE_SOFTPHONE_INBOUND,
self::TYPE_CONFERENCE,
self::TYPE_SMS_INBOUND,
self::TYPE_SMS_OUTBOUND,
self::TYPE_EMAIL_INBOUND,
self::TYPE_EMAIL_OUTBOUND,
];
public const array PLAYABLE_CHANNELS = [
self::TYPE_SOFTPHONE,
self::TYPE_SOFTPHONE_INBOUND,
self::TYPE_CONFERENCE,
];
// Recording States
public const string RECORDING_OFF = 'off'; // Default state
public const string RECORDING_IN_PROGRESS = 'in-progress';
public const string RECORDING_PAUSED = 'paused';
public const string RECORDING_STOPPED = 'stopped'; // To never be resumed.
public const string RECORDING_RECORDED = 'recorded'; // At least some portion of it was recorded.
public const string RECORDING_FAILED = 'failed'; // Recording was attempted but failed for some reason.
// Live Stream States
public const int ON_AIR_DEFAULT = 0;
public const int ON_AIR_READY = 1;
public const int ON_AIR_PREPARING = 2;
public const int ON_AIR_STREAMING = 3;
public const int ON_AIR_FINISHED = 4;
public const int ON_AIR_NOT_STREAMED = 5;
public const int ON_AIR_ERROR = -1;
public const string SOURCE_GONG = 'gong';
public const string SOURCE_CHORUS = 'chorus';
public const string SOURCE_OUTLOOK = 'outlook';
public const string SOURCE_GOOGLE = 'google';
// Activity Providers
public const string PROVIDER_TWILIO = 'twilio'; // XXX: This is run via the Jiminny Provider.
public const string PROVIDER_OUTREACH = 'outreach';
public const string PROVIDER_ZOOM_BOT = 'zoom-bot';
public const string PROVIDER_SALESLOFT = 'salesloft';
public const string PROVIDER_GOOGLE = 'google';
public const string PROVIDER_AIRCALL = 'aircall';
public const string PROVIDER_JUSTCALL = 'justcall';
public const string PROVIDER_GOOGLE_MEET = 'google-meet';
public const string PROVIDER_GONG = 'gong';
public const string PROVIDER_HUBSPOT = 'hubspot';
public const string PROVIDER_CLOSE = 'close';
public const string PROVIDER_TEAMS = 'ms-teams';
public const string PROVIDER_SALESFORCE = 'salesforce';
public const string PROVIDER_GROOVE = 'groove';
public const string PROVIDER_XANT = 'xant';
public const string PROVIDER_OFFICE = 'office';
public const string PROVIDER_NATTERBOX = 'natterbox';
public const string PROVIDER_RINGCENTRAL = 'ringcentral';
public const string PROVIDER_RINGCENTRAL_VIDEO = 'ringcentral-video';
public const string PROVIDER_GOTOMEETING = 'go-to-meeting';
public const string PROVIDER_DEMODESK = 'demo-desk';
public const string PROVIDER_DIALPAD = 'dialpad';
public const string PROVIDER_ZOOM_PHONE = 'zoom-phone';
public const string PROVIDER_CLOUDCALL = 'cloudcall';
public const string PROVIDER_CLOUDCALL_US = 'cloudcall-us';
public const string PROVIDER_EIGHT_BY_EIGHT = 'eight-by-eight'; // "8x8" UK
public const string PROVIDER_EIGHT_BY_EIGHT_CA = 'eight-by-eight-ca'; // "8x8" Canada
public const string PROVIDER_EIGHT_BY_EIGHT_AP = 'eight-by-eight-ap'; // "8x8" Australia
public const string PROVIDER_EIGHT_BY_EIGHT_US_EAST = 'eight-by-eight-use'; // "8x8" US East
public const string PROVIDER_EIGHT_BY_EIGHT_US_WEST = 'eight-by-eight-usw'; // "8x8" US West
public const string PROVIDER_CONNECT_AND_SELL = 'connect-and-sell';
public const string PROVIDER_CLOUD_TALK = 'cloud-talk';
public const string PROVIDER_AMAZON_CONNECT = 'amazon-connect';
public const string PROVIDER_VONAGE = 'vonage';
public const string PROVIDER_MIGRATOR = 'migrator';
public const string PROVIDER_UPLOADER = 'uploader';
public const string PROVIDER_TALKDESK = 'talkdesk';
public const string PROVIDER_TWILIO_FLEX = 'twilio-flex';
public const string PROVIDER_TWILIO_FLEX_DIRECT = 'twilio-flex-direct';
public const string PROVIDER_TWILIO_VIDEO = 'twilio-video';
public const string PROVIDER_AVAYA = 'avaya';
public const string PROVIDER_TELUS = 'telus';
public const string PROVIDER_FIVE_NINE = 'five-nine';
public const string PROVIDER_APOLLO = 'apollo';
public const string PROVIDER_ORUM = 'orum';
public const string PROVIDER_BLOOBIRDS = 'bloobirds';
/**
* @const API_PROVIDERS
* A list of integrations that import calls via API instead of webhooks
*/
public const array API_PROVIDERS = [
self::PROVIDER_OUTREACH,
self::PROVIDER_SALESLOFT,
self::PROVIDER_HUBSPOT,
self::PROVIDER_GROOVE,
self::PROVIDER_XANT,
self::PROVIDER_NATTERBOX,
self::PROVIDER_CLOUDCALL,
self::PROVIDER_CLOUDCALL_US,
self::PROVIDER_EIGHT_BY_EIGHT,
self::PROVIDER_EIGHT_BY_EIGHT_CA,
self::PROVIDER_EIGHT_BY_EIGHT_AP,
self::PROVIDER_EIGHT_BY_EIGHT_US_EAST,
self::PROVIDER_EIGHT_BY_EIGHT_US_WEST,
self::PROVIDER_CONNECT_AND_SELL,
self::PROVIDER_CLOUD_TALK,
self::PROVIDER_AMAZON_CONNECT,
self::PROVIDER_VONAGE,
self::PROVIDER_TALKDESK,
self::PROVIDER_TWILIO_VIDEO,
self::PROVIDER_TWILIO_FLEX,
self::PROVIDER_TWILIO_FLEX_DIRECT,
self::PROVIDER_FIVE_NINE,
self::PROVIDER_APOLLO,
self::PROVIDER_ORUM,
self::PROVIDER_BLOOBIRDS,
self::PROVIDER_RINGCENTRAL,
self::PROVIDER_AVAYA,
self::PROVIDER_TELUS,
];
public const array FINITE_STATES = [
self::TYPE_SOFTPHONE => [
self::STATUS_COMPLETED,
self::STATUS_FAILED,
self::STATUS_NO_ANSWER,
self::STATUS_BUSY,
],
self::TYPE_SOFTPHONE_INBOUND => [
self::STATUS_COMPLETED,
self::STATUS_FAILED,
self::STATUS_NO_ANSWER,
self::STATUS_BUSY,
],
self::TYPE_CONFERENCE => self::FINITE_STATES_CONFERENCE,
];
public const array FINITE_STATES_CONFERENCE = [
self::STATUS_COMPLETED,
self::STATUS_FAILED,
self::STATUS_CANCELLED,
];
public const array MEETING_BOT_JOIN_ATTEMPTED = [
self::STATUS_BOT_INSTANCE_WAITING_LOBBY,
self::STATUS_BOT_INSTANCE_STARTED,
];
public static array $enumStatuses = [
self::STATUS_SCHEDULED,
self::STATUS_PENDING,
self::STATUS_RINGING,
self::STATUS_IN_PROGRESS,
self::STATUS_COMPLETED,
self::STATUS_CANCELLED,
self::STATUS_BUSY,
self::STATUS_NO_ANSWER,
self::STATUS_FAILED,
self::STATUS_ACCEPTED,
self::STATUS_QUEUED,
self::STATUS_SENDING,
self::STATUS_SENT,
self::STATUS_RESENT,
self::STATUS_DELIVERED,
self::STATUS_UNDELIVERED,
self::STATUS_RECEIVING,
self::STATUS_RECEIVED,
self::STATUS_BOT_INSTANCE_WAITING_LOBBY,
self::STATUS_STARTING_SOON,
self::STATUS_BOT_INSTANCE_WORKER_ASSIGNED,
self::STATUS_BOT_INSTANCE_STARTED,
self::STATUS_DUPLICATED,
];
public static array $enumProviders = [
self::PROVIDER_TWILIO,
self::PROVIDER_OUTREACH,
self::PROVIDER_ZOOM_BOT,
self::PROVIDER_SALESLOFT,
self::PROVIDER_AIRCALL,
self::PROVIDER_JUSTCALL,
self::PROVIDER_GOOGLE_MEET,
self::PROVIDER_GONG,
self::PROVIDER_HUBSPOT,
self::PROVIDER_CLOSE,
self::PROVIDER_TEAMS,
self::PROVIDER_SALESFORCE,
self::PROVIDER_GROOVE,
self::PROVIDER_XANT,
self::PROVIDER_GOOGLE,
self::PROVIDER_OFFICE,
self::PROVIDER_NATTERBOX,
self::PROVIDER_RINGCENTRAL,
self::PROVIDER_RINGCENTRAL_VIDEO,
self::PROVIDER_GOTOMEETING,
self::PROVIDER_DEMODESK,
self::PROVIDER_DIALPAD,
self::PROVIDER_ZOOM_PHONE,
self::PROVIDER_CLOUDCALL,
self::PROVIDER_CLOUDCALL_US,
self::PROVIDER_EIGHT_BY_EIGHT,
self::PROVIDER_EIGHT_BY_EIGHT_CA,
self::PROVIDER_EIGHT_BY_EIGHT_AP,
self::PROVIDER_EIGHT_BY_EIGHT_US_EAST,
self::PROVIDER_EIGHT_BY_EIGHT_US_WEST,
self::PROVIDER_CONNECT_AND_SELL,
self::PROVIDER_CLOUD_TALK,
self::PROVIDER_AMAZON_CONNECT,
self::PROVIDER_VONAGE,
self::PROVIDER_TALKDESK,
self::PROVIDER_TWILIO_FLEX,
self::PROVIDER_TWILIO_FLEX_DIRECT,
self::PROVIDER_TWILIO_VIDEO,
self::PROVIDER_AVAYA,
self::PROVIDER_TELUS,
self::PROVIDER_FIVE_NINE,
self::PROVIDER_APOLLO,
self::PROVIDER_ORUM,
self::PROVIDER_BLOOBIRDS,
];
public static $enumRecordingStates = [
self::RECORDING_OFF, // Default state
self::RECORDING_IN_PROGRESS,
self::RECORDING_PAUSED,
self::RECORDING_STOPPED,
self::RECORDING_RECORDED,
self::RECORDING_FAILED,
];
// @Important:
// This collection is not used anywhere, and is fully duplicated by the Channels const.
// Validate if it is referred somehow via the enum trait, and if not, remove it entirely.
// An even better strategy will be to move all those constants to a dedicated class
protected array $enumTypes = [
self::TYPE_SOFTPHONE,
self::TYPE_SOFTPHONE_INBOUND,
self::TYPE_CONFERENCE,
self::TYPE_SMS_INBOUND,
self::TYPE_SMS_OUTBOUND,
self::TYPE_EMAIL_INBOUND,
self::TYPE_EMAIL_OUTBOUND,
];
protected static $enumFailedStatuses = [
self::STATUS_NO_ANSWER,
self::STATUS_FAILED,
self::STATUS_BUSY,
self::STATUS_CANCELLED,
];
protected $table = 'activities';
protected $fillable = [
// Type of activity.
'type', // @todo refactor to `channel`
// The activity type.
'playbook_category_id',
// User who hosts the activity.
'user_id',
// Related Lead record (if applicable)
'lead_id',
// Related Account record (if applicable)
'account_id',
// Related Contact record (if applicable)
'contact_id',
// Related Opportunity record (if applicable)
'opportunity_id',
// Stage of activity.
'stage_id',
// Value of opportunity.
'value',
// If the activity relates to a CRM task.
'crm_provider_id',
// If the activity was created through an external device.
'device_id',
// the activity's language code
'language',
// transcription id
'transcription_id',
// Duration of the call, with microseconds precision.
'duration',
// One of enumStatuses above.
'status',
// Have we reminded them to log the call?
'log_reminder_sent_at',
// If activity is private or inter-org, flagged here.
'is_internal',
// Managers and above can mark a call as private, to exclude it from other team members
'is_private',
'is_processed',
// Boolean for this activity being instant invite handled.
'is_instant_invite',
// If activity is in recording state, flagged here.
'recording_state',
// If activity recording is overidden from default.
'recording_preference',
// if recording did (not) happen, why that is
'recording_reason_code',
// Average score, updated during
'average_score',
// Summary that the organizer has taken after the call.
'summary',
// Subject of the activity, usually taken from calendar event.
'title',
// Description of the activity, usually taken from calendar event.
'description',
// Start time, usually taken from calendar event.
'scheduled_start_time',
// End time, usually taken from calendar event.
'scheduled_end_time',
// When the call actually started.
'actual_start_time',
// When the call actually ended.
'actual_end_time',
// SMS: Message reference
'telephony_provider_id',
// SMS: Participant who sent message
'from_participant_id',
// SMS: Participant who should receive the message
'to_participant_id',
// When an external guest joins an organizers meeting room and the organizer is not present,
// send them an SMS notification that someone has joined.
'organizer_notified_at',
// where was the activity imported from
'source',
// The id in the source system (e.g. the bot id in Recall.ai)
'external_id',
// The provider, by default it is twilio.
'provider',
// Meeting location url
'location',
// The snapshot for displaying a poster image.
'poster_path',
'crm_configuration_id',
// If there is an automated message that the conversation is being recorded
'has_recording_prompt',
// If the activity is being live-streamed
'on_air',
'calendar_event_id',
];
protected $appends = [
'id_string',
'organizer',
];
protected $hidden = [
'uuid',
];
protected $visible = [
'id_string',
'type',
'duration',
'average_score',
'status',
'log_reminder_sent_at',
'title',
'description',
'is_internal',
'scheduled_start_time',
'scheduled_end_time',
'actual_start_time',
'actual_end_time',
'user',
'category',
'account',
'contact',
'opportunity',
'lead',
'stage',
'stats',
'participants',
'playlists',
'tracks',
'comments',
'plays',
'coachingFeedbacks',
'shares',
'favorites',
'language',
'transcription',
'is_private',
'is_instant_invite',
'on_air',
'calendar_event_id',
];
protected function casts(): array
{
return [
'scheduled_start_time' => 'datetime',
'scheduled_end_time' => 'datetime',
'actual_start_time' => 'datetime',
'actual_end_time' => 'datetime',
'organizer_notified_at' => 'datetime',
'log_reminder_sent_at' => 'datetime',
'is_internal' => 'boolean',
'duration' => 'integer',
'average_score' => 'decimal:2',
'is_private' => 'boolean',
'is_processed' => 'boolean',
'is_instant_invite' => 'boolean',
'value' => 'decimal:2',
'recording_preference' => 'boolean',
'recording_reason_code' => 'integer',
'has_recording_prompt' => 'boolean',
'on_air' => 'integer',
];
}
protected static function boot()
{
parent::boot();
static::updated(static function (Activity $activity) {
// If activity is about to start (pending, ringing, in-progress) or event is scheduled in less than 1 week
if (in_array($activity->status, [Activity::STATUS_PENDING, Activity::STATUS_RINGING, Activity::STATUS_IN_PROGRESS], true) ||
($activity->scheduled_start_time && (int) $activity->scheduled_start_time->diffInWeeks(new Carbon(), true) < 1)) {
if ($activity->isDirty('status')) {
event(new StatusUpdated($activity));
}
if ($activity->isDirty('stage_id')) {
event(new StageUpdated($activity));
}
if ($activity->isDirty(['lead_id', 'account_id', 'contact_id'])) {
event(new ProspectUpdated($activity));
}
if ($activity->isDirty('opportunity_id')) {
event(new ActivityUpdated($activity, 'activity.opportunity-updated', Auth::user()));
}
if ($activity->isDirty('title')) {
event(new TitleUpdated($activity));
}
}
if ($activity->isDirty('playbook_category_id')) {
event(new ActivityTypeUpdated($activity));
}
});
static::deleted(static function (Activity $activity) {
// Hard delete associated playlistActivities
$activity->playlistActivities()->delete();
});
}
public function getOrganizerAttribute(): ?Participant
{
$participant = $this->participants()->where('user_id', $this->user_id)->first();
if (! $participant instanceof Participant && $participant !== null) {
throw new RuntimeException(sprintf('$participant must be an instance of "%s" or null', Participant::class));
}
return $participant;
}
public function getFormattedValueAttribute()
{
$currencyCode = 'USD';
if ($this->opportunity) {
$currencyCode = $this->opportunity->getCurrencyCode();
}
$formatter = new CurrencyFormatter();
$formatter->setTextAttribute(NumberFormatter::CURRENCY_CODE, $currencyCode);
$formatter->setAttribute(NumberFormatter::MAX_FRACTION_DIGITS, 0);
return $formatter->format($this->value, $currencyCode);
}
public function getProspectNameAttribute(): ?string
{
$prospectName = null;
if ($this->lead_id) {
$prospectName = $this->lead->name;
} elseif ($this->contact_id) {
$prospectName = $this->contact->name;
} elseif ($this->account_id) {
$prospectName = $this->account->name;
}
return $prospectName;
}
public function getProspectName(): ?string
{
/** @var string|null */
return $this->getAttribute('prospect_name');
}
/**
* Get activity title depending on prospect or title
*/
public function getActivityTitleAttribute(): ?string
{
$activityTitle = null;
if ($this->prospect && $this->prospect->getName()) {
if ($this->account_id) {
$activityTitle = $this->account->name;
} elseif ($this->lead_id) {
$activityTitle = $this->lead->company;
} elseif ($this->contact_id) {
$activityTitle = $this->contact->account ? $this->contact->account->name : $this->contact->name;
}
} elseif ($this->title) {
$activityTitle = $this->title;
}
return $activityTitle;
}
public function wasRecentlyCreated(): bool
{
return $this->wasRecentlyCreated;
}
public function getProspectTypeAttribute()
{
$prospectType = null;
if ($this->lead_id) {
$prospectType = 'Lead';
} elseif ($this->contact_id) {
$prospectType = 'Contact';
} elseif ($this->account_id) {
$prospectType = 'Account';
}
return $prospectType;
}
/**
* Return the best match for prospect. Results are in the following order of priority:
* 1. Lead
* 2. Contact
* 3. Account
* 4. NULL
*/
public function getProspectAttribute(): ?ProspectInterface
{
if ($this->hasLead()) {
return $this->getLead();
}
if ($this->hasContact()) {
return $this->getContact();
}
if ($this->hasAccount()) {
return $this->getAccount();
}
return null;
}
public function getTitleAttribute($value): ?string
{
return \getActivityTitleAttribute(
$this->user->name,
$this->getType(),
$value,
$this->prospect->name ?? null,
$this->from->national_phone_number ?? null
);
}
public function getTitle(): ?string
{
return $this->getAttribute('title');
}
public function getSummary(): ?string
{
return $this->getAttribute('summary');
}
public function isInternal(): bool
{
return $this->getAttribute('is_internal');
}
public function getIsPrivate(): bool
{
return $this->getAttribute('is_private');
}
public function getDescription(): ?string
{
return $this->getAttribute('description');
}
public function hasTitle(): bool
{
return $this->getOriginal('title') !== null;
}
public function getPlayCountAttribute()
{
return $this->getPlaysCountAttribute();
}
public function getPlaysCountAttribute()
{
if (! isset($this->attributes['plays_count'])) {
$this->loadCount('plays');
}
return $this->attributes['plays_count'];
}
public function getCommentCountAttribute()
{
return $this->getCommentsCountAttribute();
}
public function getCommentsCountAttribute()
{
if (! isset($this->attributes['comments_count'])) {
$this->loadCount('comments');
}
return $this->attributes['comments_count'];
}
public function getVisibleCommentsCountAttribute()
{
if (! isset($this->attributes['visible_comments_count'])) {
$activityCommentsService = app(ActivityCommentService::class);
$user = Auth::user() instanceof User ? Auth::user() : null;
$this->attributes['visible_comments_count'] = $activityCommentsService
->getVisibleCommentsCount($this, $user);
}
return $this->attributes['visible_comments_count'];
}
public function getShareCountAttribute()
{
return $this->getSharesCountAttribute();
}
public function getSharesCountAttribute()
{
if (! isset($this->attributes['shares_count'])) {
$this->loadCount('shares');
}
return $this->attributes['shares_count'];
}
/**
* Get the count of favorites playlists this activity appears in
*/
public function getFavoriteCountAttribute(): int
{
return $this->getFavoritesCountAttribute();
}
public function getFavoritesCountAttribute()
{
if (! isset($this->attributes['favorites_count'])) {
$this->loadCount('favorites');
}
return $this->attributes['favorites_count'];
}
public function getActiveParticipantsCountAttribute()
{
if (! isset($this->attributes['active_participants_count'])) {
$this->loadCount('activeParticipants');
}
return $this->attributes['active_participants_count'];
}
public function getTracksWithTelephonyCountAttribute()
{
if (! isset($this->attributes['tracks_with_telephony_count'])) {
$this->loadCount('tracksWithTelephony');
}
return $this->attributes['tracks_with_telephony_count'];
}
/**
* @TEMP
* $this->loadCount('tracksWithTelephony') throws null pointer exception
*/
public function countTracksWithTelephony(): int
{
return $this->tracks()->whereNotNull('telephony_provider_id')->count();
}
public function getDuration(): float
{
return $this->getAttribute('duration');
}
public function getDurationForHumansAttribute()
{
return Carbon::now()->subSeconds($this->duration)->diffForHumans(now(), true);
}
public function getDurationForHumansShortAttribute(): string
{
return Carbon::now()->subSeconds($this->duration)->diffForHumans(now(), true, true);
}
public function hasRecordingPreference(): bool
{
re...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
86379
|
2962
|
3
|
2026-05-28T14:06:24.111311+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-28/1779 /Users/lukas/.screenpipe/data/data/2026-05-28/1779977184111_m1.jpg...
|
PhpStorm
|
faVsco.js – Activity.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
#12121 on JY-20963-fix-im Project: faVsco.js, menu
#12121 on JY-20963-fix-import-on-deleted-entity, menu
Start Listening for PHP Debug Connections
ServiceTest
Run 'ServiceTest'
Debug 'ServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
4
388
9
103
4
Previous Highlighted Error
Next Highlighted Error
<?php
namespace Jiminny\Models;
use Carbon\Carbon;
use Database\Factories\ActivityFactory;
use DateTimeInterface;
use Exception;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Database\Eloquent;
use Illuminate\Database\Eloquent\Attributes\Scope;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Auth;
use InvalidArgumentException;
use Jiminny\Component\ElasticSearch;
use Jiminny\Component\MeetingBot;
use Jiminny\Component\Model\BitwiseFlagTrait;
use Jiminny\Component\PlaybackPage\Comments\Services\ActivityCommentService;
use Jiminny\Component\Sidekick\SidekickService;
use Jiminny\Component\Uuid\UuidAwareInterface;
use Jiminny\Component\Workflow;
use Jiminny\Contracts;
use Jiminny\Contracts\Crm\ProspectInterface;
use Jiminny\DTO\ImportCall\Call;
use Jiminny\Events\Activities\ActivityTypeUpdated;
use Jiminny\Events\Activities\ActivityUpdated;
use Jiminny\Events\Activities\ProspectUpdated;
use Jiminny\Events\Activities\StageUpdated;
use Jiminny\Events\Activities\StatusUpdated;
use Jiminny\Events\Activities\TitleUpdated;
use Jiminny\Exceptions\InvalidArgumentException as InvalidArgumentJiminnyException;
use Jiminny\Exceptions\LogicException;
use Jiminny\Exceptions\RuntimeException;
use Jiminny\Models;
use Jiminny\Models\Activity\ActivitySummaryLog;
use Jiminny\Models\Activity\ActivityUploadSetting;
use Jiminny\Models\Activity\AvailabilityNotification;
use Jiminny\Models\Activity\CoachRequest;
use Jiminny\Models\Activity\Comment;
use Jiminny\Models\Activity\Log;
use Jiminny\Models\Activity\Message;
use Jiminny\Models\Activity\Moment;
use Jiminny\Models\Activity\Note;
use Jiminny\Models\Activity\ParticipantSpeech;
use Jiminny\Models\Activity\Play;
use Jiminny\Models\Activity\Question;
use Jiminny\Models\Activity\Share;
use Jiminny\Models\Activity\Snapshot;
use Jiminny\Models\Activity\Stats;
use Jiminny\Models\Activity\SubscriptionSet;
use Jiminny\Models\Activity\TopicTrigger;
use Jiminny\Models\Activity\Transcription;
use Jiminny\Models\Calendar\CalendarEvent;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Models\Crm\FieldData;
use Jiminny\Models\ElasticSearch\ActivityElasticSearchTrait;
use Jiminny\Models\Feature\FeatureEnum;
use Jiminny\Models\Participant\Connection;
use Jiminny\Models\Playlist\Activity as PlaylistActivity;
use Jiminny\Services\Activity\ActivityProviderRegistry;
use Jiminny\Services\Activity\Import\DataResolvers\UpdateCrmDataByStrategy;
use Jiminny\Services\Activity\Import\DataResolvers\UpdateCrmDataResolverFactory;
use Jiminny\Services\Activity\Import\DataResolvers\UpdateCrmDataResolverInterface;
use Jiminny\Traits\Enums;
use Jiminny\Traits\RequiresUUID;
use Jiminny\Utils\CurrencyFormatter;
use NumberFormatter;
use function in_array;
/**
* Jiminny\Models\Activity
*
* @property null|int $auto_score filled from ES hydrator, not in DB!
* @property-read Account|null $account
* @property-read CalendarEvent|null $calendarEvent
* @property-read Contact|null $contact
* @property-read Lead|null $lead
* @property-read Opportunity|null $opportunity
* @property-read Stage|null $stage
* @property int $id
* @property mixed|null $uuid
* @property string|null $source
* @property string|null $external_id
* @property string $provider
* @property string|null $location
* @property string|null $telephony_provider_id
* @property int|null $from_participant_id
* @property int|null $to_participant_id
* @property int|null $device_id
* @property string|null $type
* @property int|null $playbook_category_id
* @property int $user_id
* @property int|null $lead_id
* @property int|null $account_id
* @property int|null $contact_id
* @property int|null $opportunity_id
* @property int|null $stage_id
* @property string|null $value
* @property int|null $crm_configuration_id
* @property string|null $crm_provider_id
* @property string|null $language
* @property int|null $transcription_id
* @property int $duration
* @property string $status
* @property int|null $on_air
* @property int|null $calendar_event_id
* @property string $recording_state
* @property bool|null $recording_preference
* @property int $recording_reason_code
* @property int $summary_reminder_sent
* @property \Illuminate\Support\Carbon|null $log_reminder_sent_at
* @property \Illuminate\Support\Carbon|null $organizer_notified_at
* @property bool|null $has_recording_prompt
* @property bool $is_internal
* @property int $is_locked
* @property int $is_recording
* @property bool|null $is_processed
* @property bool $is_private
* @property bool $is_instant_invite
* @property string|null $poster_path
* @property string|null $summary
* @property string|null $title
* @property string|null $description
* @property \Illuminate\Support\Carbon|null $scheduled_start_time
* @property \Illuminate\Support\Carbon|null $scheduled_end_time
* @property \Illuminate\Support\Carbon|null $actual_start_time
* @property \Illuminate\Support\Carbon|null $actual_end_time
* @property int|null $uploaded_by
* @property \Illuminate\Support\Carbon|null $deleted_at
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property string|null $average_score
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Participant> $activeParticipants
* @property-read int|null $active_participants_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Scorecard\ActivityScorecardRuleTrigger> $activityScorecardRuleTriggers
* @property-read int|null $activity_scorecard_rule_triggers_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Scorecard\ActivityScorecardRule> $activityScorecardRules
* @property-read int|null $activity_scorecard_rules_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, AvailabilityNotification> $availabilityNotifications
* @property-read int|null $availability_notifications_count
* @property-read \Jiminny\Models\PlaybookCategory|null $category
* @property-read \Illuminate\Database\Eloquent\Collection<int, CoachRequest> $coachRequests
* @property-read int|null $coach_requests_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\CoachingFeedback> $coachingFeedbacks
* @property-read int|null $coaching_feedbacks_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, Message> $coachingMessages
* @property-read int|null $coaching_messages_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, Comment> $comments
* @property-read int|null $comments_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, Connection> $connections
* @property-read int|null $connections_count
* @property-read Configuration|null $crm
* @property-read \Illuminate\Database\Eloquent\Collection<int, FieldData> $data
* @property-read int|null $data_count
* @property-read \Jiminny\Models\Device|null $device
* @property-read \Kalnoy\Nestedset\Collection<int, \Jiminny\Models\Playlist> $favoritePlaylists
* @property-read int|null $favorite_playlists_count
* @property-read \Jiminny\Models\Participant|null $from
* @property-read string|null $activity_title
* @property-read mixed $comment_count
* @property-read mixed $duration_for_humans
* @property-read string $duration_for_humans_short
* @property-read int $favorite_count
* @property-read mixed $favorites_count
* @property-read mixed $formatted_value
* @property-read string $id_string
* @property-read \Jiminny\Models\Participant|null $organizer
* @property-read mixed $play_count
* @property-read int|null $plays_count
* @property-read ?ProspectInterface $prospect
* @property-read string|null $prospect_name
* @property-read mixed $prospect_type
* @property-read mixed $share_count
* @property-read int|null $shares_count
* @property-read int|null $tracks_with_telephony_count
* @property-read int|null $visible_comments_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\CoachingFeedback> $latestCoachingFeedbacks
* @property-read int|null $latest_coaching_feedbacks_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, Log> $logs
* @property-read int|null $logs_count
* @property-read \Jiminny\Models\Track|null $masterTrack
* @property-read \Illuminate\Database\Eloquent\Collection<int, Message> $messages
* @property-read int|null $messages_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, Moment> $moments
* @property-read int|null $moments_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, Note> $notes
* @property-read int|null $notes_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Participant\Share> $participantShares
* @property-read int|null $participant_shares_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, ParticipantSpeech> $participantSpeeches
* @property-read int|null $participant_speeches_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Participant\ParticipantStats> $participantStats
* @property-read int|null $participant_stats_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Participant> $participants
* @property-read int|null $participants_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, PlaylistActivity> $playlistActivities
* @property-read int|null $playlist_activities_count
* @property-read \Kalnoy\Nestedset\Collection<int, \Jiminny\Models\Playlist> $playlists
* @property-read int|null $playlists_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, Play> $plays
* @property-read \Illuminate\Database\Eloquent\Collection<int, Question> $questions
* @property-read int|null $questions_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Session> $sessions
* @property-read int|null $sessions_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, Share> $shares
* @property-read \Illuminate\Database\Eloquent\Collection<int, Snapshot> $snapshots
* @property-read int|null $snapshots_count
* @property-read Stats|null $stats
* @property-read \Jiminny\Models\Participant|null $to
* @property-read \Illuminate\Database\Eloquent\Collection<int, TopicTrigger> $topicTriggers
* @property-read int|null $topic_triggers_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Track> $tracks
* @property-read int|null $tracks_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Track> $tracksWithTelephony
* @property-read Transcription|null $transcription
* @property-read \Jiminny\Models\User $user
* @property-read \Illuminate\Database\Eloquent\Collection<int, Comment> $visibleComments
*
* @method static \Illuminate\Database\Eloquent\Collection<int, static> all($columns = ['*'])
* @method static \Jiminny\Component\Eloquent\Builder|Activity chunkByIdDesc($count, callable $callback, $column = null, $alias = null)
* @method static \Database\Factories\ActivityFactory factory(...$parameters)
* @method static \Illuminate\Database\Eloquent\Collection<int, static> get($columns = ['*'])
* @method static \Jiminny\Component\Eloquent\Builder|Activity heldBetween(\Carbon\Carbon $start, \Carbon\Carbon $end)
* @method static \Jiminny\Component\Eloquent\Builder|Activity idOrUuId($idOrUuid, bool $first = true)
* @method static \Jiminny\Component\Eloquent\Builder|Activity newModelQuery()
* @method static \Jiminny\Component\Eloquent\Builder|Activity newQuery()
* @method static Builder|Activity onlyTrashed()
* @method static \Jiminny\Component\Eloquent\Builder|Activity query()
* @method static \Jiminny\Component\Eloquent\Builder|Activity scheduledBetween(\Carbon\Carbon $start, \Carbon\Carbon $end)
* @method static \Jiminny\Component\Eloquent\Builder|Activity inOpenDeals()
* @method static \Jiminny\Component\Eloquent\Builder|Activity notInOpenDeals()
* @method static \Jiminny\Component\Eloquent\Builder|Activity forTeam(int $teamId)
* @method static \Jiminny\Component\Eloquent\Builder|Activity search(callable $searchQuery, $key = null, $sortByResults = true)
* @method static \Jiminny\Component\Eloquent\Builder|Activity uuid(string $uuid, bool $first = true)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereAccountId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereActualEndTime($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereActualStartTime($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereAverageScore($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereCalendarEventId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereContactId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereCreatedAt($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereCrmConfigurationId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereCrmProviderId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereDeletedAt($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereDescription($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereDeviceId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereDuration($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereFromParticipantId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereHasRecordingPrompt($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereIsInstantInvite($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereIsInternal($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereIsLocked($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereIsPrivate($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereIsProcessed($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereIsRecording($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereLanguage($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereLeadId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereLocation($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereLogReminderSentAt($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereOnAir($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereOpportunityId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereOrganizerNotifiedAt($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity wherePlaybookCategoryId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity wherePosterPath($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereProvider($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereRecordingPreference($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereRecordingReasonCode($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereRecordingState($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereScheduledEndTime($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereScheduledStartTime($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereSource($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereExternalId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereStageId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereStatus($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereSummary($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereSummaryReminderSent($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereTelephonyProviderId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereTitle($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereToParticipantId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereTranscriptionId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereType($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereUpdatedAt($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereUploadedBy($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereUserId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereUuid($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereValue($value)
* @method static Builder|Activity withTrashed()
* @method static Builder|Activity withoutTrashed()
*
* @mixin \Eloquent
*/
class Activity extends Model implements
ElasticSearch\Contract\Searchable,
Workflow\Workflow\WorkflowAwareInterface,
Models\Contracts\ActivityContract,
Contracts\Model\ActivityInterface,
UuidAwareInterface
{
use HasFactory;
use Enums;
use SoftDeletes;
use RequiresUUID;
use BitwiseFlagTrait;
use ElasticSearch\Model\Searchable;
use ActivityElasticSearchTrait;
use Workflow\Workflow\WorkflowAware {
transitionTo as traitTransitionTo;
}
public const int FLAG_RECORDING_REASON_DEFAULT = 0;
// Recording Prompted but never started
public const int FLAG_RECORDING_REASON_COMPLIANCE_PROMPT = 1;
public const int FLAG_RECORDING_REASON_COMPLIANCE_RESUMED = 2;
public const int FLAG_RECORDING_REASON_NO_AUDIO = 3;
// Recording Disabled by Organization
public const int FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT = 4;
// Recording was restricted to one-side recordings only
public const int FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT_ONE_SIDE = 8;
// Recording was not started because it was internal and team setting disabled that.
public const int FLAG_RECORDING_REASON_TEAM_INTERNAL_DISABLED = 16;
// Recording was not started because it was internal and user setting disabled that.
public const int FLAG_RECORDING_REASON_USER_INTERNAL_DISABLED = 32;
// Recording was not started because user setting disabled automatic recording.
public const int FLAG_RECORDING_REASON_USER_AUTOMATIC_DISABLED = 64;
// Recording was not started because team setting disabled automatic recording.
public const int FLAG_RECORDING_REASON_TEAM_AUTOMATIC_DISABLED = 128;
// Recording was not started because user has overriden default.
public const int FLAG_RECORDING_REASON_PREFERENCE_OVERRIDE = 256;
// Recording was not started because they don't want internal, and this meeting was not scheduled/imported in time.
public const int FLAG_RECORDING_REASON_USER_INTERNAL_DISABLED_UNSCHEDULED = 512;
// Recording was not started because their team setting does excludes the meeting type.
public const int FLAG_RECORDING_REASON_UNSUPPORTED_TYPE = 1024;
// Recording was not started because the external provider disabled it (or recording is missing etc).
public const int FLAG_RECORDING_REASON_EXTERNALLY_DISABLED = 2048;
// Recording was stopped externally ("exit-meeting" Pusher event)
public const int FLAG_RECORDING_REASON_STOPPED_EXTERNALLY = 384;
// Recording couldn't be started due to Zoom hosting conflict error
public const int FLAG_RECORDING_REASON_HOSTING_CONFLICT = 448;
// meeting.failed event with reason code BOT_DENIED_FROM_LOBBY
public const int FLAG_RECORDING_REASON_MEETING_BOT_DENIED_FROM_LOBBY = 4096;
// meeting.failed event with reason code LOBBY_TIMEOUT
public const int FLAG_RECORDING_REASON_MEETING_BOT_LOBBY_TIMEOUT = 8192;
// meeting.failed event with reason code BOT_KICKED
public const int FLAG_RECORDING_REASON_MEETING_BOT_KICKED = 16384;
// meeting.failed event with reason code UNKNOWN
public const int FLAG_RECORDING_REASON_MEETING_BOT_UNKNOWN = 32768;
public const int FLAG_RECORDING_REASON_CONSENT_DENIED = 65536;
// Invalid meeting (e.g. URL is invalid, or the meeting is not found)
public const int FLAG_RECORDING_REASON_MEETING_BOT_INVALID = 131072;
// The host stopped the recording.
public const int FLAG_RECORDING_REASON_USER_STOPPED = 262144;
// Recording was not started because an alternative vendor disabled it (or overrode it).
public const int FLAG_RECORDING_REASON_VENDOR_OVERRIDE = 1048576;
// Login required meeting.failed code
public const int FLAG_RECORDING_REASON_LOGIN_REQUIRED = 524288;
// Password for meeting was not provided - meeting.failed code
public const int FLAG_RECORDING_REASON_MEETING_PASSWORD_NOT_PROVIDED = 2097152;
// meeting.failed - when the meeting is locked
public const int FLAG_RECORDING_REASON_MEETING_IS_LOCKED = 4194304;
// max recording duration reached
public const int FLAG_RECORDING_REASON_MAX_DURATION_REACHED = 8388608;
// recording size is too small
public const int FLAG_RECORDING_REASON_EMPTY_RECORDING = 16777216;
// meeting.failed - when bot is redirected to sign in page multiple times
public const int FLAG_RECORDING_REASON_MAX_RESTART_COUNT_IS_REACHED = 33554432;
// meeting.failed event with reason code CONNECTION_LOST
public const int FLAG_RECORDING_REASON_MEETING_BOT_CONNECTION_LOST = 67108864;
// recording is corrupted.
public const int FLAG_RECORDING_REASON_MEDIA_FILE_UNSUPPORTED_MIME_TYPE = 134217728;
// meeting ended in lobby
public const int FLAG_RECORDING_REASON_MEETING_ENDED_IN_LOBBY = 268435456;
// meeting not started
public const int FLAG_RECORDING_REASON_REASON_MEETING_NOT_STARTED = 536870912;
// unfinished zoom custom disclaimer
public const int FLAG_RECORDING_REASON_FEATURE_RULE_NOT_FOUND_ERROR = 1073741824;
// recording download failed - server error
public const int FLAG_RECORDING_REASON_SERVER_ERROR = 2147483648;
// recording download failed - client code 404
public const int FLAG_RECORDING_REASON_NOT_FOUND = 2147483649;
// recording download failed - client code 401, 403
public const int FLAG_RECORDING_REASON_ACCESS_DENIED = 2147483650;
// recording download failed - client code 429
public const int FLAG_RECORDING_REASON_TOO_MANY_REQUESTS = 2147483651;
// recording download failed - unknown client error
public const int FLAG_RECORDING_REASON_CLIENT_ERROR = 2147483652;
// recording download failed - unknown error
public const int FLAG_RECORDING_REASON_UNKNOWN_ERROR = 2147483653;
// It has been setup ahead of time through calendar
public const string STATUS_SCHEDULED = 'scheduled';
// It is awaiting audio.
public const string STATUS_PENDING = 'pending';
// Participant(s) dialed in, awaiting organizer.
public const string STATUS_RINGING = 'ringing';
// Call is in progress.
public const string STATUS_IN_PROGRESS = 'in-progress';
// It has ended.
public const string STATUS_COMPLETED = 'completed';
// Cancelled prior to starting.
public const string STATUS_CANCELLED = 'canceled';
public const string STATUS_DUPLICATED = 'duplicated'; // duplicated conference
public const string STATUS_STARTING_SOON = 'starting-soon';
public const string STATUS_BOT_CREATE_SENT = 'bot-create-sent';
public const string STATUS_BOT_INSTANCE_WORKER_ASSIGNED = 'worker-assigned';
public const string STATUS_BOT_INSTANCE_STARTED = 'bot-started';
// When bot instance is waiting in lobby
public const string STATUS_BOT_INSTANCE_WAITING_LOBBY = 'bot-waiting';
public const string STATUS_BUSY = 'busy';
public const string STATUS_NO_ANSWER = 'no-answer';
public const string STATUS_FAILED = 'failed'; // Used by SMS too
// SMS related
public const string STATUS_ACCEPTED = 'accepted';
public const string STATUS_QUEUED = 'queued';
public const string STATUS_SENDING = 'sending';
public const string STATUS_SENT = 'sent';
public const string STATUS_DELIVERED = 'delivered';
public const string STATUS_UNDELIVERED = 'undelivered';
public const string STATUS_RECEIVING = 'receiving';
public const string STATUS_RECEIVED = 'received';
public const string STATUS_RESENT = 'resent';
public const array SMS_STATUSES = [
Activity::STATUS_RECEIVED,
Activity::STATUS_SENT,
Activity::STATUS_DELIVERED,
];
public const array SOFT_PHONE_CONFERENCE_STATUSES = [
Activity::STATUS_IN_PROGRESS,
Activity::STATUS_COMPLETED,
];
// @todo refactor prefix from `TYPE_` to `CHANNEL_`
public const string TYPE_SOFTPHONE = 'softphone';
public const string TYPE_SOFTPHONE_INBOUND = 'softphone-inbound';
public const string TYPE_CONFERENCE = 'conference';
public const string TYPE_SMS_INBOUND = 'sms-inbound';
public const string TYPE_SMS_OUTBOUND = 'sms-outbound';
public const string TYPE_EMAIL_INBOUND = 'email-inbound';
public const string TYPE_EMAIL_OUTBOUND = 'email-outbound';
public const array CHANNELS = [
self::TYPE_SOFTPHONE,
self::TYPE_SOFTPHONE_INBOUND,
self::TYPE_CONFERENCE,
self::TYPE_SMS_INBOUND,
self::TYPE_SMS_OUTBOUND,
self::TYPE_EMAIL_INBOUND,
self::TYPE_EMAIL_OUTBOUND,
];
public const array PLAYABLE_CHANNELS = [
self::TYPE_SOFTPHONE,
self::TYPE_SOFTPHONE_INBOUND,
self::TYPE_CONFERENCE,
];
// Recording States
public const string RECORDING_OFF = 'off'; // Default state
public const string RECORDING_IN_PROGRESS = 'in-progress';
public const string RECORDING_PAUSED = 'paused';
public const string RECORDING_STOPPED = 'stopped'; // To never be resumed.
public const string RECORDING_RECORDED = 'recorded'; // At least some portion of it was recorded.
public const string RECORDING_FAILED = 'failed'; // Recording was attempted but failed for some reason.
// Live Stream States
public const int ON_AIR_DEFAULT = 0;
public const int ON_AIR_READY = 1;
public const int ON_AIR_PREPARING = 2;
public const int ON_AIR_STREAMING = 3;
public const int ON_AIR_FINISHED = 4;
public const int ON_AIR_NOT_STREAMED = 5;
public const int ON_AIR_ERROR = -1;
public const string SOURCE_GONG = 'gong';
public const string SOURCE_CHORUS = 'chorus';
public const string SOURCE_OUTLOOK = 'outlook';
public const string SOURCE_GOOGLE = 'google';
// Activity Providers
public const string PROVIDER_TWILIO = 'twilio'; // XXX: This is run via the Jiminny Provider.
public const string PROVIDER_OUTREACH = 'outreach';
public const string PROVIDER_ZOOM_BOT = 'zoom-bot';
public const string PROVIDER_SALESLOFT = 'salesloft';
public const string PROVIDER_GOOGLE = 'google';
public const string PROVIDER_AIRCALL = 'aircall';
public const string PROVIDER_JUSTCALL = 'justcall';
public const string PROVIDER_GOOGLE_MEET = 'google-meet';
public const string PROVIDER_GONG = 'gong';
public const string PROVIDER_HUBSPOT = 'hubspot';
public const string PROVIDER_CLOSE = 'close';
public const string PROVIDER_TEAMS = 'ms-teams';
public const string PROVIDER_SALESFORCE = 'salesforce';
public const string PROVIDER_GROOVE = 'groove';
public const string PROVIDER_XANT = 'xant';
public const string PROVIDER_OFFICE = 'office';
public const string PROVIDER_NATTERBOX = 'natterbox';
public const string PROVIDER_RINGCENTRAL = 'ringcentral';
public const string PROVIDER_RINGCENTRAL_VIDEO = 'ringcentral-video';
public const string PROVIDER_GOTOMEETING = 'go-to-meeting';
public const string PROVIDER_DEMODESK = 'demo-desk';
public const string PROVIDER_DIALPAD = 'dialpad';
public const string PROVIDER_ZOOM_PHONE = 'zoom-phone';
public const string PROVIDER_CLOUDCALL = 'cloudcall';
public const string PROVIDER_CLOUDCALL_US = 'cloudcall-us';
public const string PROVIDER_EIGHT_BY_EIGHT = 'eight-by-eight'; // "8x8" UK
public const string PROVIDER_EIGHT_BY_EIGHT_CA = 'eight-by-eight-ca'; // "8x8" Canada
public const string PROVIDER_EIGHT_BY_EIGHT_AP = 'eight-by-eight-ap'; // "8x8" Australia
public const string PROVIDER_EIGHT_BY_EIGHT_US_EAST = 'eight-by-eight-use'; // "8x8" US East
public const string PROVIDER_EIGHT_BY_EIGHT_US_WEST = 'eight-by-eight-usw'; // "8x8" US West
public const string PROVIDER_CONNECT_AND_SELL = 'connect-and-sell';
public const string PROVIDER_CLOUD_TALK = 'cloud-talk';
public const string PROVIDER_AMAZON_CONNECT = 'amazon-connect';
public const string PROVIDER_VONAGE = 'vonage';
public const string PROVIDER_MIGRATOR = 'migrator';
public const string PROVIDER_UPLOADER = 'uploader';
public const string PROVIDER_TALKDESK = 'talkdesk';
public const string PROVIDER_TWILIO_FLEX = 'twilio-flex';
public const string PROVIDER_TWILIO_FLEX_DIRECT = 'twilio-flex-direct';
public const string PROVIDER_TWILIO_VIDEO = 'twilio-video';
public const string PROVIDER_AVAYA = 'avaya';
public const string PROVIDER_TELUS = 'telus';
public const string PROVIDER_FIVE_NINE = 'five-nine';
public const string PROVIDER_APOLLO = 'apollo';
public const string PROVIDER_ORUM = 'orum';
public const string PROVIDER_BLOOBIRDS = 'bloobirds';
/**
* @const API_PROVIDERS
* A list of integrations that import calls via API instead of webhooks
*/
public const array API_PROVIDERS = [
self::PROVIDER_OUTREACH,
self::PROVIDER_SALESLOFT,
self::PROVIDER_HUBSPOT,
self::PROVIDER_GROOVE,
self::PROVIDER_XANT,
self::PROVIDER_NATTERBOX,
self::PROVIDER_CLOUDCALL,
self::PROVIDER_CLOUDCALL_US,
self::PROVIDER_EIGHT_BY_EIGHT,
self::PROVIDER_EIGHT_BY_EIGHT_CA,
self::PROVIDER_EIGHT_BY_EIGHT_AP,
self::PROVIDER_EIGHT_BY_EIGHT_US_EAST,
self::PROVIDER_EIGHT_BY_EIGHT_US_WEST,
self::PROVIDER_CONNECT_AND_SELL,
self::PROVIDER_CLOUD_TALK,
self::PROVIDER_AMAZON_CONNECT,
self::PROVIDER_VONAGE,
self::PROVIDER_TALKDESK,
self::PROVIDER_TWILIO_VIDEO,
self::PROVIDER_TWILIO_FLEX,
self::PROVIDER_TWILIO_FLEX_DIRECT,
self::PROVIDER_FIVE_NINE,
self::PROVIDER_APOLLO,
self::PROVIDER_ORUM,
self::PROVIDER_BLOOBIRDS,
self::PROVIDER_RINGCENTRAL,
self::PROVIDER_AVAYA,
self::PROVIDER_TELUS,
];
public const array FINITE_STATES = [
self::TYPE_SOFTPHONE => [
self::STATUS_COMPLETED,
self::STATUS_FAILED,
self::STATUS_NO_ANSWER,
self::STATUS_BUSY,
],
self::TYPE_SOFTPHONE_INBOUND => [
self::STATUS_COMPLETED,
self::STATUS_FAILED,
self::STATUS_NO_ANSWER,
self::STATUS_BUSY,
],
self::TYPE_CONFERENCE => self::FINITE_STATES_CONFERENCE,
];
public const array FINITE_STATES_CONFERENCE = [
self::STATUS_COMPLETED,
self::STATUS_FAILED,
self::STATUS_CANCELLED,
];
public const array MEETING_BOT_JOIN_ATTEMPTED = [
self::STATUS_BOT_INSTANCE_WAITING_LOBBY,
self::STATUS_BOT_INSTANCE_STARTED,
];
public static array $enumStatuses = [
self::STATUS_SCHEDULED,
self::STATUS_PENDING,
self::STATUS_RINGING,
self::STATUS_IN_PROGRESS,
self::STATUS_COMPLETED,
self::STATUS_CANCELLED,
self::STATUS_BUSY,
self::STATUS_NO_ANSWER,
self::STATUS_FAILED,
self::STATUS_ACCEPTED,
self::STATUS_QUEUED,
self::STATUS_SENDING,
self::STATUS_SENT,
self::STATUS_RESENT,
self::STATUS_DELIVERED,
self::STATUS_UNDELIVERED,
self::STATUS_RECEIVING,
self::STATUS_RECEIVED,
self::STATUS_BOT_INSTANCE_WAITING_LOBBY,
self::STATUS_STARTING_SOON,
self::STATUS_BOT_INSTANCE_WORKER_ASSIGNED,
self::STATUS_BOT_INSTANCE_STARTED,
self::STATUS_DUPLICATED,
];
public static array $enumProviders = [
self::PROVIDER_TWILIO,
self::PROVIDER_OUTREACH,
self::PROVIDER_ZOOM_BOT,
self::PROVIDER_SALESLOFT,
self::PROVIDER_AIRCALL,
self::PROVIDER_JUSTCALL,
self::PROVIDER_GOOGLE_MEET,
self::PROVIDER_GONG,
self::PROVIDER_HUBSPOT,
self::PROVIDER_CLOSE,
self::PROVIDER_TEAMS,
self::PROVIDER_SALESFORCE,
self::PROVIDER_GROOVE,
self::PROVIDER_XANT,
self::PROVIDER_GOOGLE,
self::PROVIDER_OFFICE,
self::PROVIDER_NATTERBOX,
self::PROVIDER_RINGCENTRAL,
self::PROVIDER_RINGCENTRAL_VIDEO,
self::PROVIDER_GOTOMEETING,
self::PROVIDER_DEMODESK,
self::PROVIDER_DIALPAD,
self::PROVIDER_ZOOM_PHONE,
self::PROVIDER_CLOUDCALL,
self::PROVIDER_CLOUDCALL_US,
self::PROVIDER_EIGHT_BY_EIGHT,
self::PROVIDER_EIGHT_BY_EIGHT_CA,
self::PROVIDER_EIGHT_BY_EIGHT_AP,
self::PROVIDER_EIGHT_BY_EIGHT_US_EAST,
self::PROVIDER_EIGHT_BY_EIGHT_US_WEST,
self::PROVIDER_CONNECT_AND_SELL,
self::PROVIDER_CLOUD_TALK,
self::PROVIDER_AMAZON_CONNECT,
self::PROVIDER_VONAGE,
self::PROVIDER_TALKDESK,
self::PROVIDER_TWILIO_FLEX,
self::PROVIDER_TWILIO_FLEX_DIRECT,
self::PROVIDER_TWILIO_VIDEO,
self::PROVIDER_AVAYA,
self::PROVIDER_TELUS,
self::PROVIDER_FIVE_NINE,
self::PROVIDER_APOLLO,
self::PROVIDER_ORUM,
self::PROVIDER_BLOOBIRDS,
];
public static $enumRecordingStates = [
self::RECORDING_OFF, // Default state
self::RECORDING_IN_PROGRESS,
self::RECORDING_PAUSED,
self::RECORDING_STOPPED,
self::RECORDING_RECORDED,
self::RECORDING_FAILED,
];
// @Important:
// This collection is not used anywhere, and is fully duplicated by the Channels const.
// Validate if it is referred somehow via the enum trait, and if not, remove it entirely.
// An even better strategy will be to move all those constants to a dedicated class
protected array $enumTypes = [
self::TYPE_SOFTPHONE,
self::TYPE_SOFTPHONE_INBOUND,
self::TYPE_CONFERENCE,
self::TYPE_SMS_INBOUND,
self::TYPE_SMS_OUTBOUND,
self::TYPE_EMAIL_INBOUND,
self::TYPE_EMAIL_OUTBOUND,
];
protected static $enumFailedStatuses = [
self::STATUS_NO_ANSWER,
self::STATUS_FAILED,
self::STATUS_BUSY,
self::STATUS_CANCELLED,
];
protected $table = 'activities';
protected $fillable = [
// Type of activity.
'type', // @todo refactor to `channel`
// The activity type.
'playbook_category_id',
// User who hosts the activity.
'user_id',
// Related Lead record (if applicable)
'lead_id',
// Related Account record (if applicable)
'account_id',
// Related Contact record (if applicable)
'contact_id',
// Related Opportunity record (if applicable)
'opportunity_id',
// Stage of activity.
'stage_id',
// Value of opportunity.
'value',
// If the activity relates to a CRM task.
'crm_provider_id',
// If the activity was created through an external device.
'device_id',
// the activity's language code
'language',
// transcription id
'transcription_id',
// Duration of the call, with microseconds precision.
'duration',
// One of enumStatuses above.
'status',
// Have we reminded them to log the call?
'log_reminder_sent_at',
// If activity is private or inter-org, flagged here.
'is_internal',
// Managers and above can mark a call as private, to exclude it from other team members
'is_private',
'is_processed',
// Boolean for this activity being instant invite handled.
'is_instant_invite',
// If activity is in recording state, flagged here.
'recording_state',
// If activity recording is overidden from default.
'recording_preference',
// if recording did (not) happen, why that is
'recording_reason_code',
// Average score, updated during
'average_score',
// Summary that the organizer has taken after the call.
'summary',
// Subject of the activity, usually taken from calendar event.
'title',
// Description of the activity, usually taken from calendar event.
'description',
// Start time, usually taken from calendar event.
'scheduled_start_time',
// End time, usually taken from calendar event.
'scheduled_end_time',
// When the call actually started.
'actual_start_time',
// When the call actually ended.
'actual_end_time',
// SMS: Message reference
'telephony_provider_id',
// SMS: Participant who sent message
'from_participant_id',
// SMS: Participant who should receive the message
'to_participant_id',
// When an external guest joins an organizers meeting room and the organizer is not present,
// send them an SMS notification that someone has joined.
'organizer_notified_at',
// where was the activity imported from
'source',
// The id in the source system (e.g. the bot id in Recall.ai)
'external_id',
// The provider, by default it is twilio.
'provider',
// Meeting location url
'location',
// The snapshot for displaying a poster image.
'poster_path',
'crm_configuration_id',
// If there is an automated message that the conversation is being recorded
'has_recording_prompt',
// If the activity is being live-streamed
'on_air',
'calendar_event_id',
];
protected $appends = [
'id_string',
'organizer',
];
protected $hidden = [
'uuid',
];
protected $visible = [
'id_string',
'type',
'duration',
'average_score',
'status',
'log_reminder_sent_at',
'title',
'description',
'is_internal',
'scheduled_start_time',
'scheduled_end_time',
'actual_start_time',
'actual_end_time',
'user',
'category',
'account',
'contact',
'opportunity',
'lead',
'stage',
'stats',
'participants',
'playlists',
'tracks',
'comments',
'plays',
'coachingFeedbacks',
'shares',
'favorites',
'language',
'transcription',
'is_private',
'is_instant_invite',
'on_air',
'calendar_event_id',
];
protected function casts(): array
{
return [
'scheduled_start_time' => 'datetime',
'scheduled_end_time' => 'datetime',
'actual_start_time' => 'datetime',
'actual_end_time' => 'datetime',
'organizer_notified_at' => 'datetime',
'log_reminder_sent_at' => 'datetime',
'is_internal' => 'boolean',
'duration' => 'integer',
'average_score' => 'decimal:2',
'is_private' => 'boolean',
'is_processed' => 'boolean',
'is_instant_invite' => 'boolean',
'value' => 'decimal:2',
'recording_preference' => 'boolean',
'recording_reason_code' => 'integer',
'has_recording_prompt' => 'boolean',
'on_air' => 'integer',
];
}
protected static function boot()
{
parent::boot();
static::updated(static function (Activity $activity) {
// If activity is about to start (pending, ringing, in-progress) or event is scheduled in less than 1 week
if (in_array($activity->status, [Activity::STATUS_PENDING, Activity::STATUS_RINGING, Activity::STATUS_IN_PROGRESS], true) ||
($activity->scheduled_start_time && (int) $activity->scheduled_start_time->diffInWeeks(new Carbon(), true) < 1)) {
if ($activity->isDirty('status')) {
event(new StatusUpdated($activity));
}
if ($activity->isDirty('stage_id')) {
event(new StageUpdated($activity));
}
if ($activity->isDirty(['lead_id', 'account_id', 'contact_id'])) {
event(new ProspectUpdated($activity));
}
if ($activity->isDirty('opportunity_id')) {
event(new ActivityUpdated($activity, 'activity.opportunity-updated', Auth::user()));
}
if ($activity->isDirty('title')) {
event(new TitleUpdated($activity));
}
}
if ($activity->isDirty('playbook_category_id')) {
event(new ActivityTypeUpdated($activity));
}
});
static::deleted(static function (Activity $activity) {
// Hard delete associated playlistActivities
$activity->playlistActivities()->delete();
});
}
public function getOrganizerAttribute(): ?Participant
{
$participant = $this->participants()->where('user_id', $this->user_id)->first();
if (! $participant instanceof Participant && $participant !== null) {
throw new RuntimeException(sprintf('$participant must be an instance of "%s" or null', Participant::class));
}
return $participant;
}
public function getFormattedValueAttribute()
{
$currencyCode = 'USD';
if ($this->opportunity) {
$currencyCode = $this->opportunity->getCurrencyCode();
}
$formatter = new CurrencyFormatter();
$formatter->setTextAttribute(NumberFormatter::CURRENCY_CODE, $currencyCode);
$formatter->setAttribute(NumberFormatter::MAX_FRACTION_DIGITS, 0);
return $formatter->format($this->value, $currencyCode);
}
public function getProspectNameAttribute(): ?string
{
$prospectName = null;
if ($this->lead_id) {
$prospectName = $this->lead->name;
} elseif ($this->contact_id) {
$prospectName = $this->contact->name;
} elseif ($this->account_id) {
$prospectName = $this->account->name;
}
return $prospectName;
}
public function getProspectName(): ?string
{
/** @var string|null */
return $this->getAttribute('prospect_name');
}
/**
* Get activity title depending on prospect or title
*/
public function getActivityTitleAttribute(): ?string
{
$activityTitle = null;
if ($this->prospect && $this->prospect->getName()) {
if ($this->account_id) {
$activityTitle = $this->account->name;
} elseif ($this->lead_id) {
$activityTitle = $this->lead->company;
} elseif ($this->contact_id) {
$activityTitle = $this->contact->account ? $this->contact->account->name : $this->contact->name;
}
} elseif ($this->title) {
$activityTitle = $this->title;
}
return $activityTitle;
}
public function wasRecentlyCreated(): bool
{
return $this->wasRecentlyCreated;
}
public function getProspectTypeAttribute()
{
$prospectType = null;
if ($this->lead_id) {
$prospectType = 'Lead';
} elseif ($this->contact_id) {
$prospectType = 'Contact';
} elseif ($this->account_id) {
$prospectType = 'Account';
}
return $prospectType;
}
/**
* Return the best match for prospect. Results are in the following order of priority:
* 1. Lead
* 2. Contact
* 3. Account
* 4. NULL
*/
public function getProspectAttribute(): ?ProspectInterface
{
if ($this->hasLead()) {
return $this->getLead();
}
if ($this->hasContact()) {
return $this->getContact();
}
if ($this->hasAccount()) {
return $this->getAccount();
}
return null;
}
public function getTitleAttribute($value): ?string
{
return \getActivityTitleAttribute(
$this->user->name,
$this->getType(),
$value,
$this->prospect->name ?? null,
$this->from->national_phone_number ?? null
);
}
public function getTitle(): ?string
{
return $this->getAttribute('title');
}
public function getSummary(): ?string
{
return $this->getAttribute('summary');
}
public function isInternal(): bool
{
return $this->getAttribute('is_internal');
}
public function getIsPrivate(): bool
{
return $this->getAttribute('is_private');
}
public function getDescription(): ?string
{
return $this->getAttribute('description');
}
public function hasTitle(): bool
{
return $this->getOriginal('title') !== null;
}
public function getPlayCountAttribute()
{
return $this->getPlaysCountAttribute();
}
public function getPlaysCountAttribute()
{
if (! isset($this->attributes['plays_count'])) {
$this->loadCount('plays');
}
return $this->attributes['plays_count'];
}
public function getCommentCountAttribute()
{
return $this->getCommentsCountAttribute();
}
public function getCommentsCountAttribute()
{
if (! isset($this->attributes['comments_count'])) {
$this->loadCount('comments');
}
return $this->attributes['comments_count'];
}
public function getVisibleCommentsCountAttribute()
{
if (! isset($this->attributes['visible_comments_count'])) {
$activityCommentsService = app(ActivityCommentService::class);
$user = Auth::user() instanceof User ? Auth::user() : null;
$this->attributes['visible_comments_count'] = $activityCommentsService
->getVisibleCommentsCount($this, $user);
}
return $this->attributes['visible_comments_count'];
}
public function getShareCountAttribute()
{
return $this->getSharesCountAttribute();
}
public function getSharesCountAttribute()
{
if (! isset($this->attributes['shares_count'])) {
$this->loadCount('shares');
}
return $this->attributes['shares_count'];
}
/**
* Get the count of favorites playlists this activity appears in
*/
public function getFavoriteCountAttribute(): int
{
return $this->getFavoritesCountAttribute();
}
public function getFavoritesCountAttribute()
{
if (! isset($this->attributes['favorites_count'])) {
$this->loadCount('favorites');
}
return $this->attributes['favorites_count'];
}
public function getActiveParticipantsCountAttribute()
{
if (! isset($this->attributes['active_participants_count'])) {
$this->loadCount('activeParticipants');
}
return $this->attributes['active_participants_count'];
}
public function getTracksWithTelephonyCountAttribute()
{
if (! isset($this->attributes['tracks_with_telephony_count'])) {
$this->loadCount('tracksWithTelephony');
}
return $this->attributes['tracks_with_telephony_count'];
}
/**
* @TEMP
* $this->loadCount('tracksWithTelephony') throws null pointer exception
*/
public function countTracksWithTelephony(): int
{
return $this->tracks()->whereNotNull('telephony_provider_id')->count();
}
public function getDuration(): float
{
return $this->getAttribute('duration');
}
public function getDurationForHumansAttribute()
{
return Carbon::now()->subSeconds($this->duration)->diffForHumans(now(), true);
}
public function getDurationForHumansShortAttribute(): string
{
return Carbon::now()->subSeconds($this->duration)->diffForHumans(now(), true, true);
}
public function hasRecordingPreference(): bool
{
re...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"#12121 on JY-20963-fix-import-on-deleted-entity, menu","depth":5,"on_screen":true,"help_text":"Pull request #12121 exists for current branch JY-20963-fix-import-on-deleted-entity, but pull request details loading failed","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"ServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'ServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'ServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"4","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"388","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"9","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"103","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"4","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\nnamespace Jiminny\\Models;\n\nuse Carbon\\Carbon;\nuse Database\\Factories\\ActivityFactory;\nuse DateTimeInterface;\nuse Exception;\nuse Illuminate\\Contracts\\Auth\\Authenticatable;\nuse Illuminate\\Database\\Eloquent;\nuse Illuminate\\Database\\Eloquent\\Attributes\\Scope;\nuse Illuminate\\Database\\Eloquent\\Builder;\nuse Illuminate\\Database\\Eloquent\\Factories\\Factory;\nuse Illuminate\\Database\\Eloquent\\Factories\\HasFactory;\nuse Illuminate\\Database\\Eloquent\\Relations\\BelongsTo;\nuse Illuminate\\Database\\Eloquent\\Relations\\BelongsToMany;\nuse Illuminate\\Database\\Eloquent\\Relations\\HasMany;\nuse Illuminate\\Database\\Eloquent\\Relations\\HasManyThrough;\nuse Illuminate\\Database\\Eloquent\\Relations\\HasOne;\nuse Illuminate\\Database\\Eloquent\\SoftDeletes;\nuse Illuminate\\Support\\Collection;\nuse Illuminate\\Support\\Facades\\Auth;\nuse InvalidArgumentException;\nuse Jiminny\\Component\\ElasticSearch;\nuse Jiminny\\Component\\MeetingBot;\nuse Jiminny\\Component\\Model\\BitwiseFlagTrait;\nuse Jiminny\\Component\\PlaybackPage\\Comments\\Services\\ActivityCommentService;\nuse Jiminny\\Component\\Sidekick\\SidekickService;\nuse Jiminny\\Component\\Uuid\\UuidAwareInterface;\nuse Jiminny\\Component\\Workflow;\nuse Jiminny\\Contracts;\nuse Jiminny\\Contracts\\Crm\\ProspectInterface;\nuse Jiminny\\DTO\\ImportCall\\Call;\nuse Jiminny\\Events\\Activities\\ActivityTypeUpdated;\nuse Jiminny\\Events\\Activities\\ActivityUpdated;\nuse Jiminny\\Events\\Activities\\ProspectUpdated;\nuse Jiminny\\Events\\Activities\\StageUpdated;\nuse Jiminny\\Events\\Activities\\StatusUpdated;\nuse Jiminny\\Events\\Activities\\TitleUpdated;\nuse Jiminny\\Exceptions\\InvalidArgumentException as InvalidArgumentJiminnyException;\nuse Jiminny\\Exceptions\\LogicException;\nuse Jiminny\\Exceptions\\RuntimeException;\nuse Jiminny\\Models;\nuse Jiminny\\Models\\Activity\\ActivitySummaryLog;\nuse Jiminny\\Models\\Activity\\ActivityUploadSetting;\nuse Jiminny\\Models\\Activity\\AvailabilityNotification;\nuse Jiminny\\Models\\Activity\\CoachRequest;\nuse Jiminny\\Models\\Activity\\Comment;\nuse Jiminny\\Models\\Activity\\Log;\nuse Jiminny\\Models\\Activity\\Message;\nuse Jiminny\\Models\\Activity\\Moment;\nuse Jiminny\\Models\\Activity\\Note;\nuse Jiminny\\Models\\Activity\\ParticipantSpeech;\nuse Jiminny\\Models\\Activity\\Play;\nuse Jiminny\\Models\\Activity\\Question;\nuse Jiminny\\Models\\Activity\\Share;\nuse Jiminny\\Models\\Activity\\Snapshot;\nuse Jiminny\\Models\\Activity\\Stats;\nuse Jiminny\\Models\\Activity\\SubscriptionSet;\nuse Jiminny\\Models\\Activity\\TopicTrigger;\nuse Jiminny\\Models\\Activity\\Transcription;\nuse Jiminny\\Models\\Calendar\\CalendarEvent;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Models\\Crm\\FieldData;\nuse Jiminny\\Models\\ElasticSearch\\ActivityElasticSearchTrait;\nuse Jiminny\\Models\\Feature\\FeatureEnum;\nuse Jiminny\\Models\\Participant\\Connection;\nuse Jiminny\\Models\\Playlist\\Activity as PlaylistActivity;\nuse Jiminny\\Services\\Activity\\ActivityProviderRegistry;\nuse Jiminny\\Services\\Activity\\Import\\DataResolvers\\UpdateCrmDataByStrategy;\nuse Jiminny\\Services\\Activity\\Import\\DataResolvers\\UpdateCrmDataResolverFactory;\nuse Jiminny\\Services\\Activity\\Import\\DataResolvers\\UpdateCrmDataResolverInterface;\nuse Jiminny\\Traits\\Enums;\nuse Jiminny\\Traits\\RequiresUUID;\nuse Jiminny\\Utils\\CurrencyFormatter;\nuse NumberFormatter;\n\nuse function in_array;\n\n/**\n * Jiminny\\Models\\Activity\n *\n * @property null|int $auto_score filled from ES hydrator, not in DB!\n * @property-read Account|null $account\n * @property-read CalendarEvent|null $calendarEvent\n * @property-read Contact|null $contact\n * @property-read Lead|null $lead\n * @property-read Opportunity|null $opportunity\n * @property-read Stage|null $stage\n * @property int $id\n * @property mixed|null $uuid\n * @property string|null $source\n * @property string|null $external_id\n * @property string $provider\n * @property string|null $location\n * @property string|null $telephony_provider_id\n * @property int|null $from_participant_id\n * @property int|null $to_participant_id\n * @property int|null $device_id\n * @property string|null $type\n * @property int|null $playbook_category_id\n * @property int $user_id\n * @property int|null $lead_id\n * @property int|null $account_id\n * @property int|null $contact_id\n * @property int|null $opportunity_id\n * @property int|null $stage_id\n * @property string|null $value\n * @property int|null $crm_configuration_id\n * @property string|null $crm_provider_id\n * @property string|null $language\n * @property int|null $transcription_id\n * @property int $duration\n * @property string $status\n * @property int|null $on_air\n * @property int|null $calendar_event_id\n * @property string $recording_state\n * @property bool|null $recording_preference\n * @property int $recording_reason_code\n * @property int $summary_reminder_sent\n * @property \\Illuminate\\Support\\Carbon|null $log_reminder_sent_at\n * @property \\Illuminate\\Support\\Carbon|null $organizer_notified_at\n * @property bool|null $has_recording_prompt\n * @property bool $is_internal\n * @property int $is_locked\n * @property int $is_recording\n * @property bool|null $is_processed\n * @property bool $is_private\n * @property bool $is_instant_invite\n * @property string|null $poster_path\n * @property string|null $summary\n * @property string|null $title\n * @property string|null $description\n * @property \\Illuminate\\Support\\Carbon|null $scheduled_start_time\n * @property \\Illuminate\\Support\\Carbon|null $scheduled_end_time\n * @property \\Illuminate\\Support\\Carbon|null $actual_start_time\n * @property \\Illuminate\\Support\\Carbon|null $actual_end_time\n * @property int|null $uploaded_by\n * @property \\Illuminate\\Support\\Carbon|null $deleted_at\n * @property \\Illuminate\\Support\\Carbon|null $created_at\n * @property \\Illuminate\\Support\\Carbon|null $updated_at\n * @property string|null $average_score\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Participant> $activeParticipants\n * @property-read int|null $active_participants_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Scorecard\\ActivityScorecardRuleTrigger> $activityScorecardRuleTriggers\n * @property-read int|null $activity_scorecard_rule_triggers_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Scorecard\\ActivityScorecardRule> $activityScorecardRules\n * @property-read int|null $activity_scorecard_rules_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, AvailabilityNotification> $availabilityNotifications\n * @property-read int|null $availability_notifications_count\n * @property-read \\Jiminny\\Models\\PlaybookCategory|null $category\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, CoachRequest> $coachRequests\n * @property-read int|null $coach_requests_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\CoachingFeedback> $coachingFeedbacks\n * @property-read int|null $coaching_feedbacks_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Message> $coachingMessages\n * @property-read int|null $coaching_messages_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Comment> $comments\n * @property-read int|null $comments_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Connection> $connections\n * @property-read int|null $connections_count\n * @property-read Configuration|null $crm\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, FieldData> $data\n * @property-read int|null $data_count\n * @property-read \\Jiminny\\Models\\Device|null $device\n * @property-read \\Kalnoy\\Nestedset\\Collection<int, \\Jiminny\\Models\\Playlist> $favoritePlaylists\n * @property-read int|null $favorite_playlists_count\n * @property-read \\Jiminny\\Models\\Participant|null $from\n * @property-read string|null $activity_title\n * @property-read mixed $comment_count\n * @property-read mixed $duration_for_humans\n * @property-read string $duration_for_humans_short\n * @property-read int $favorite_count\n * @property-read mixed $favorites_count\n * @property-read mixed $formatted_value\n * @property-read string $id_string\n * @property-read \\Jiminny\\Models\\Participant|null $organizer\n * @property-read mixed $play_count\n * @property-read int|null $plays_count\n * @property-read ?ProspectInterface $prospect\n * @property-read string|null $prospect_name\n * @property-read mixed $prospect_type\n * @property-read mixed $share_count\n * @property-read int|null $shares_count\n * @property-read int|null $tracks_with_telephony_count\n * @property-read int|null $visible_comments_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\CoachingFeedback> $latestCoachingFeedbacks\n * @property-read int|null $latest_coaching_feedbacks_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Log> $logs\n * @property-read int|null $logs_count\n * @property-read \\Jiminny\\Models\\Track|null $masterTrack\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Message> $messages\n * @property-read int|null $messages_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Moment> $moments\n * @property-read int|null $moments_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Note> $notes\n * @property-read int|null $notes_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Participant\\Share> $participantShares\n * @property-read int|null $participant_shares_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, ParticipantSpeech> $participantSpeeches\n * @property-read int|null $participant_speeches_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Participant\\ParticipantStats> $participantStats\n * @property-read int|null $participant_stats_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Participant> $participants\n * @property-read int|null $participants_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, PlaylistActivity> $playlistActivities\n * @property-read int|null $playlist_activities_count\n * @property-read \\Kalnoy\\Nestedset\\Collection<int, \\Jiminny\\Models\\Playlist> $playlists\n * @property-read int|null $playlists_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Play> $plays\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Question> $questions\n * @property-read int|null $questions_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Session> $sessions\n * @property-read int|null $sessions_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Share> $shares\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Snapshot> $snapshots\n * @property-read int|null $snapshots_count\n * @property-read Stats|null $stats\n * @property-read \\Jiminny\\Models\\Participant|null $to\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, TopicTrigger> $topicTriggers\n * @property-read int|null $topic_triggers_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Track> $tracks\n * @property-read int|null $tracks_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Track> $tracksWithTelephony\n * @property-read Transcription|null $transcription\n * @property-read \\Jiminny\\Models\\User $user\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Comment> $visibleComments\n *\n * @method static \\Illuminate\\Database\\Eloquent\\Collection<int, static> all($columns = ['*'])\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity chunkByIdDesc($count, callable $callback, $column = null, $alias = null)\n * @method static \\Database\\Factories\\ActivityFactory factory(...$parameters)\n * @method static \\Illuminate\\Database\\Eloquent\\Collection<int, static> get($columns = ['*'])\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity heldBetween(\\Carbon\\Carbon $start, \\Carbon\\Carbon $end)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity idOrUuId($idOrUuid, bool $first = true)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity newModelQuery()\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity newQuery()\n * @method static Builder|Activity onlyTrashed()\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity query()\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity scheduledBetween(\\Carbon\\Carbon $start, \\Carbon\\Carbon $end)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity inOpenDeals()\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity notInOpenDeals()\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity forTeam(int $teamId)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity search(callable $searchQuery, $key = null, $sortByResults = true)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity uuid(string $uuid, bool $first = true)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereAccountId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereActualEndTime($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereActualStartTime($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereAverageScore($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereCalendarEventId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereContactId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereCreatedAt($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereCrmConfigurationId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereCrmProviderId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereDeletedAt($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereDescription($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereDeviceId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereDuration($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereFromParticipantId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereHasRecordingPrompt($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereIsInstantInvite($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereIsInternal($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereIsLocked($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereIsPrivate($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereIsProcessed($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereIsRecording($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereLanguage($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereLeadId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereLocation($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereLogReminderSentAt($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereOnAir($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereOpportunityId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereOrganizerNotifiedAt($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity wherePlaybookCategoryId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity wherePosterPath($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereProvider($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereRecordingPreference($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereRecordingReasonCode($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereRecordingState($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereScheduledEndTime($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereScheduledStartTime($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereSource($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereExternalId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereStageId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereStatus($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereSummary($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereSummaryReminderSent($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereTelephonyProviderId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereTitle($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereToParticipantId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereTranscriptionId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereType($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereUpdatedAt($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereUploadedBy($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereUserId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereUuid($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereValue($value)\n * @method static Builder|Activity withTrashed()\n * @method static Builder|Activity withoutTrashed()\n *\n * @mixin \\Eloquent\n */\nclass Activity extends Model implements\n ElasticSearch\\Contract\\Searchable,\n Workflow\\Workflow\\WorkflowAwareInterface,\n Models\\Contracts\\ActivityContract,\n Contracts\\Model\\ActivityInterface,\n UuidAwareInterface\n{\n use HasFactory;\n\n use Enums;\n use SoftDeletes;\n use RequiresUUID;\n use BitwiseFlagTrait;\n use ElasticSearch\\Model\\Searchable;\n use ActivityElasticSearchTrait;\n\n use Workflow\\Workflow\\WorkflowAware {\n transitionTo as traitTransitionTo;\n }\n\n public const int FLAG_RECORDING_REASON_DEFAULT = 0;\n\n // Recording Prompted but never started\n public const int FLAG_RECORDING_REASON_COMPLIANCE_PROMPT = 1;\n public const int FLAG_RECORDING_REASON_COMPLIANCE_RESUMED = 2;\n public const int FLAG_RECORDING_REASON_NO_AUDIO = 3;\n\n // Recording Disabled by Organization\n public const int FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT = 4;\n\n // Recording was restricted to one-side recordings only\n public const int FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT_ONE_SIDE = 8;\n\n // Recording was not started because it was internal and team setting disabled that.\n public const int FLAG_RECORDING_REASON_TEAM_INTERNAL_DISABLED = 16;\n\n // Recording was not started because it was internal and user setting disabled that.\n public const int FLAG_RECORDING_REASON_USER_INTERNAL_DISABLED = 32;\n\n // Recording was not started because user setting disabled automatic recording.\n public const int FLAG_RECORDING_REASON_USER_AUTOMATIC_DISABLED = 64;\n\n // Recording was not started because team setting disabled automatic recording.\n public const int FLAG_RECORDING_REASON_TEAM_AUTOMATIC_DISABLED = 128;\n\n // Recording was not started because user has overriden default.\n public const int FLAG_RECORDING_REASON_PREFERENCE_OVERRIDE = 256;\n\n // Recording was not started because they don't want internal, and this meeting was not scheduled/imported in time.\n public const int FLAG_RECORDING_REASON_USER_INTERNAL_DISABLED_UNSCHEDULED = 512;\n\n // Recording was not started because their team setting does excludes the meeting type.\n public const int FLAG_RECORDING_REASON_UNSUPPORTED_TYPE = 1024;\n\n // Recording was not started because the external provider disabled it (or recording is missing etc).\n public const int FLAG_RECORDING_REASON_EXTERNALLY_DISABLED = 2048;\n\n // Recording was stopped externally (\"exit-meeting\" Pusher event)\n public const int FLAG_RECORDING_REASON_STOPPED_EXTERNALLY = 384;\n\n // Recording couldn't be started due to Zoom hosting conflict error\n public const int FLAG_RECORDING_REASON_HOSTING_CONFLICT = 448;\n\n // meeting.failed event with reason code BOT_DENIED_FROM_LOBBY\n public const int FLAG_RECORDING_REASON_MEETING_BOT_DENIED_FROM_LOBBY = 4096;\n\n // meeting.failed event with reason code LOBBY_TIMEOUT\n public const int FLAG_RECORDING_REASON_MEETING_BOT_LOBBY_TIMEOUT = 8192;\n\n // meeting.failed event with reason code BOT_KICKED\n public const int FLAG_RECORDING_REASON_MEETING_BOT_KICKED = 16384;\n\n // meeting.failed event with reason code UNKNOWN\n public const int FLAG_RECORDING_REASON_MEETING_BOT_UNKNOWN = 32768;\n\n public const int FLAG_RECORDING_REASON_CONSENT_DENIED = 65536;\n\n // Invalid meeting (e.g. URL is invalid, or the meeting is not found)\n public const int FLAG_RECORDING_REASON_MEETING_BOT_INVALID = 131072;\n\n // The host stopped the recording.\n public const int FLAG_RECORDING_REASON_USER_STOPPED = 262144;\n\n // Recording was not started because an alternative vendor disabled it (or overrode it).\n public const int FLAG_RECORDING_REASON_VENDOR_OVERRIDE = 1048576;\n\n // Login required meeting.failed code\n public const int FLAG_RECORDING_REASON_LOGIN_REQUIRED = 524288;\n\n // Password for meeting was not provided - meeting.failed code\n public const int FLAG_RECORDING_REASON_MEETING_PASSWORD_NOT_PROVIDED = 2097152;\n\n // meeting.failed - when the meeting is locked\n public const int FLAG_RECORDING_REASON_MEETING_IS_LOCKED = 4194304;\n\n // max recording duration reached\n public const int FLAG_RECORDING_REASON_MAX_DURATION_REACHED = 8388608;\n\n // recording size is too small\n public const int FLAG_RECORDING_REASON_EMPTY_RECORDING = 16777216;\n\n // meeting.failed - when bot is redirected to sign in page multiple times\n public const int FLAG_RECORDING_REASON_MAX_RESTART_COUNT_IS_REACHED = 33554432;\n\n // meeting.failed event with reason code CONNECTION_LOST\n public const int FLAG_RECORDING_REASON_MEETING_BOT_CONNECTION_LOST = 67108864;\n\n // recording is corrupted.\n public const int FLAG_RECORDING_REASON_MEDIA_FILE_UNSUPPORTED_MIME_TYPE = 134217728;\n\n // meeting ended in lobby\n public const int FLAG_RECORDING_REASON_MEETING_ENDED_IN_LOBBY = 268435456;\n\n // meeting not started\n public const int FLAG_RECORDING_REASON_REASON_MEETING_NOT_STARTED = 536870912;\n\n // unfinished zoom custom disclaimer\n public const int FLAG_RECORDING_REASON_FEATURE_RULE_NOT_FOUND_ERROR = 1073741824;\n\n // recording download failed - server error\n public const int FLAG_RECORDING_REASON_SERVER_ERROR = 2147483648;\n\n // recording download failed - client code 404\n public const int FLAG_RECORDING_REASON_NOT_FOUND = 2147483649;\n\n // recording download failed - client code 401, 403\n public const int FLAG_RECORDING_REASON_ACCESS_DENIED = 2147483650;\n\n // recording download failed - client code 429\n public const int FLAG_RECORDING_REASON_TOO_MANY_REQUESTS = 2147483651;\n\n // recording download failed - unknown client error\n public const int FLAG_RECORDING_REASON_CLIENT_ERROR = 2147483652;\n\n // recording download failed - unknown error\n public const int FLAG_RECORDING_REASON_UNKNOWN_ERROR = 2147483653;\n\n // It has been setup ahead of time through calendar\n public const string STATUS_SCHEDULED = 'scheduled';\n\n // It is awaiting audio.\n public const string STATUS_PENDING = 'pending';\n\n // Participant(s) dialed in, awaiting organizer.\n public const string STATUS_RINGING = 'ringing';\n\n // Call is in progress.\n public const string STATUS_IN_PROGRESS = 'in-progress';\n\n // It has ended.\n public const string STATUS_COMPLETED = 'completed';\n\n // Cancelled prior to starting.\n public const string STATUS_CANCELLED = 'canceled';\n\n public const string STATUS_DUPLICATED = 'duplicated'; // duplicated conference\n\n public const string STATUS_STARTING_SOON = 'starting-soon';\n\n public const string STATUS_BOT_CREATE_SENT = 'bot-create-sent';\n\n public const string STATUS_BOT_INSTANCE_WORKER_ASSIGNED = 'worker-assigned';\n\n public const string STATUS_BOT_INSTANCE_STARTED = 'bot-started';\n\n // When bot instance is waiting in lobby\n public const string STATUS_BOT_INSTANCE_WAITING_LOBBY = 'bot-waiting';\n\n public const string STATUS_BUSY = 'busy';\n public const string STATUS_NO_ANSWER = 'no-answer';\n public const string STATUS_FAILED = 'failed'; // Used by SMS too\n\n // SMS related\n public const string STATUS_ACCEPTED = 'accepted';\n public const string STATUS_QUEUED = 'queued';\n public const string STATUS_SENDING = 'sending';\n public const string STATUS_SENT = 'sent';\n public const string STATUS_DELIVERED = 'delivered';\n public const string STATUS_UNDELIVERED = 'undelivered';\n public const string STATUS_RECEIVING = 'receiving';\n public const string STATUS_RECEIVED = 'received';\n public const string STATUS_RESENT = 'resent';\n\n public const array SMS_STATUSES = [\n Activity::STATUS_RECEIVED,\n Activity::STATUS_SENT,\n Activity::STATUS_DELIVERED,\n ];\n\n public const array SOFT_PHONE_CONFERENCE_STATUSES = [\n Activity::STATUS_IN_PROGRESS,\n Activity::STATUS_COMPLETED,\n ];\n\n // @todo refactor prefix from `TYPE_` to `CHANNEL_`\n public const string TYPE_SOFTPHONE = 'softphone';\n public const string TYPE_SOFTPHONE_INBOUND = 'softphone-inbound';\n public const string TYPE_CONFERENCE = 'conference';\n public const string TYPE_SMS_INBOUND = 'sms-inbound';\n public const string TYPE_SMS_OUTBOUND = 'sms-outbound';\n public const string TYPE_EMAIL_INBOUND = 'email-inbound';\n public const string TYPE_EMAIL_OUTBOUND = 'email-outbound';\n\n public const array CHANNELS = [\n self::TYPE_SOFTPHONE,\n self::TYPE_SOFTPHONE_INBOUND,\n self::TYPE_CONFERENCE,\n self::TYPE_SMS_INBOUND,\n self::TYPE_SMS_OUTBOUND,\n self::TYPE_EMAIL_INBOUND,\n self::TYPE_EMAIL_OUTBOUND,\n ];\n\n public const array PLAYABLE_CHANNELS = [\n self::TYPE_SOFTPHONE,\n self::TYPE_SOFTPHONE_INBOUND,\n self::TYPE_CONFERENCE,\n ];\n\n // Recording States\n public const string RECORDING_OFF = 'off'; // Default state\n public const string RECORDING_IN_PROGRESS = 'in-progress';\n public const string RECORDING_PAUSED = 'paused';\n public const string RECORDING_STOPPED = 'stopped'; // To never be resumed.\n public const string RECORDING_RECORDED = 'recorded'; // At least some portion of it was recorded.\n public const string RECORDING_FAILED = 'failed'; // Recording was attempted but failed for some reason.\n\n // Live Stream States\n public const int ON_AIR_DEFAULT = 0;\n public const int ON_AIR_READY = 1;\n public const int ON_AIR_PREPARING = 2;\n public const int ON_AIR_STREAMING = 3;\n public const int ON_AIR_FINISHED = 4;\n public const int ON_AIR_NOT_STREAMED = 5;\n public const int ON_AIR_ERROR = -1;\n\n public const string SOURCE_GONG = 'gong';\n public const string SOURCE_CHORUS = 'chorus';\n public const string SOURCE_OUTLOOK = 'outlook';\n public const string SOURCE_GOOGLE = 'google';\n\n // Activity Providers\n public const string PROVIDER_TWILIO = 'twilio'; // XXX: This is run via the Jiminny Provider.\n public const string PROVIDER_OUTREACH = 'outreach';\n public const string PROVIDER_ZOOM_BOT = 'zoom-bot';\n public const string PROVIDER_SALESLOFT = 'salesloft';\n public const string PROVIDER_GOOGLE = 'google';\n public const string PROVIDER_AIRCALL = 'aircall';\n public const string PROVIDER_JUSTCALL = 'justcall';\n public const string PROVIDER_GOOGLE_MEET = 'google-meet';\n public const string PROVIDER_GONG = 'gong';\n public const string PROVIDER_HUBSPOT = 'hubspot';\n public const string PROVIDER_CLOSE = 'close';\n public const string PROVIDER_TEAMS = 'ms-teams';\n public const string PROVIDER_SALESFORCE = 'salesforce';\n public const string PROVIDER_GROOVE = 'groove';\n public const string PROVIDER_XANT = 'xant';\n public const string PROVIDER_OFFICE = 'office';\n public const string PROVIDER_NATTERBOX = 'natterbox';\n public const string PROVIDER_RINGCENTRAL = 'ringcentral';\n public const string PROVIDER_RINGCENTRAL_VIDEO = 'ringcentral-video';\n public const string PROVIDER_GOTOMEETING = 'go-to-meeting';\n public const string PROVIDER_DEMODESK = 'demo-desk';\n public const string PROVIDER_DIALPAD = 'dialpad';\n public const string PROVIDER_ZOOM_PHONE = 'zoom-phone';\n public const string PROVIDER_CLOUDCALL = 'cloudcall';\n public const string PROVIDER_CLOUDCALL_US = 'cloudcall-us';\n public const string PROVIDER_EIGHT_BY_EIGHT = 'eight-by-eight'; // \"8x8\" UK\n public const string PROVIDER_EIGHT_BY_EIGHT_CA = 'eight-by-eight-ca'; // \"8x8\" Canada\n public const string PROVIDER_EIGHT_BY_EIGHT_AP = 'eight-by-eight-ap'; // \"8x8\" Australia\n public const string PROVIDER_EIGHT_BY_EIGHT_US_EAST = 'eight-by-eight-use'; // \"8x8\" US East\n public const string PROVIDER_EIGHT_BY_EIGHT_US_WEST = 'eight-by-eight-usw'; // \"8x8\" US West\n public const string PROVIDER_CONNECT_AND_SELL = 'connect-and-sell';\n public const string PROVIDER_CLOUD_TALK = 'cloud-talk';\n public const string PROVIDER_AMAZON_CONNECT = 'amazon-connect';\n public const string PROVIDER_VONAGE = 'vonage';\n public const string PROVIDER_MIGRATOR = 'migrator';\n public const string PROVIDER_UPLOADER = 'uploader';\n public const string PROVIDER_TALKDESK = 'talkdesk';\n public const string PROVIDER_TWILIO_FLEX = 'twilio-flex';\n public const string PROVIDER_TWILIO_FLEX_DIRECT = 'twilio-flex-direct';\n public const string PROVIDER_TWILIO_VIDEO = 'twilio-video';\n public const string PROVIDER_AVAYA = 'avaya';\n public const string PROVIDER_TELUS = 'telus';\n public const string PROVIDER_FIVE_NINE = 'five-nine';\n public const string PROVIDER_APOLLO = 'apollo';\n public const string PROVIDER_ORUM = 'orum';\n public const string PROVIDER_BLOOBIRDS = 'bloobirds';\n\n /**\n * @const API_PROVIDERS\n * A list of integrations that import calls via API instead of webhooks\n */\n public const array API_PROVIDERS = [\n self::PROVIDER_OUTREACH,\n self::PROVIDER_SALESLOFT,\n self::PROVIDER_HUBSPOT,\n self::PROVIDER_GROOVE,\n self::PROVIDER_XANT,\n self::PROVIDER_NATTERBOX,\n self::PROVIDER_CLOUDCALL,\n self::PROVIDER_CLOUDCALL_US,\n self::PROVIDER_EIGHT_BY_EIGHT,\n self::PROVIDER_EIGHT_BY_EIGHT_CA,\n self::PROVIDER_EIGHT_BY_EIGHT_AP,\n self::PROVIDER_EIGHT_BY_EIGHT_US_EAST,\n self::PROVIDER_EIGHT_BY_EIGHT_US_WEST,\n self::PROVIDER_CONNECT_AND_SELL,\n self::PROVIDER_CLOUD_TALK,\n self::PROVIDER_AMAZON_CONNECT,\n self::PROVIDER_VONAGE,\n self::PROVIDER_TALKDESK,\n self::PROVIDER_TWILIO_VIDEO,\n self::PROVIDER_TWILIO_FLEX,\n self::PROVIDER_TWILIO_FLEX_DIRECT,\n self::PROVIDER_FIVE_NINE,\n self::PROVIDER_APOLLO,\n self::PROVIDER_ORUM,\n self::PROVIDER_BLOOBIRDS,\n self::PROVIDER_RINGCENTRAL,\n self::PROVIDER_AVAYA,\n self::PROVIDER_TELUS,\n ];\n\n public const array FINITE_STATES = [\n self::TYPE_SOFTPHONE => [\n self::STATUS_COMPLETED,\n self::STATUS_FAILED,\n self::STATUS_NO_ANSWER,\n self::STATUS_BUSY,\n ],\n self::TYPE_SOFTPHONE_INBOUND => [\n self::STATUS_COMPLETED,\n self::STATUS_FAILED,\n self::STATUS_NO_ANSWER,\n self::STATUS_BUSY,\n ],\n self::TYPE_CONFERENCE => self::FINITE_STATES_CONFERENCE,\n ];\n\n public const array FINITE_STATES_CONFERENCE = [\n self::STATUS_COMPLETED,\n self::STATUS_FAILED,\n self::STATUS_CANCELLED,\n ];\n\n public const array MEETING_BOT_JOIN_ATTEMPTED = [\n self::STATUS_BOT_INSTANCE_WAITING_LOBBY,\n self::STATUS_BOT_INSTANCE_STARTED,\n ];\n\n public static array $enumStatuses = [\n self::STATUS_SCHEDULED,\n self::STATUS_PENDING,\n self::STATUS_RINGING,\n self::STATUS_IN_PROGRESS,\n self::STATUS_COMPLETED,\n self::STATUS_CANCELLED,\n self::STATUS_BUSY,\n self::STATUS_NO_ANSWER,\n self::STATUS_FAILED,\n self::STATUS_ACCEPTED,\n self::STATUS_QUEUED,\n self::STATUS_SENDING,\n self::STATUS_SENT,\n self::STATUS_RESENT,\n self::STATUS_DELIVERED,\n self::STATUS_UNDELIVERED,\n self::STATUS_RECEIVING,\n self::STATUS_RECEIVED,\n self::STATUS_BOT_INSTANCE_WAITING_LOBBY,\n self::STATUS_STARTING_SOON,\n self::STATUS_BOT_INSTANCE_WORKER_ASSIGNED,\n self::STATUS_BOT_INSTANCE_STARTED,\n self::STATUS_DUPLICATED,\n ];\n\n public static array $enumProviders = [\n self::PROVIDER_TWILIO,\n self::PROVIDER_OUTREACH,\n self::PROVIDER_ZOOM_BOT,\n self::PROVIDER_SALESLOFT,\n self::PROVIDER_AIRCALL,\n self::PROVIDER_JUSTCALL,\n self::PROVIDER_GOOGLE_MEET,\n self::PROVIDER_GONG,\n self::PROVIDER_HUBSPOT,\n self::PROVIDER_CLOSE,\n self::PROVIDER_TEAMS,\n self::PROVIDER_SALESFORCE,\n self::PROVIDER_GROOVE,\n self::PROVIDER_XANT,\n self::PROVIDER_GOOGLE,\n self::PROVIDER_OFFICE,\n self::PROVIDER_NATTERBOX,\n self::PROVIDER_RINGCENTRAL,\n self::PROVIDER_RINGCENTRAL_VIDEO,\n self::PROVIDER_GOTOMEETING,\n self::PROVIDER_DEMODESK,\n self::PROVIDER_DIALPAD,\n self::PROVIDER_ZOOM_PHONE,\n self::PROVIDER_CLOUDCALL,\n self::PROVIDER_CLOUDCALL_US,\n self::PROVIDER_EIGHT_BY_EIGHT,\n self::PROVIDER_EIGHT_BY_EIGHT_CA,\n self::PROVIDER_EIGHT_BY_EIGHT_AP,\n self::PROVIDER_EIGHT_BY_EIGHT_US_EAST,\n self::PROVIDER_EIGHT_BY_EIGHT_US_WEST,\n self::PROVIDER_CONNECT_AND_SELL,\n self::PROVIDER_CLOUD_TALK,\n self::PROVIDER_AMAZON_CONNECT,\n self::PROVIDER_VONAGE,\n self::PROVIDER_TALKDESK,\n self::PROVIDER_TWILIO_FLEX,\n self::PROVIDER_TWILIO_FLEX_DIRECT,\n self::PROVIDER_TWILIO_VIDEO,\n self::PROVIDER_AVAYA,\n self::PROVIDER_TELUS,\n self::PROVIDER_FIVE_NINE,\n self::PROVIDER_APOLLO,\n self::PROVIDER_ORUM,\n self::PROVIDER_BLOOBIRDS,\n ];\n\n public static $enumRecordingStates = [\n self::RECORDING_OFF, // Default state\n self::RECORDING_IN_PROGRESS,\n self::RECORDING_PAUSED,\n self::RECORDING_STOPPED,\n self::RECORDING_RECORDED,\n self::RECORDING_FAILED,\n ];\n\n // @Important:\n // This collection is not used anywhere, and is fully duplicated by the Channels const.\n // Validate if it is referred somehow via the enum trait, and if not, remove it entirely.\n // An even better strategy will be to move all those constants to a dedicated class\n protected array $enumTypes = [\n self::TYPE_SOFTPHONE,\n self::TYPE_SOFTPHONE_INBOUND,\n self::TYPE_CONFERENCE,\n self::TYPE_SMS_INBOUND,\n self::TYPE_SMS_OUTBOUND,\n self::TYPE_EMAIL_INBOUND,\n self::TYPE_EMAIL_OUTBOUND,\n ];\n\n protected static $enumFailedStatuses = [\n self::STATUS_NO_ANSWER,\n self::STATUS_FAILED,\n self::STATUS_BUSY,\n self::STATUS_CANCELLED,\n ];\n\n protected $table = 'activities';\n\n protected $fillable = [\n // Type of activity.\n 'type', // @todo refactor to `channel`\n // The activity type.\n 'playbook_category_id',\n // User who hosts the activity.\n 'user_id',\n // Related Lead record (if applicable)\n 'lead_id',\n // Related Account record (if applicable)\n 'account_id',\n // Related Contact record (if applicable)\n 'contact_id',\n // Related Opportunity record (if applicable)\n 'opportunity_id',\n // Stage of activity.\n 'stage_id',\n // Value of opportunity.\n 'value',\n // If the activity relates to a CRM task.\n 'crm_provider_id',\n // If the activity was created through an external device.\n 'device_id',\n // the activity's language code\n 'language',\n // transcription id\n 'transcription_id',\n // Duration of the call, with microseconds precision.\n 'duration',\n // One of enumStatuses above.\n 'status',\n // Have we reminded them to log the call?\n 'log_reminder_sent_at',\n // If activity is private or inter-org, flagged here.\n 'is_internal',\n // Managers and above can mark a call as private, to exclude it from other team members\n 'is_private',\n 'is_processed',\n // Boolean for this activity being instant invite handled.\n 'is_instant_invite',\n // If activity is in recording state, flagged here.\n 'recording_state',\n // If activity recording is overidden from default.\n 'recording_preference',\n // if recording did (not) happen, why that is\n 'recording_reason_code',\n // Average score, updated during\n 'average_score',\n // Summary that the organizer has taken after the call.\n 'summary',\n // Subject of the activity, usually taken from calendar event.\n 'title',\n // Description of the activity, usually taken from calendar event.\n 'description',\n // Start time, usually taken from calendar event.\n 'scheduled_start_time',\n // End time, usually taken from calendar event.\n 'scheduled_end_time',\n // When the call actually started.\n 'actual_start_time',\n // When the call actually ended.\n 'actual_end_time',\n // SMS: Message reference\n 'telephony_provider_id',\n // SMS: Participant who sent message\n 'from_participant_id',\n // SMS: Participant who should receive the message\n 'to_participant_id',\n // When an external guest joins an organizers meeting room and the organizer is not present,\n // send them an SMS notification that someone has joined.\n 'organizer_notified_at',\n // where was the activity imported from\n 'source',\n // The id in the source system (e.g. the bot id in Recall.ai)\n 'external_id',\n // The provider, by default it is twilio.\n 'provider',\n // Meeting location url\n 'location',\n // The snapshot for displaying a poster image.\n 'poster_path',\n 'crm_configuration_id',\n // If there is an automated message that the conversation is being recorded\n 'has_recording_prompt',\n // If the activity is being live-streamed\n 'on_air',\n 'calendar_event_id',\n ];\n\n protected $appends = [\n 'id_string',\n 'organizer',\n ];\n\n protected $hidden = [\n 'uuid',\n ];\n\n protected $visible = [\n 'id_string',\n 'type',\n 'duration',\n 'average_score',\n 'status',\n 'log_reminder_sent_at',\n 'title',\n 'description',\n 'is_internal',\n 'scheduled_start_time',\n 'scheduled_end_time',\n 'actual_start_time',\n 'actual_end_time',\n 'user',\n 'category',\n 'account',\n 'contact',\n 'opportunity',\n 'lead',\n 'stage',\n 'stats',\n 'participants',\n 'playlists',\n 'tracks',\n 'comments',\n 'plays',\n 'coachingFeedbacks',\n 'shares',\n 'favorites',\n 'language',\n 'transcription',\n 'is_private',\n 'is_instant_invite',\n 'on_air',\n 'calendar_event_id',\n ];\n\n protected function casts(): array\n {\n return [\n 'scheduled_start_time' => 'datetime',\n 'scheduled_end_time' => 'datetime',\n 'actual_start_time' => 'datetime',\n 'actual_end_time' => 'datetime',\n 'organizer_notified_at' => 'datetime',\n 'log_reminder_sent_at' => 'datetime',\n 'is_internal' => 'boolean',\n 'duration' => 'integer',\n 'average_score' => 'decimal:2',\n 'is_private' => 'boolean',\n 'is_processed' => 'boolean',\n 'is_instant_invite' => 'boolean',\n 'value' => 'decimal:2',\n 'recording_preference' => 'boolean',\n 'recording_reason_code' => 'integer',\n 'has_recording_prompt' => 'boolean',\n 'on_air' => 'integer',\n ];\n }\n\n protected static function boot()\n {\n parent::boot();\n\n static::updated(static function (Activity $activity) {\n // If activity is about to start (pending, ringing, in-progress) or event is scheduled in less than 1 week\n if (in_array($activity->status, [Activity::STATUS_PENDING, Activity::STATUS_RINGING, Activity::STATUS_IN_PROGRESS], true) ||\n ($activity->scheduled_start_time && (int) $activity->scheduled_start_time->diffInWeeks(new Carbon(), true) < 1)) {\n if ($activity->isDirty('status')) {\n event(new StatusUpdated($activity));\n }\n\n if ($activity->isDirty('stage_id')) {\n event(new StageUpdated($activity));\n }\n\n if ($activity->isDirty(['lead_id', 'account_id', 'contact_id'])) {\n event(new ProspectUpdated($activity));\n }\n\n if ($activity->isDirty('opportunity_id')) {\n event(new ActivityUpdated($activity, 'activity.opportunity-updated', Auth::user()));\n }\n\n if ($activity->isDirty('title')) {\n event(new TitleUpdated($activity));\n }\n }\n\n if ($activity->isDirty('playbook_category_id')) {\n event(new ActivityTypeUpdated($activity));\n }\n });\n\n static::deleted(static function (Activity $activity) {\n // Hard delete associated playlistActivities\n $activity->playlistActivities()->delete();\n });\n }\n\n public function getOrganizerAttribute(): ?Participant\n {\n $participant = $this->participants()->where('user_id', $this->user_id)->first();\n\n if (! $participant instanceof Participant && $participant !== null) {\n throw new RuntimeException(sprintf('$participant must be an instance of \"%s\" or null', Participant::class));\n }\n\n return $participant;\n }\n\n public function getFormattedValueAttribute()\n {\n $currencyCode = 'USD';\n if ($this->opportunity) {\n $currencyCode = $this->opportunity->getCurrencyCode();\n }\n\n $formatter = new CurrencyFormatter();\n $formatter->setTextAttribute(NumberFormatter::CURRENCY_CODE, $currencyCode);\n $formatter->setAttribute(NumberFormatter::MAX_FRACTION_DIGITS, 0);\n\n return $formatter->format($this->value, $currencyCode);\n }\n\n public function getProspectNameAttribute(): ?string\n {\n $prospectName = null;\n\n if ($this->lead_id) {\n $prospectName = $this->lead->name;\n } elseif ($this->contact_id) {\n $prospectName = $this->contact->name;\n } elseif ($this->account_id) {\n $prospectName = $this->account->name;\n }\n\n return $prospectName;\n }\n\n public function getProspectName(): ?string\n {\n /** @var string|null */\n return $this->getAttribute('prospect_name');\n }\n\n /**\n * Get activity title depending on prospect or title\n */\n public function getActivityTitleAttribute(): ?string\n {\n $activityTitle = null;\n if ($this->prospect && $this->prospect->getName()) {\n if ($this->account_id) {\n $activityTitle = $this->account->name;\n } elseif ($this->lead_id) {\n $activityTitle = $this->lead->company;\n } elseif ($this->contact_id) {\n $activityTitle = $this->contact->account ? $this->contact->account->name : $this->contact->name;\n }\n } elseif ($this->title) {\n $activityTitle = $this->title;\n }\n\n return $activityTitle;\n }\n\n public function wasRecentlyCreated(): bool\n {\n return $this->wasRecentlyCreated;\n }\n\n public function getProspectTypeAttribute()\n {\n $prospectType = null;\n\n if ($this->lead_id) {\n $prospectType = 'Lead';\n } elseif ($this->contact_id) {\n $prospectType = 'Contact';\n } elseif ($this->account_id) {\n $prospectType = 'Account';\n }\n\n return $prospectType;\n }\n\n /**\n * Return the best match for prospect. Results are in the following order of priority:\n * 1. Lead\n * 2. Contact\n * 3. Account\n * 4. NULL\n */\n public function getProspectAttribute(): ?ProspectInterface\n {\n if ($this->hasLead()) {\n return $this->getLead();\n }\n\n if ($this->hasContact()) {\n return $this->getContact();\n }\n\n if ($this->hasAccount()) {\n return $this->getAccount();\n }\n\n return null;\n }\n\n public function getTitleAttribute($value): ?string\n {\n return \\getActivityTitleAttribute(\n $this->user->name,\n $this->getType(),\n $value,\n $this->prospect->name ?? null,\n $this->from->national_phone_number ?? null\n );\n }\n\n public function getTitle(): ?string\n {\n return $this->getAttribute('title');\n }\n\n public function getSummary(): ?string\n {\n return $this->getAttribute('summary');\n }\n\n public function isInternal(): bool\n {\n return $this->getAttribute('is_internal');\n }\n\n public function getIsPrivate(): bool\n {\n return $this->getAttribute('is_private');\n }\n\n public function getDescription(): ?string\n {\n return $this->getAttribute('description');\n }\n\n public function hasTitle(): bool\n {\n return $this->getOriginal('title') !== null;\n }\n\n public function getPlayCountAttribute()\n {\n return $this->getPlaysCountAttribute();\n }\n\n public function getPlaysCountAttribute()\n {\n if (! isset($this->attributes['plays_count'])) {\n $this->loadCount('plays');\n }\n\n return $this->attributes['plays_count'];\n }\n\n public function getCommentCountAttribute()\n {\n return $this->getCommentsCountAttribute();\n }\n\n public function getCommentsCountAttribute()\n {\n if (! isset($this->attributes['comments_count'])) {\n $this->loadCount('comments');\n }\n\n return $this->attributes['comments_count'];\n }\n\n public function getVisibleCommentsCountAttribute()\n {\n if (! isset($this->attributes['visible_comments_count'])) {\n $activityCommentsService = app(ActivityCommentService::class);\n $user = Auth::user() instanceof User ? Auth::user() : null;\n $this->attributes['visible_comments_count'] = $activityCommentsService\n ->getVisibleCommentsCount($this, $user);\n }\n\n return $this->attributes['visible_comments_count'];\n }\n\n public function getShareCountAttribute()\n {\n return $this->getSharesCountAttribute();\n }\n\n public function getSharesCountAttribute()\n {\n if (! isset($this->attributes['shares_count'])) {\n $this->loadCount('shares');\n }\n\n return $this->attributes['shares_count'];\n }\n\n\n /**\n * Get the count of favorites playlists this activity appears in\n */\n public function getFavoriteCountAttribute(): int\n {\n return $this->getFavoritesCountAttribute();\n }\n\n public function getFavoritesCountAttribute()\n {\n if (! isset($this->attributes['favorites_count'])) {\n $this->loadCount('favorites');\n }\n\n return $this->attributes['favorites_count'];\n }\n\n public function getActiveParticipantsCountAttribute()\n {\n if (! isset($this->attributes['active_participants_count'])) {\n $this->loadCount('activeParticipants');\n }\n\n return $this->attributes['active_participants_count'];\n }\n\n public function getTracksWithTelephonyCountAttribute()\n {\n if (! isset($this->attributes['tracks_with_telephony_count'])) {\n $this->loadCount('tracksWithTelephony');\n }\n\n return $this->attributes['tracks_with_telephony_count'];\n }\n\n /**\n * @TEMP\n * $this->loadCount('tracksWithTelephony') throws null pointer exception\n */\n public function countTracksWithTelephony(): int\n {\n return $this->tracks()->whereNotNull('telephony_provider_id')->count();\n }\n\n public function getDuration(): float\n {\n return $this->getAttribute('duration');\n }\n\n public function getDurationForHumansAttribute()\n {\n return Carbon::now()->subSeconds($this->duration)->diffForHumans(now(), true);\n }\n\n public function getDurationForHumansShortAttribute(): string\n {\n return Carbon::now()->subSeconds($this->duration)->diffForHumans(now(), true, true);\n }\n\n public function hasRecordingPreference(): bool\n {\n return $this->getAttribute('recording_preference') !== null;\n }\n\n public function getRecordingPreference()\n {\n return $this->getAttribute('recording_preference');\n }\n\n /** @return BelongsTo<User, self> */\n public function user(): BelongsTo\n {\n return $this->belongsTo(User::class)->with('group');\n }\n\n public function device()\n {\n return $this->belongsTo(Device::class);\n }\n\n public function category()\n {\n return $this->belongsTo(PlaybookCategory::class, 'playbook_category_id');\n }\n\n public function getCategory(): ?PlaybookCategory\n {\n return $this->getAttribute('category');\n }\n\n public function getPlaybookCategoryId(): ?int\n {\n return $this->getAttribute('playbook_category_id');\n }\n\n public function hasStats(): bool\n {\n return $this->getAttribute('stats') !== null;\n }\n\n public function getStats(): ?Stats\n {\n return $this->getAttribute('stats');\n }\n\n public function stats(): HasOne\n {\n return $this->hasOne(Stats::class);\n }\n\n public function participantStats(): Eloquent\\Relations\\HasManyThrough\n {\n return $this->hasManyThrough(\n Models\\Participant\\ParticipantStats::class,\n Participant::class,\n 'activity_id',\n 'participant_id'\n );\n }\n\n public function getParticipantStats(): Eloquent\\Collection\n {\n return $this->getAttribute('participantStats');\n }\n\n public function account()\n {\n return $this->belongsTo(Account::class);\n }\n\n public function contact()\n {\n return $this->belongsTo(Contact::class)->with(['account']);\n }\n\n public function lead()\n {\n return $this->belongsTo(Lead::class)->with(['stage', 'recordType']);\n }\n\n /**\n * @return BelongsTo<Opportunity, self>\n */\n public function opportunity(): BelongsTo\n {\n /** @var BelongsTo<Opportunity, self> */\n return $this->belongsTo(Opportunity::class);\n }\n\n public function stage()\n {\n return $this->belongsTo(Stage::class);\n }\n\n /**\n * @return HasMany<Session>\n */\n public function sessions(): HasMany\n {\n return $this->hasMany(Session::class);\n }\n\n /**\n * @return HasMany|ParticipantSpeech[]|Eloquent\\Collection\n */\n public function participantSpeeches()\n {\n return $this->hasMany(ParticipantSpeech::class);\n }\n\n public function getParticipantSpeeches(): Eloquent\\Collection\n {\n return $this->getAttribute('participantSpeeches');\n }\n\n /**\n * @return HasMany|Log[]|Eloquent\\Collection\n */\n public function logs()\n {\n return $this->hasMany(Log::class);\n }\n\n /**\n * @return HasMany|Moment[]|Eloquent\\Collection\n */\n public function moments()\n {\n return $this->hasMany(Moment::class);\n }\n\n /**\n * @return HasMany|Note[]|Eloquent\\Collection\n */\n public function notes()\n {\n return $this->hasMany(Note::class);\n }\n\n /**\n * @return Eloquent\\Collection|Note[]\n */\n public function getNotes(): Eloquent\\Collection\n {\n return $this->getAttribute('notes');\n }\n\n /**\n * @return HasMany|Message[]|Eloquent\\Collection\n */\n public function messages()\n {\n return $this->hasMany(Message::class);\n }\n\n public function coachingMessages(): HasMany\n {\n return $this->hasMany(Message::class)\n ->where('is_private', 1);\n }\n\n public function getCoachingMessages(): Eloquent\\Collection\n {\n return $this->getAttribute('coachingMessages');\n }\n\n public function participants(): HasMany\n {\n return $this->hasMany(Participant::class);\n }\n\n public function getSnapshots(): Eloquent\\Collection\n {\n return $this->getAttribute('snapshots');\n }\n\n /** @return HasMany<Track> */\n public function tracks(): HasMany\n {\n return $this->hasMany(Track::class);\n }\n\n public function tracksWithTelephony(): HasMany\n {\n return $this->hasMany(Track::class)->whereNotNull('telephony_provider_id');\n }\n\n public function getTracksWithTelephony(): Eloquent\\Collection\n {\n return $this->getAttribute('tracksWithTelephony');\n }\n\n /** @return Collection|Track[] */\n public function getTracks(): Eloquent\\Collection\n {\n return $this->getAttribute('tracks');\n }\n\n public function masterTrack(): HasOne\n {\n return $this->hasOne(Track::class)->where('is_master', 1)\n ->whereIn('format', [Track::FORMAT_WAV, Track::FORMAT_M3U8])\n ->latest();\n }\n\n public function getMasterTrack(): ?Track\n {\n /** @var Track|null */\n return $this->getAttribute('masterTrack');\n }\n\n public function transcription(): Eloquent\\Relations\\BelongsTo\n {\n return $this->belongsTo(Transcription::class, 'transcription_id');\n }\n\n public function findTranscriptionPromptSummaries(): Collection\n {\n $transcriptionId = $this->getTranscriptionId();\n if (is_null($transcriptionId)) {\n return new Collection();\n }\n\n return Models\\AiPrompt::query()\n ->where('transcription_id', $transcriptionId)\n ->get();\n }\n\n public function getTranscription(): Transcription\n {\n return $this->getAttribute('transcription');\n }\n\n public function hasTranscription(): bool\n {\n return $this->getAttribute('transcription') !== null;\n }\n\n public function setTranscriptionId(int $transcriptionId): Activity\n {\n $this->setAttribute('transcription_id', $transcriptionId);\n\n return $this;\n }\n\n public function unsetTranscriptionId(): self\n {\n $this->setAttribute('transcription_id', null);\n\n return $this;\n }\n\n public function getTranscriptionId(): ?int\n {\n return $this->getAttribute('transcription_id');\n }\n\n /** @deprecated */\n public function hasTranscriptionId(): bool\n {\n return $this->getAttribute('transcription_id') !== null;\n }\n\n public function coachRequests()\n {\n return $this->hasMany(CoachRequest::class);\n }\n\n public function availabilityNotifications()\n {\n return $this->hasMany(AvailabilityNotification::class);\n }\n\n public function processingStates()\n {\n return $this->hasMany(Models\\Activity\\ActivityProcessingState::class);\n }\n\n public function uploadSettings()\n {\n return $this->hasMany(ActivityUploadSetting::class);\n }\n\n public function comments()\n {\n return $this->hasMany(Comment::class);\n }\n\n public function getComments(): Eloquent\\Collection\n {\n return $this->getAttribute('comments');\n }\n\n public function visibleComments()\n {\n $rel = $this->hasMany(Comment::class);\n // Doesn't have auth()->user() in some tests, breaks the build\n if ($user = auth()->user()) {\n return $rel->visibleThreads($user->id);\n }\n\n return $rel;\n }\n\n public function snapshots(): HasMany\n {\n return $this->hasMany(Snapshot::class);\n }\n\n public function calendarEvent()\n {\n return $this->belongsTo(CalendarEvent::class);\n }\n\n public function getCalendarEvent(): ?CalendarEvent\n {\n return $this->getAttribute('calendarEvent');\n }\n\n public function latestCoachingFeedbacks(): HasMany\n {\n return $this->hasMany(CoachingFeedback::class)->latest();\n }\n\n public function playlists(): BelongsToMany\n {\n return $this->belongsToMany(Playlist::class, 'playlist_activities')\n ->withPivot('id', 'uuid', 'user_id', 'start_time', 'end_time')\n ->using(PlaylistActivity::class)\n ->withTimestamps();\n }\n\n public function coachingFeedbacks(): HasMany\n {\n return $this->hasMany(CoachingFeedback::class);\n }\n\n /**\n * @return Eloquent\\Collection|CoachingFeedback[]\n */\n public function getCoachingFeedback(?int $visibility = null): Eloquent\\Collection\n {\n $feedbacks = $this->coachingFeedbacks();\n if ($visibility !== null) {\n $feedbacks = $feedbacks->where('visibility', $visibility);\n }\n\n return $feedbacks->get();\n }\n\n /** @return Eloquent\\Collection<int, PlaylistActivity> */\n public function favoritedBy(User $user): Eloquent\\Collection\n {\n return $this->favorites()->where('user_id', $user->getId())->get();\n }\n\n /**\n * Checks whether consumer has added this activity to their favorites playlist\n * In addition a default playlist gets created if not already present\n */\n public function wasFavoritedBy(User $user): bool\n {\n $playlist = $user->favoritePlaylist();\n\n return $playlist\n ->activities()\n ->where('activity_id', '=', $this->getId())\n ->exists();\n }\n\n /**\n * @return HasMany<PlaylistActivity>\n */\n public function playlistActivities(): HasMany\n {\n return $this->hasMany(PlaylistActivity::class);\n }\n\n /**\n * @return HasManyThrough<Playlist>\n */\n public function favoritePlaylists(): HasManyThrough\n {\n return $this->hasManyThrough(\n Playlist::class,\n PlaylistActivity::class,\n 'activity_id',\n 'id',\n 'id',\n 'playlist_id'\n )->where('is_default', 1);\n }\n\n /**\n * @return Eloquent\\Collection<int, Playlist>\n */\n public function getFavoritePlaylists(): Eloquent\\Collection\n {\n return $this->getAttribute('favoritePlaylists');\n }\n\n /**\n * Get activities from the default/favorite playlist\n *\n * @return Eloquent\\Builder|static\n */\n public function favorites()\n {\n return $this->playlistActivities()->whereHas('playlist', function ($query) {\n $query->where('is_default', 1);\n });\n }\n\n /**\n * @return Model|SubscriptionSet|null|object\n */\n public function subscribedBy(User $user)\n {\n if ($this->prospect === null) {\n return null;\n }\n\n return SubscriptionSet::select('activity_subscription_sets.*')\n ->where('user_id', $user->id)\n ->join('activity_subscriptions', function ($join) {\n $join\n ->on('subscription_set_id', '=', 'activity_subscription_sets.id');\n\n if ($this->account_id) {\n if ($this->opportunity_id) {\n $join\n ->where('followable_type', 'opportunity')\n ->where('followable_id', $this->opportunity_id);\n } else {\n $join\n ->where('followable_type', 'account')\n ->where('followable_id', $this->account_id);\n }\n } elseif ($this->contact_id) {\n $join\n ->where('followable_type', 'contact')\n ->where('followable_id', $this->contact_id);\n } elseif ($this->lead_id) {\n $join\n ->where('followable_type', 'lead')\n ->where('followable_id', $this->lead_id);\n }\n })\n ->first();\n }\n\n /**\n * @return array|Eloquent\\Builder[]|Eloquent\\Collection|SubscriptionSet[]\n */\n public function subscribers()\n {\n if ($this->prospect === null) {\n return [];\n }\n\n return SubscriptionSet::with(['subscriptions', 'user'])\n ->whereHas('subscriptions', function ($query) {\n if ($this->account_id) {\n if ($this->opportunity_id) {\n $query\n ->where('followable_type', 'opportunity')\n ->where('followable_id', $this->opportunity_id);\n } else {\n $query\n ->where('followable_type', 'account')\n ->where('followable_id', $this->account_id);\n }\n } elseif ($this->contact_id) {\n $query\n ->where('followable_type', 'contact')\n ->where('followable_id', $this->contact_id);\n } elseif ($this->lead_id) {\n $query\n ->where('followable_type', 'lead')\n ->where('followable_id', $this->lead_id);\n } else {\n // Nothing to join on?\n // refactor - use Jiminny specific exception\n throw new InvalidArgumentException('Cannot join on a specific customer filter.');\n }\n })\n ->whereHas('user', function ($query) {\n $query\n ->where('team_id', $this->user->team_id)\n ->where('status', User::STATUS_ACTIVE);\n })\n ->get();\n }\n\n /**\n * @return HasMany|Builder|Eloquent\\Collection|Play[]\n */\n public function plays()\n {\n return $this->hasMany(Play::class);\n }\n\n public function getPlays(): Eloquent\\Collection\n {\n return $this->getAttribute('plays');\n }\n\n public function playsBy(User $user)\n {\n /** @var Builder $builder */\n $builder = $this->plays()->where('user_id', $user->id);\n\n return $builder->get();\n }\n\n /**\n * Check if activity was played by a user\n */\n public function wasPlayedBy(User $user): bool\n {\n return $this->plays()->where('user_id', $user->id)->exists();\n }\n\n public function shares()\n {\n return $this->hasMany(Share::class);\n }\n\n /** @return BelongsTo<Participant, self> */\n public function from(): BelongsTo\n {\n return $this->belongsTo(Participant::class, 'from_participant_id');\n }\n\n /** @return BelongsTo<Participant, self> */\n public function to(): BelongsTo\n {\n return $this->belongsTo(Participant::class, 'to_participant_id');\n }\n\n /**\n * Get all of the connections through the participants.\n */\n public function connections()\n {\n return $this->hasManyThrough(\n Connection::class,\n Participant::class,\n 'activity_id',\n 'participant_id'\n );\n }\n\n public function getConnections(): Eloquent\\Collection\n {\n return $this->getAttribute('connections');\n }\n\n /**\n * Get all of the shares through the participants.\n */\n public function participantShares()\n {\n return $this->hasManyThrough(\n Participant\\Share::class,\n Participant::class,\n 'activity_id',\n 'participant_id'\n );\n }\n\n public function getParticipantShares(): Eloquent\\Collection\n {\n return $this->getAttribute('participantShares');\n }\n\n public function topicTriggers(): HasMany\n {\n return $this->hasMany(TopicTrigger::class);\n }\n\n public function activityScorecardRuleTriggers(): HasMany\n {\n return $this->hasMany(Models\\Scorecard\\ActivityScorecardRuleTrigger::class);\n }\n\n public function activityScorecardRules(): HasMany\n {\n return $this->hasMany(Models\\Scorecard\\ActivityScorecardRule::class);\n }\n\n public function questions(): HasMany\n {\n return $this->hasMany(Question::class);\n }\n\n /**\n * Get all the custom data attached to it.\n */\n public function data(): HasMany\n {\n return $this->hasMany(FieldData::class);\n }\n\n public function getData(): Eloquent\\Collection\n {\n /** @var Eloquent\\Collection */\n return $this->getAttribute('data');\n }\n\n #[Scope]\n protected function heldBetween($query, Carbon $start, Carbon $end)\n {\n // Sanity check.\n $from = min($start, $end);\n $until = max($start, $end);\n\n return $query\n ->where('actual_start_date', '>=', $from)\n ->where('actual_end_date', '<=', $until);\n }\n\n #[Scope]\n protected function scheduledBetween($query, Carbon $start, Carbon $end)\n {\n // Sanity check.\n $from = min($start, $end);\n $until = max($start, $end);\n\n return $query\n ->where('scheduled_start_date', '>=', $from)\n ->where('scheduled_end_date', '<=', $until);\n }\n\n /**\n * @param Builder<self> $query\n *\n * @return Builder<self>\n */\n #[Scope]\n protected function forTeam(Builder $query, int $teamId): Builder\n {\n /** @var Builder<self> */\n return $query->whereHas('user', static function (Builder $query) use ($teamId): void {\n $query->where('team_id', $teamId);\n });\n }\n\n /**\n * @param Builder<self> $query\n *\n * @return Builder<self>\n */\n #[Scope]\n protected function inOpenDeals(Builder $query): Builder\n {\n /** @var Builder<self> */\n return $query->whereHas(\n 'opportunity',\n static fn (Builder $query): Builder => $query\n ->where('is_closed', false)\n ->where('deleted_at', '=', null),\n );\n }\n\n /**\n * @param Builder<self> $query\n *\n * @return Builder<self>\n */\n #[Scope]\n protected function notInOpenDeals(Builder $query): Builder\n {\n /** @var Builder<self> */\n return $query->where(\n static fn (Builder $query): Builder => $query->whereNull('opportunity_id')\n ->orWhereHas(\n 'opportunity',\n static fn (Builder $query): Builder => $query->where('is_closed', true),\n )\n ->orWhereHas(\n 'opportunity',\n static fn (Builder $query): Builder => $query->withTrashed()->where('deleted_at', '!=', null),\n ),\n );\n }\n\n /**\n * Finds a participant and updates it with data. If participant doesn't exist creates a new participant from data.\n *\n * @param array $data participant data used to identify the participant and update it\n * @param bool $enterRoom true if participant is entering the room. false if we just want to update some participant data\n * @param Carbon|null $enterTime if $enterNow is true then this is the join time when the actual enter has occurred\n */\n public function updateOrCreateParticipant(\n array $data,\n bool $enterRoom = true,\n ?Carbon $enterTime = null,\n bool $nameMatching = false,\n ): Participant {\n $search = [];\n $participant = null;\n\n if (isset($data['user_id'])) {\n // Check if they already exist based on their ID.\n $search['user_id'] = $data['user_id'];\n } elseif (isset($data['provider_id'])) {\n $search['provider_id'] = $data['provider_id'];\n } elseif ($nameMatching && isset($data['name'])) {\n $search['name'] = $data['name'];\n }\n\n if (! empty($data['email'])) {\n $search['email'] = $data['email'];\n\n // If we have their email, this should be unique enough to lookup (e.g. calendar event based participant).\n unset($search['provider_id']);\n }\n\n // Search by phone number only in case nothing else is available to search by.\n if (array_key_exists('phone_number', $data) && empty($search)) {\n $search['phone_number'] = $data['phone_number'];\n }\n\n if (! empty($search)) {\n // Do a lookup now to see if we have a match on the provided data.\n $lookup = array_map(static function ($key, $value): array {\n return [$key, $value];\n }, array_keys($search), $search);\n\n $participant = $this->participants()->withTrashed()->where($lookup)->first();\n }\n\n // Do a partial match on the name and search in the team members.\n if (! $participant instanceof Participant && $nameMatching && ! empty($data['name'])) {\n $participantMatcher = app(MeetingBot\\Service\\ParticipantMatcher::class);\n\n if (! $participantMatcher instanceof MeetingBot\\Service\\ParticipantMatcher) {\n throw new LogicException('Expecting ParticipantMatcher service instance');\n }\n\n $participant = $participantMatcher->match($this, $data['name']);\n\n // If we've found good participant, avoid data overwrite in `$participant->fill($data)` below.\n if ($participant instanceof Models\\Participant && $participant->hasName()) {\n unset($data['name']); // Thoughts: should we unset also $data['user_id'] and $data['email'] ?\n }\n }\n\n if (! $participant instanceof Participant) {\n // If no match, create a new participant.\n if (empty($search)) {\n $participant = $this->participants()->create();\n } else {\n // If no match, create a new participant but avoid creating duplicates\n $participant = $this->participants()->withTrashed()->firstOrNew($search);\n }\n }\n\n // If we have just recycled a deleted participant\n if ($participant->trashed()) {\n $participant->deleted_at = null;\n }\n\n // Deal with the case when calendar syncs the event while it's in progress.\n // We should prevent change of the participant name, because speeches mapping will fail.\n if ($enterRoom === false\n && $this->isInProgress()\n && $participant->hasName()\n && isset($data['name'])\n && $data['name'] !== $participant->getName()\n ) {\n unset($data['name']);\n }\n\n // Upsert with new data.\n $participant->fill($data);\n\n if ($enterRoom) {\n if ($enterTime === null) {\n $enterTime = now();\n }\n\n // Participant enters room for the first time\n if ($participant->enter_time === null) {\n $participant->enter_time = $enterTime;\n }\n\n // If there is an exit time and it's prior to new enter_time\n if ($participant->exit_time && $participant->exit_time->lt($enterTime)) {\n // Participant has re-joined\n $participant->exit_time = null;\n }\n }\n\n $participant->save();\n\n return $participant;\n }\n\n /**\n * Updates participant CRM data\n *\n * @param array{\n * Lead|null,\n * Account|null,\n * Opportunity|null,\n * Contact|null,\n * Stage|null,\n * string|null\n *} $records\n * @param Participant $participant participant the CRM data is associated with\n */\n public function updateParticipantCrmData(array $records, Participant $participant): void\n {\n // Extract the records.\n [$lead, , , $contact] = $records;\n\n $resolver = $this->getUpdateCrmDataResolver();\n $strategy = $resolver->resolveForParticipant($lead, $contact);\n\n if ($strategy == UpdateCrmDataByStrategy::Lead) {\n if (! $participant->hasName()) {\n $participant->name = $lead->name;\n }\n\n if (! $participant->hasEmailAddress()) {\n $participant->email = $lead->email;\n }\n\n if (! $participant->hasPhoneNumber()) {\n $participant->phone_number = $lead->phone;\n }\n\n $participant->lead_id = $lead->id;\n $participant->save();\n } elseif ($strategy == UpdateCrmDataByStrategy::Contact) {\n if (! $participant->hasName()) {\n $participant->name = $contact->name;\n }\n\n if (! $participant->hasEmailAddress()) {\n $participant->email = $contact->email;\n }\n\n if (! $participant->hasPhoneNumber()) {\n $participant->phone_number = $contact->phone;\n }\n\n $participant->contact_id = $contact->id;\n $participant->save();\n }\n }\n\n /**\n * Updates activity CRM data\n *\n * @param array{\n * Lead|null,\n * Account|null,\n * Opportunity|null,\n * Contact|null,\n * Stage|null,\n * string|null\n *} $records\n */\n public function updateActivityCrmData(array $records): void\n {\n // Extract the records.\n [$lead, $account, $opportunity, $contact, $stage] = $records;\n\n $resolver = $this->getUpdateCrmDataResolver();\n $strategy = $resolver->resolveForActivity($lead, $contact, $account);\n\n if ($strategy == UpdateCrmDataByStrategy::Lead) {\n // Also update the parent activity if required, checking we don't create a mixed lead/account record.\n if ($this->account_id === null && $this->contact_id === null && $this->lead_id === null) {\n $this->lead_id = $lead->id;\n\n if ($this->stage_id === null && $stage) {\n $this->stage_id = $stage->id;\n }\n\n $this->save();\n }\n } elseif ($strategy == UpdateCrmDataByStrategy::Contact) {\n // Also update the parent activity if required, checking we don't create a mixed lead/account record.\n $this->lead_id = null;\n if ($this->stage && $this->stage->getType() === Stage::TYPE_LEAD) {\n $this->stage_id = null;\n }\n\n // Don't trust previous matched account_id as it might have been changed in the CRM\n if ($account && $account->id !== $this->account_id) {\n $this->account_id = $account->id;\n }\n\n if ($opportunity && $this->opportunity_id !== $opportunity->id) {\n $this->opportunity_id = $opportunity->id;\n // Reset stage_id when opportunity changes to avoid stale stage from previous opportunity match\n $this->stage_id = $stage?->id;\n }\n\n if ($this->stage_id === null && $stage) {\n $this->stage_id = $stage->id;\n }\n\n if ($opportunity && $this->value !== $opportunity->value) {\n $this->value = $opportunity->value;\n }\n\n // Always set contact_id when available, regardless of account_id status\n if ($this->contact_id === null && $contact) {\n $this->contact_id = $contact->id;\n }\n\n $this->save();\n } elseif ($strategy == UpdateCrmDataByStrategy::Account && $this->account_id === null) {\n // Also update the parent activity if required, checking we don't create a mixed lead/account record.\n $this->lead_id = null;\n if ($this->stage && $this->stage->getType() === Stage::TYPE_LEAD) {\n $this->stage_id = null;\n }\n\n // Update the account and opportunity on the activity record if possible.\n $this->account_id = $account->id;\n\n if ($this->stage_id === null && $stage) {\n $this->stage_id = $stage->id;\n }\n\n if ($this->opportunity_id === null && $opportunity) {\n $this->opportunity_id = $opportunity->id;\n $this->value = $opportunity->value;\n }\n\n $this->save();\n }\n }\n\n public function getActivityProspectData(): array\n {\n return [\n 'lead' => $this->lead_id,\n 'contact' => $this->contact_id,\n 'account' => $this->account_id,\n 'opportunity' => $this->opportunity_id,\n 'stage' => $this->stage_id,\n ];\n }\n\n public function isOrganizer(User $user): bool\n {\n return $this->user_id && $this->user_id === $user->id;\n }\n\n public function isJoinable(): bool\n {\n return \\in_array($this->status, [\n self::STATUS_SCHEDULED,\n self::STATUS_PENDING,\n self::STATUS_RINGING,\n self::STATUS_IN_PROGRESS,\n ], true);\n }\n\n public function isAttemptedForBotJoin(): bool\n {\n return in_array($this->getAttribute('status'), self::MEETING_BOT_JOIN_ATTEMPTED, true);\n }\n\n /**\n * Check if the activity can be saved to CRM (manual or autolog)\n */\n public function isLoggable(): bool\n {\n if ($this->getUser()->getTeam()->hasFeature(FeatureEnum::SIDEKICK_SETTINGS)) {\n $sidekickService = app(SidekickService::class);\n\n if (! $sidekickService->isSidekickEnabledForUser($this->getUser())) {\n return false;\n }\n }\n\n // If we don't know the activity type, don't try to log.\n if ($this->playbook_category_id === null) {\n return false;\n }\n\n if ($this->user->crm_required === false) {\n return false;\n }\n\n // Don't prompt for internal meetings.\n if ($this->is_internal) {\n return false;\n }\n\n // If we don't know who we are trying to log to, don't try to log.\n if ($this->prospect === null) {\n return false;\n }\n\n $validStatus = false;\n switch ($this->type) {\n case self::TYPE_SOFTPHONE:\n case self::TYPE_SOFTPHONE_INBOUND:\n $validStatus = true;\n\n break;\n case self::TYPE_CONFERENCE:\n $validStatus = in_array($this->status, [\n self::STATUS_BUSY,\n self::STATUS_NO_ANSWER,\n self::STATUS_COMPLETED,\n self::STATUS_CANCELLED,\n ], true);\n\n break;\n case self::TYPE_SMS_INBOUND:\n case self::TYPE_SMS_OUTBOUND:\n $validStatus = in_array($this->status, [\n self::STATUS_QUEUED,\n self::STATUS_SENT,\n self::STATUS_UNDELIVERED,\n self::STATUS_DELIVERED,\n self::STATUS_RECEIVED,\n ], true);\n\n break;\n }\n\n // Depending on the activity channel, we should not try to log.\n return $validStatus;\n }\n\n public function isScheduled(): bool\n {\n return $this->status === self::STATUS_SCHEDULED;\n }\n\n public function scheduledDuration(): int\n {\n if ($this->scheduled_start_time && $this->scheduled_end_time) {\n return $this->scheduled_end_time->timestamp - $this->scheduled_start_time->timestamp;\n }\n\n return 0;\n }\n\n public function isPending(): bool\n {\n return $this->status === self::STATUS_PENDING;\n }\n\n public function isCompleted(): bool\n {\n return $this->status === self::STATUS_COMPLETED;\n }\n\n public function isRinging(): bool\n {\n return $this->status === self::STATUS_RINGING;\n }\n\n public function isInProgress(): bool\n {\n return $this->status === self::STATUS_IN_PROGRESS;\n }\n\n public function isBusy(): bool\n {\n return $this->status === self::STATUS_BUSY;\n }\n\n public function isNoAnswer(): bool\n {\n return $this->status === self::STATUS_NO_ANSWER;\n }\n\n public function isFailed(): bool\n {\n return $this->status === self::STATUS_FAILED;\n }\n\n public function isCancelled(): bool\n {\n return $this->status === self::STATUS_CANCELLED;\n }\n\n public function hasEnded(int $gracePeriodMinutes = 15): bool\n {\n if ($this->isCompleted()) {\n return true;\n }\n\n if (($this->isFailed() || $this->isCancelled()) && $this->hasScheduledEndTime()) {\n return $this->getScheduledEndTime()->addMinutes($gracePeriodMinutes)->isPast();\n }\n\n return false;\n }\n\n public function hasStarted(): bool\n {\n return $this->hasActualStartTime();\n }\n\n public function isOngoing(): bool\n {\n return $this->hasActualStartTime() && ! $this->hasActualEndTime();\n }\n\n public function isTypeSmsInbound(): bool\n {\n return $this->getType() === self::TYPE_SMS_INBOUND;\n }\n\n public function isTypeSmsOutbound(): bool\n {\n return $this->getType() === self::TYPE_SMS_OUTBOUND;\n }\n\n public function isTypeSoftPhone(): bool\n {\n return $this->getType() === self::TYPE_SOFTPHONE;\n }\n\n public function isTypeSoftphoneInbound(): bool\n {\n return $this->getType() === self::TYPE_SOFTPHONE_INBOUND;\n }\n\n public function isTypeConference(): bool\n {\n return $this->getType() === self::TYPE_CONFERENCE;\n }\n\n /**\n * Get a conference elapsed time in seconds.\n *\n * @return int seconds count\n */\n public function secondsTimeElapsed(): int\n {\n if (empty($this->actual_start_time)) {\n return 0;\n }\n\n // Get number of seconds since conference actual start time\n return (int) abs(Carbon::now()->diffInRealSeconds($this->actual_start_time));\n }\n\n /**\n * Get a conference elapsed time formatted as \"1:30:20\" if more than 1 hour or \"30:20\" otherwise.\n */\n public function formattedTimeElapsed(): string\n {\n // Get number of seconds since conference actual start time.\n $elapsedSeconds = $this->secondsTimeElapsed();\n $elapsedTime = Carbon::createFromTimestampUTC($elapsedSeconds);\n\n // Format conference start time.\n return $elapsedTime->format($elapsedSeconds < 3600 ? 'i:s' : 'G:i:s');\n }\n\n public function wasScheduled(): bool\n {\n return $this->calendarEvent !== null || in_array($this->getSource(), [self::SOURCE_OUTLOOK, self::SOURCE_GOOGLE]);\n }\n\n public function isInstant(): bool\n {\n return ! $this->wasScheduled();\n }\n\n /**\n * GETTERS AND SETTERS FOLLOW BELOW\n */\n\n public function getUuid(): string\n {\n return $this->getAttribute('id_string');\n }\n\n public function getId(): int\n {\n return $this->getAttribute('id');\n }\n\n public function getFromParticipantId(): ?int\n {\n return $this->getAttribute('from_participant_id');\n }\n\n public function getFromParticipant(): ?Participant\n {\n return $this->getAttribute('from');\n }\n\n public function getToParticipantId(): ?int\n {\n return $this->getAttribute('to_participant_id');\n }\n\n public function getToParticipant(): ?Participant\n {\n return $this->getAttribute('to');\n }\n\n public function hasScheduledStartTime(): bool\n {\n return $this->getAttribute('scheduled_start_time') !== null;\n }\n\n public function getScheduledStartTime(): ?Carbon\n {\n return $this->getAttribute('scheduled_start_time');\n }\n\n public function setScheduledStartTime(DateTimeInterface $dateTime): self\n {\n $this->setAttribute('scheduled_start_time', $dateTime);\n\n return $this;\n }\n\n public function getScheduledEndTime(): ?DateTimeInterface\n {\n return $this->getAttribute('scheduled_end_time');\n }\n\n public function hasScheduledEndTime(): bool\n {\n return $this->getAttribute('scheduled_end_time') !== null;\n }\n\n public function setScheduledEndTime(DateTimeInterface $dateTime): self\n {\n $this->setAttribute('scheduled_end_time', $dateTime);\n\n return $this;\n }\n\n public function getActualStartTime(): ?Carbon\n {\n return $this->getAttribute('actual_start_time');\n }\n\n public function hasActualStartTime(): bool\n {\n return $this->getAttribute('actual_start_time') !== null;\n }\n\n public function getActualEndTime(): ?Carbon\n {\n return $this->getAttribute('actual_end_time');\n }\n\n public function hasActualEndTime(): bool\n {\n return $this->getAttribute('actual_end_time') !== null;\n }\n\n public function getType(): ?string\n {\n return $this->getAttribute('type');\n }\n\n public function getStatus(): string\n {\n return $this->getAttribute('status');\n }\n\n public function setStatus(string $status): self\n {\n $this->setAttribute('status', $status);\n\n return $this;\n }\n\n public function setActualStartTime(DateTimeInterface $dateTime): self\n {\n $this->setAttribute('actual_start_time', $dateTime);\n\n return $this;\n }\n\n public function setActualEndTime(DateTimeInterface $dateTime, bool $shouldUpdateDuration = true): self\n {\n $this->setAttribute('actual_end_time', $dateTime);\n\n if (! $shouldUpdateDuration) {\n return $this;\n }\n\n return $this->updateDuration();\n }\n\n public function updateDuration(): self\n {\n if (! $this->hasActualStartTime() || ! $this->hasActualEndTime()) {\n return $this;\n }\n\n return $this->setDuration(\n (int) abs($this->getActualStartTime()->diffInRealSeconds($this->getActualEndTime()))\n );\n }\n\n public function setDuration(int $duration): self\n {\n $this->setAttribute('duration', $duration);\n\n return $this;\n }\n\n public function getRecordingState(): string\n {\n return $this->getAttribute('recording_state');\n }\n\n public function isRecordingState(string $recordingState): bool\n {\n return $this->getRecordingState() === $recordingState;\n }\n\n public function setRecordingState(string $recordingState): self\n {\n $this->setAttribute('recording_state', $recordingState);\n\n return $this;\n }\n\n public function hasActivityType(): bool\n {\n return $this->getAttribute('category') !== null;\n }\n\n public function getActivityType(): ?PlaybookCategory\n {\n return $this->getAttribute('category');\n }\n\n public function setActivityType(int $playbookCategoryId): self\n {\n $this->setAttribute('playbook_category_id', $playbookCategoryId);\n\n return $this;\n }\n\n public function hasStage(): bool\n {\n return $this->getAttribute('stage') !== null;\n }\n\n public function getStage(): ?Stage\n {\n return $this->getAttribute('stage');\n }\n\n public function getStageId(): ?int\n {\n return $this->getAttribute('stage_id');\n }\n\n public function setStageId(?int $stageId): void\n {\n $this->setAttribute('stage_id', $stageId);\n }\n\n public function hasOpportunity(): bool\n {\n return $this->getAttribute('opportunity') !== null;\n }\n\n public function getOpportunity(): ?Opportunity\n {\n return $this->getAttribute('opportunity');\n }\n\n public function getOpportunityId(): ?int\n {\n return $this->getAttribute('opportunity_id');\n }\n\n public function setOpportunityId(?int $opportunityId): void\n {\n $this->setAttribute('opportunity_id', $opportunityId);\n }\n\n public function hasContact(): bool\n {\n return $this->getAttribute('contact') !== null;\n }\n\n public function getContact(): ?Contact\n {\n return $this->getAttribute('contact');\n }\n\n public function getContactId(): ?int\n {\n return $this->getAttribute('contact_id');\n }\n\n public function setContactId(?int $contactId): void\n {\n $this->setAttribute('contact_id', $contactId);\n }\n\n public function hasLead(): bool\n {\n return $this->getAttribute('lead') !== null;\n }\n\n public function getLead(): ?Lead\n {\n return $this->getAttribute('lead');\n }\n\n public function getLeadId(): ?int\n {\n return $this->getAttribute('lead_id');\n }\n\n public function setLeadId(?int $leadId): void\n {\n $this->setAttribute('lead_id', $leadId);\n }\n\n public function hasAccount(): bool\n {\n return $this->getAttribute('account') !== null;\n }\n\n public function getAccount(): ?Account\n {\n return $this->getAttribute('account');\n }\n\n public function getAccountId(): ?int\n {\n return $this->getAttribute('account_id');\n }\n\n public function setAccountId(?int $accountId): void\n {\n $this->setAttribute('account_id', $accountId);\n }\n\n /**\n * This method exists to avoid confusion using ->participants() or ->participants. Use the getter instead.\n *\n * @return Collection<int, Participant>|Participant[]\n */\n public function getParticipants(): Collection\n {\n return $this->participants;\n }\n\n /**\n * @deprecated use ParticipantRepository::findParticipantRoomOwner() instead\n */\n public function findParticipantRoomOwner(): ?Participant\n {\n $roomOwnerId = $this->getUserId();\n\n return $this->getParticipants()\n ->filter(static fn (Participant $participant): bool => $participant->isSameUserId($roomOwnerId))\n ->first();\n }\n\n public function hasCrmProviderId(): bool\n {\n return $this->getAttribute('crm_provider_id') !== null;\n }\n\n public function getCrmProviderId(): ?string\n {\n return $this->getAttribute('crm_provider_id');\n }\n\n public function setCrmProviderId(?string $crmProviderId): void\n {\n $this->setAttribute('crm_provider_id', $crmProviderId);\n }\n\n public function getUserId(): ?int\n {\n return $this->getAttribute('user_id');\n }\n\n public function hasUser(): bool\n {\n return $this->user()->exists();\n }\n\n public function getUser(): User\n {\n return $this->getAttribute('user');\n }\n\n public function getCreatedAt(): Carbon\n {\n return $this->getAttribute('created_at');\n }\n\n public function isInFiniteState(): bool\n {\n return $this->isFiniteState($this->getStatus());\n }\n\n public function isFiniteState(string $status): bool\n {\n $finiteStates = self::FINITE_STATES[$this->getType()] ?? [];\n\n return in_array($status, $finiteStates, true);\n }\n\n public function getParticipant(Authenticatable $user): Participant\n {\n return $this->findParticipant($user);\n }\n\n public function findParticipant(Authenticatable $user): ?Participant\n {\n if ($user instanceof User) {\n /** @var User $user */\n return $this->participants()->where('user_id', '=', $user->getId())->first();\n }\n\n throw new LogicException(sprintf('Unsupported Authenticatable implementation %s', get_class($user)));\n }\n\n public function hasLanguageCode(): bool\n {\n return $this->getAttribute('language') !== null;\n }\n\n public function getLanguageCode(): ?string\n {\n /** @var string|null */\n return $this->getAttribute('language');\n }\n\n public function getLanguageCodeHyphenated(): string\n {\n return str_replace('_', '-', $this->getLanguageCode() ?? '');\n }\n\n public function getLanguageCodeLocale(): string\n {\n [ $language ] = explode('_', $this->getLanguageCode() ?? '');\n\n return $language;\n }\n\n public function setLanguageCode(string $value): self\n {\n return $this->setAttribute('language', $value);\n }\n\n public function hasSource(): bool\n {\n return $this->getAttribute('source') !== null;\n }\n\n public function setSource(?string $source): self\n {\n return $this->setAttribute('source', $source);\n }\n\n public function isSource(string $source): bool\n {\n return $this->getAttribute('source') === $source;\n }\n\n public function getSource(): ?string\n {\n return $this->getAttribute('source');\n }\n\n public function isSourceGong(): bool\n {\n return $this->isSource(self::SOURCE_GONG);\n }\n\n public function getExternalId(): ?string\n {\n return $this->getAttribute('external_id');\n }\n\n public function setExternalId(?string $externalId): self\n {\n return $this->setAttribute('external_id', $externalId);\n }\n\n public function hasExternalId(): bool\n {\n return $this->getAttribute('external_id') !== null;\n }\n\n public function getProvider(): string\n {\n return $this->getAttribute('provider');\n }\n\n public function hasTelephonyProviderId(): bool\n {\n return $this->getAttribute('telephony_provider_id') !== null;\n }\n\n public function getTelephonyProviderId(): ?string\n {\n return $this->getAttribute('telephony_provider_id');\n }\n\n public function setTelephonyProviderId(?string $telephonyProviderId): self\n {\n return $this->setAttribute('telephony_provider_id', $telephonyProviderId);\n }\n\n public function getLocation(): ?string\n {\n return $this->getAttribute('location');\n }\n\n public function setLocation(?string $location): self\n {\n return $this->setAttribute('location', $location);\n }\n\n public function isDeleted(): bool\n {\n return $this->getAttribute('deleted_at') !== null;\n }\n\n /**\n * Check if activity recording is on and activity status is not one of the failed statuses.\n */\n public function canReviewActivity(): bool\n {\n $failedStatuses = self::$enumFailedStatuses;\n\n return (! in_array($this->recording_state, [self::RECORDING_OFF, self::RECORDING_STOPPED], true) &&\n ! in_array($this->status, $failedStatuses, true));\n }\n\n public function hasReasonCodeBotKicked(): bool\n {\n return $this->getFlag('recording_reason_code', self::FLAG_RECORDING_REASON_MEETING_BOT_KICKED);\n }\n\n public function hasReasonCodeNotCompliant(): bool\n {\n return $this->getFlag('recording_reason_code', self::FLAG_RECORDING_REASON_CONSENT_DENIED);\n }\n\n public function hasTopicTriggers(): bool\n {\n return $this->topicTriggers()->count() !== 0;\n }\n\n public function getTopicTriggers(): Collection\n {\n return $this->topicTriggers;\n }\n\n public function getTopicTriggersSorted(): Collection\n {\n $this->loadMissing([\n 'topicTriggers.participant',\n 'topicTriggers.playbackThemeTopicTrigger',\n 'topicTriggers.playbackThemeTopicTrigger',\n 'topicTriggers.playbackThemeTopicTrigger.playbackThemeTopic',\n 'topicTriggers.playbackThemeTopicTrigger.playbackThemeTopic.playbackTheme',\n ]);\n\n return $this->topicTriggers\n ->sortBy([\n 'playbackThemeTopicTrigger.playbackThemeTopic.playbackTheme.sort',\n 'playbackThemeTopicTrigger.playbackThemeTopic.sort',\n 'playbackThemeTopicTrigger.sort',\n ]);\n }\n\n public function hasQuestions(): bool\n {\n return $this->questions()->exists();\n }\n\n public function getQuestions(): Collection\n {\n return $this->questions;\n }\n\n public function hasValue(): bool\n {\n return $this->getAttribute('value') !== null;\n }\n\n public function getValue(): ?float\n {\n return $this->getAttribute('value');\n }\n\n public function setValue(?float $value): void\n {\n $this->setAttribute('value', $value);\n }\n\n public function transitionTo(string $newState, callable $callback, ?int $timeout = null): self\n {\n $newState = $this->getWorkflowStateFor(\n $this->getType(),\n $newState\n );\n\n return $this->traitTransitionTo($newState, $callback, $timeout);\n }\n\n public function getWorkflowState(): string\n {\n return $this->getWorkflowStateFor(\n $this->getType(),\n $this->getStatus()\n );\n }\n\n public function getActivityProviderDisplayName(): string\n {\n return \\Cache::remember('activity_provider_display_name-' . $this->getProvider(), 60 * 60 * 24, function () {\n $activityProviderRegistry = app()->make(ActivityProviderRegistry::class);\n\n try {\n return $activityProviderRegistry->get($this->getProvider())->getDisplayName();\n } catch (Exception $exception) {\n return ucfirst($this->getProvider());\n }\n });\n }\n\n private function getWorkflowStateFor(string $activityChannel, string $activityStatus): string\n {\n return sprintf(\n '%s::%s',\n $activityChannel,\n $activityStatus\n );\n }\n\n public function getWorkflow(): array\n {\n $map = [\n self::TYPE_SOFTPHONE => [\n self::STATUS_SCHEDULED => [\n self::STATUS_PENDING,\n self::STATUS_IN_PROGRESS,\n self::STATUS_FAILED,\n self::STATUS_BUSY,\n ],\n self::STATUS_PENDING => [\n self::STATUS_IN_PROGRESS,\n self::STATUS_FAILED,\n self::STATUS_BUSY,\n ],\n self::STATUS_RINGING => [\n self::STATUS_CANCELLED,\n self::STATUS_FAILED,\n self::STATUS_IN_PROGRESS,\n self::STATUS_BUSY,\n ],\n self::STATUS_IN_PROGRESS => [\n self::STATUS_COMPLETED,\n ],\n ],\n self::TYPE_SOFTPHONE_INBOUND => [\n self::STATUS_RINGING => [\n self::STATUS_IN_PROGRESS,\n self::STATUS_NO_ANSWER,\n self::STATUS_CANCELLED,\n self::STATUS_FAILED,\n self::STATUS_BUSY,\n ],\n self::STATUS_IN_PROGRESS => [\n self::STATUS_COMPLETED,\n ],\n ],\n ];\n\n return collect($map)\n ->mapWithKeys(function (array $currentStates, string $activityChannel): array {\n return [\n $activityChannel => collect($currentStates)\n ->mapWithKeys(function (array $possibleStates, $currentState) use ($activityChannel): array {\n $transitionName = $this->getWorkflowStateFor($activityChannel, $currentState);\n\n return [\n $transitionName => array_map(function (string $newState) use ($activityChannel) {\n return $this->getWorkflowStateFor($activityChannel, $newState);\n }, $possibleStates),\n ];\n }),\n ];\n })\n ->reduce(static function (array $carry, Collection $item): array {\n return array_merge($carry, $item->all());\n }, []);\n }\n\n public function hasPosterPath(): bool\n {\n return $this->getAttribute('poster_path') !== null;\n }\n\n public function getPosterPath(): ?string\n {\n return $this->getAttribute('poster_path');\n }\n\n /**\n * Take into account all recording settings and determine if we need to record this activity or not.\n */\n public function shouldRecord(): bool\n {\n return $this->determineRecordingReasonCode() === null;\n }\n\n public function determineRecordingReasonCode(): ?int\n {\n // Conference specific decisions.\n if ($this->isTypeConference()) {\n // If they have manually overridden the recording setting to not record.\n if ($this->hasRecordingPreference() && $this->getRecordingPreference() === false) {\n return self::FLAG_RECORDING_REASON_PREFERENCE_OVERRIDE;\n }\n\n // If they have manually overridden the recording setting to record.\n if ($this->hasRecordingPreference() && $this->getRecordingPreference() === true) {\n return null;\n }\n\n // If their team has disabled recording meetings, don't record.\n if ($this->user->team->isConferenceRecordPreferenceDisabled()) {\n return self::FLAG_RECORDING_REASON_TEAM_AUTOMATIC_DISABLED;\n }\n\n // If the host has disabled recording meetings, don't record.\n if ($this->user->checkConferenceRecordPreference() === false) {\n return self::FLAG_RECORDING_REASON_USER_AUTOMATIC_DISABLED;\n }\n\n // If it was marked internal...\n if ($this->is_internal) {\n // and their team has disabled recording internal meetings, don't record.\n if (\n $this->user->team->isConferenceRecordPreferenceEnabled()\n && ! $this->user->team->isConferenceRecordInternalPreferenceEnabled()\n ) {\n return self::FLAG_RECORDING_REASON_TEAM_INTERNAL_DISABLED;\n }\n\n // and the host has disabled recording internal meetings, don't record.\n if ($this->user->checkConferenceRecordInternalPreference() === false) {\n return self::FLAG_RECORDING_REASON_USER_INTERNAL_DISABLED;\n }\n }\n\n // If it was not scheduled and they disabled internal meetings, we cannot determine if it was internal.\n if ($this->wasScheduled() === false && $this->user->checkConferenceRecordInternalPreference() === false) {\n return self::FLAG_RECORDING_REASON_USER_INTERNAL_DISABLED_UNSCHEDULED;\n }\n }\n\n return null;\n }\n\n public function getRecordingReasonCode(): int\n {\n return $this->getAttribute('recording_reason_code');\n }\n\n public function setRecordingReasonCode(int $recordingReasonCode): self\n {\n $this->setAttribute('recording_reason_code', $recordingReasonCode);\n\n return $this;\n }\n\n // Not used today.\n public function getRecordingReasonString(): ?string\n {\n if ($this->hasRecordingReasonCompliancePrompted()) {\n return Team::COMPLIANCE_MODE_RECORDING_PROMPT;\n }\n\n if ($this->hasRecordingReasonComplianceRestricted()) {\n return Team::COMPLIANCE_MODE_RECORDING_RESTRICT;\n }\n\n if ($this->hasRecordingReasonComplianceRestrictedToOneSideRecording()) {\n return Team::COMPLIANCE_MODE_RECORDING_RESTRICT_ONE_SIDE;\n }\n\n return null;\n }\n\n public function hasRecordingReasonComplianceRestricted(): bool\n {\n return $this->getFlag('recording_reason_code', self::FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT);\n }\n\n public function hasRecordingReasonCompliancePrompted(): bool\n {\n return $this->getFlag('recording_reason_code', self::FLAG_RECORDING_REASON_COMPLIANCE_PROMPT);\n }\n\n public function hasRecordingReasonComplianceRestrictedToOneSideRecording(): bool\n {\n return $this->getFlag('recording_reason_code', self::FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT_ONE_SIDE);\n }\n\n public function getAudioTrack(): ?Track\n {\n /** @var Track|null */\n return $this->tracks()\n ->where('type', '=', Track::TYPE_AUDIO)\n ->first();\n }\n\n public function activeParticipants(): HasMany\n {\n return $this->hasMany(Participant::class)->active();\n }\n\n public function getActiveParticipants(): Eloquent\\Collection\n {\n return $this->getAttribute('activeParticipants');\n }\n\n public function crm(): Eloquent\\Relations\\BelongsTo\n {\n return $this->belongsTo(Configuration::class, 'crm_configuration_id');\n }\n\n public function activitySummaryLogs(): HasMany\n {\n return $this->hasMany(ActivitySummaryLog::class);\n }\n\n public function getCrm(): ?Configuration\n {\n return $this->getAttribute('crm');\n }\n\n public function hasCrmConfiguration(): bool\n {\n return $this->getAttribute('crm') !== null;\n }\n\n public function isProcessed(): ?bool\n {\n return $this->getAttribute('is_processed');\n }\n\n public function hasRecordingPrompt(): bool\n {\n return $this->getAttribute('has_recording_prompt') === true;\n }\n\n public function isOnAir(): bool\n {\n return $this->getAttribute('on_air') === self::ON_AIR_READY || $this->getAttribute('on_air') === self::ON_AIR_STREAMING;\n }\n\n public function setOnAir(int $onAir): self\n {\n $this->setAttribute('on_air', $onAir);\n\n return $this;\n }\n\n public function getOnAir(): ?int\n {\n return $this->getAttribute('on_air');\n }\n\n public function setTitleFromCallData(Call $call): void\n {\n $direction = $call->isOutbound() ? 'to' : 'from';\n\n $party = $this->prospect_name\n ?? $call->getContactName()\n ?? $call->getOtherPartyPhoneNumber()\n ;\n\n $this->update(['title' => sprintf('Call %s %s', $direction, $party)]);\n }\n\n /**\n * @param array{}|array{channels:string|null, format:string|null, type:string|null, status:string|null} $audioParams\n */\n public function createAudioTrack(\n string $telephonyProviderId,\n string $recordingUrl,\n array $audioParams = []\n ): Track {\n return $this->tracks()->updateOrCreate([\n 'telephony_provider_id' => $telephonyProviderId,\n ], [\n 'type' => $audioParams['type'] ?? Track::TYPE_AUDIO,\n 'status' => $audioParams['status'] ?? Track::STATUS_PENDING,\n 'format' => $audioParams['format'] ?? Track::FORMAT_WAV,\n 'provider_content_url' => $recordingUrl,\n 'start_time' => $this->actual_start_time,\n 'end_time' => $this->actual_end_time,\n ]);\n }\n\n public function createTrack(string $telephonyProviderId, array $params): Track\n {\n return $this->tracks()->updateOrCreate(\n [\n 'telephony_provider_id' => $telephonyProviderId,\n ],\n $params\n );\n }\n\n public function createOrganiserParticipant(Call $call): Participant\n {\n $user = $this->getUser();\n\n return $this->updateOrCreateParticipant([\n 'is_ghost' => 0,\n 'name' => $user->name,\n 'email' => $user->email,\n 'phone_number' => phone_e164(null, $call->getUserPhoneNumber()),\n 'enter_time' => $this->actual_start_time,\n 'exit_time' => $this->actual_end_time,\n 'user_id' => $user->id,\n ], false);\n }\n\n public function createProspectParticipant(Call $call): Participant\n {\n // not null 'name' is mandatory here to create a separate participant with 'nameMatching'\n // in case of the same phone_number with the Organiser\n $useNameMatching = $call->getUserPhoneNumber() === $call->getOtherPartyPhoneNumber();\n $defaultName = $useNameMatching ? '' : null;\n\n return $this->updateOrCreateParticipant(data: [\n 'is_ghost' => 0,\n 'name' => $this->prospect->name ?? $defaultName,\n 'email' => $this->prospect->email ?? null,\n 'phone_number' => phone_e164(null, $call->getOtherPartyPhoneNumber()),\n 'enter_time' => $this->actual_start_time,\n 'exit_time' => $this->actual_end_time,\n 'contact_id' => $this->contact_id ?? null,\n 'lead_id' => $this->lead_id ?? null,\n ], enterRoom: false, nameMatching: $useNameMatching);\n }\n\n public function updateParticipants(Participant $organiserParticipant, Participant $prospectParticipant): void\n {\n $this->update([\n 'from_participant_id' => $this->isTypeSoftPhone() ? $organiserParticipant->id : $prospectParticipant->id,\n 'to_participant_id' => $this->isTypeSoftPhone() ? $prospectParticipant->id : $organiserParticipant->id,\n ]);\n }\n\n public function hasProspect(): bool\n {\n return $this->getProspectAttribute() !== null;\n }\n\n public function isPrivate(): bool\n {\n return $this->getAttribute('is_private');\n }\n\n /** Create a new factory instance for the model. */\n protected static function newFactory(): Factory\n {\n return ActivityFactory::new();\n }\n\n public function getUpdatedAt(): Carbon\n {\n return $this->getAttribute('updated_at');\n }\n\n public function getActivitySummaryLogs(): Eloquent\\Collection\n {\n return $this->getAttribute('activitySummaryLogs');\n }\n\n public function hasProspectActivitySummaryLog(): bool\n {\n return $this->getActivitySummaryLogs()->contains(\n 'relation_type',\n ActivitySummaryLog::RELATION_OBJECT_TYPE_PROSPECT\n );\n }\n\n public function getTeam(): Team\n {\n return $this->getUser()->getTeam();\n }\n\n private function getUpdateCrmDataResolver(): UpdateCrmDataResolverInterface\n {\n $factory = app(UpdateCrmDataResolverFactory::class);\n\n return $factory->create($this);\n }\n\n public function getMeetingTrackProviderId(string $type): string\n {\n $label = match ($type) {\n Track::TYPE_VIDEO => 'v',\n Track::TYPE_AUDIO => 'a',\n default => throw new InvalidArgumentJiminnyException('Invalid track type'),\n };\n\n $startTimestamp = $this->getScheduledStartTime()?->getTimestamp();\n $teamId = $this->getTeam()->getId();\n\n return $this->getTelephonyProviderId() . ':' . $label . ':' . $startTimestamp . ':' . $teamId;\n }\n\n /**\n * Get all consent records associated with this activity\n *\n * @return \\Illuminate\\Database\\Eloquent\\Relations\\HasMany\n */\n public function participantConsents(): HasMany\n {\n return $this->hasMany(Participant\\Consent::class);\n }\n\n public function isDiallerCall(): bool\n {\n if ($this->getProvider() === Activity::PROVIDER_UPLOADER) {\n return false;\n }\n\n if (! in_array($this->getType(), [self::TYPE_SOFTPHONE, self::TYPE_SOFTPHONE_INBOUND])) {\n return false;\n }\n\n return $this->getProvider() !== self::PROVIDER_TWILIO;\n }\n\n public function getActivityDateWithFallback(): Carbon\n {\n if ($this->getActualStartTime() !== null) {\n return $this->getActualStartTime();\n }\n\n if ($this->getScheduledStartTime() !== null) {\n return $this->getScheduledStartTime();\n }\n\n return $this->getCreatedAt();\n }\n\n public function getCrmType(): ?string\n {\n // Treat uploader activities as conferences\n if ($this->getProvider() === Activity::PROVIDER_UPLOADER) {\n return Activity::TYPE_CONFERENCE;\n }\n\n return $this->getType();\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\nnamespace Jiminny\\Models;\n\nuse Carbon\\Carbon;\nuse Database\\Factories\\ActivityFactory;\nuse DateTimeInterface;\nuse Exception;\nuse Illuminate\\Contracts\\Auth\\Authenticatable;\nuse Illuminate\\Database\\Eloquent;\nuse Illuminate\\Database\\Eloquent\\Attributes\\Scope;\nuse Illuminate\\Database\\Eloquent\\Builder;\nuse Illuminate\\Database\\Eloquent\\Factories\\Factory;\nuse Illuminate\\Database\\Eloquent\\Factories\\HasFactory;\nuse Illuminate\\Database\\Eloquent\\Relations\\BelongsTo;\nuse Illuminate\\Database\\Eloquent\\Relations\\BelongsToMany;\nuse Illuminate\\Database\\Eloquent\\Relations\\HasMany;\nuse Illuminate\\Database\\Eloquent\\Relations\\HasManyThrough;\nuse Illuminate\\Database\\Eloquent\\Relations\\HasOne;\nuse Illuminate\\Database\\Eloquent\\SoftDeletes;\nuse Illuminate\\Support\\Collection;\nuse Illuminate\\Support\\Facades\\Auth;\nuse InvalidArgumentException;\nuse Jiminny\\Component\\ElasticSearch;\nuse Jiminny\\Component\\MeetingBot;\nuse Jiminny\\Component\\Model\\BitwiseFlagTrait;\nuse Jiminny\\Component\\PlaybackPage\\Comments\\Services\\ActivityCommentService;\nuse Jiminny\\Component\\Sidekick\\SidekickService;\nuse Jiminny\\Component\\Uuid\\UuidAwareInterface;\nuse Jiminny\\Component\\Workflow;\nuse Jiminny\\Contracts;\nuse Jiminny\\Contracts\\Crm\\ProspectInterface;\nuse Jiminny\\DTO\\ImportCall\\Call;\nuse Jiminny\\Events\\Activities\\ActivityTypeUpdated;\nuse Jiminny\\Events\\Activities\\ActivityUpdated;\nuse Jiminny\\Events\\Activities\\ProspectUpdated;\nuse Jiminny\\Events\\Activities\\StageUpdated;\nuse Jiminny\\Events\\Activities\\StatusUpdated;\nuse Jiminny\\Events\\Activities\\TitleUpdated;\nuse Jiminny\\Exceptions\\InvalidArgumentException as InvalidArgumentJiminnyException;\nuse Jiminny\\Exceptions\\LogicException;\nuse Jiminny\\Exceptions\\RuntimeException;\nuse Jiminny\\Models;\nuse Jiminny\\Models\\Activity\\ActivitySummaryLog;\nuse Jiminny\\Models\\Activity\\ActivityUploadSetting;\nuse Jiminny\\Models\\Activity\\AvailabilityNotification;\nuse Jiminny\\Models\\Activity\\CoachRequest;\nuse Jiminny\\Models\\Activity\\Comment;\nuse Jiminny\\Models\\Activity\\Log;\nuse Jiminny\\Models\\Activity\\Message;\nuse Jiminny\\Models\\Activity\\Moment;\nuse Jiminny\\Models\\Activity\\Note;\nuse Jiminny\\Models\\Activity\\ParticipantSpeech;\nuse Jiminny\\Models\\Activity\\Play;\nuse Jiminny\\Models\\Activity\\Question;\nuse Jiminny\\Models\\Activity\\Share;\nuse Jiminny\\Models\\Activity\\Snapshot;\nuse Jiminny\\Models\\Activity\\Stats;\nuse Jiminny\\Models\\Activity\\SubscriptionSet;\nuse Jiminny\\Models\\Activity\\TopicTrigger;\nuse Jiminny\\Models\\Activity\\Transcription;\nuse Jiminny\\Models\\Calendar\\CalendarEvent;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Models\\Crm\\FieldData;\nuse Jiminny\\Models\\ElasticSearch\\ActivityElasticSearchTrait;\nuse Jiminny\\Models\\Feature\\FeatureEnum;\nuse Jiminny\\Models\\Participant\\Connection;\nuse Jiminny\\Models\\Playlist\\Activity as PlaylistActivity;\nuse Jiminny\\Services\\Activity\\ActivityProviderRegistry;\nuse Jiminny\\Services\\Activity\\Import\\DataResolvers\\UpdateCrmDataByStrategy;\nuse Jiminny\\Services\\Activity\\Import\\DataResolvers\\UpdateCrmDataResolverFactory;\nuse Jiminny\\Services\\Activity\\Import\\DataResolvers\\UpdateCrmDataResolverInterface;\nuse Jiminny\\Traits\\Enums;\nuse Jiminny\\Traits\\RequiresUUID;\nuse Jiminny\\Utils\\CurrencyFormatter;\nuse NumberFormatter;\n\nuse function in_array;\n\n/**\n * Jiminny\\Models\\Activity\n *\n * @property null|int $auto_score filled from ES hydrator, not in DB!\n * @property-read Account|null $account\n * @property-read CalendarEvent|null $calendarEvent\n * @property-read Contact|null $contact\n * @property-read Lead|null $lead\n * @property-read Opportunity|null $opportunity\n * @property-read Stage|null $stage\n * @property int $id\n * @property mixed|null $uuid\n * @property string|null $source\n * @property string|null $external_id\n * @property string $provider\n * @property string|null $location\n * @property string|null $telephony_provider_id\n * @property int|null $from_participant_id\n * @property int|null $to_participant_id\n * @property int|null $device_id\n * @property string|null $type\n * @property int|null $playbook_category_id\n * @property int $user_id\n * @property int|null $lead_id\n * @property int|null $account_id\n * @property int|null $contact_id\n * @property int|null $opportunity_id\n * @property int|null $stage_id\n * @property string|null $value\n * @property int|null $crm_configuration_id\n * @property string|null $crm_provider_id\n * @property string|null $language\n * @property int|null $transcription_id\n * @property int $duration\n * @property string $status\n * @property int|null $on_air\n * @property int|null $calendar_event_id\n * @property string $recording_state\n * @property bool|null $recording_preference\n * @property int $recording_reason_code\n * @property int $summary_reminder_sent\n * @property \\Illuminate\\Support\\Carbon|null $log_reminder_sent_at\n * @property \\Illuminate\\Support\\Carbon|null $organizer_notified_at\n * @property bool|null $has_recording_prompt\n * @property bool $is_internal\n * @property int $is_locked\n * @property int $is_recording\n * @property bool|null $is_processed\n * @property bool $is_private\n * @property bool $is_instant_invite\n * @property string|null $poster_path\n * @property string|null $summary\n * @property string|null $title\n * @property string|null $description\n * @property \\Illuminate\\Support\\Carbon|null $scheduled_start_time\n * @property \\Illuminate\\Support\\Carbon|null $scheduled_end_time\n * @property \\Illuminate\\Support\\Carbon|null $actual_start_time\n * @property \\Illuminate\\Support\\Carbon|null $actual_end_time\n * @property int|null $uploaded_by\n * @property \\Illuminate\\Support\\Carbon|null $deleted_at\n * @property \\Illuminate\\Support\\Carbon|null $created_at\n * @property \\Illuminate\\Support\\Carbon|null $updated_at\n * @property string|null $average_score\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Participant> $activeParticipants\n * @property-read int|null $active_participants_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Scorecard\\ActivityScorecardRuleTrigger> $activityScorecardRuleTriggers\n * @property-read int|null $activity_scorecard_rule_triggers_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Scorecard\\ActivityScorecardRule> $activityScorecardRules\n * @property-read int|null $activity_scorecard_rules_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, AvailabilityNotification> $availabilityNotifications\n * @property-read int|null $availability_notifications_count\n * @property-read \\Jiminny\\Models\\PlaybookCategory|null $category\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, CoachRequest> $coachRequests\n * @property-read int|null $coach_requests_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\CoachingFeedback> $coachingFeedbacks\n * @property-read int|null $coaching_feedbacks_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Message> $coachingMessages\n * @property-read int|null $coaching_messages_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Comment> $comments\n * @property-read int|null $comments_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Connection> $connections\n * @property-read int|null $connections_count\n * @property-read Configuration|null $crm\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, FieldData> $data\n * @property-read int|null $data_count\n * @property-read \\Jiminny\\Models\\Device|null $device\n * @property-read \\Kalnoy\\Nestedset\\Collection<int, \\Jiminny\\Models\\Playlist> $favoritePlaylists\n * @property-read int|null $favorite_playlists_count\n * @property-read \\Jiminny\\Models\\Participant|null $from\n * @property-read string|null $activity_title\n * @property-read mixed $comment_count\n * @property-read mixed $duration_for_humans\n * @property-read string $duration_for_humans_short\n * @property-read int $favorite_count\n * @property-read mixed $favorites_count\n * @property-read mixed $formatted_value\n * @property-read string $id_string\n * @property-read \\Jiminny\\Models\\Participant|null $organizer\n * @property-read mixed $play_count\n * @property-read int|null $plays_count\n * @property-read ?ProspectInterface $prospect\n * @property-read string|null $prospect_name\n * @property-read mixed $prospect_type\n * @property-read mixed $share_count\n * @property-read int|null $shares_count\n * @property-read int|null $tracks_with_telephony_count\n * @property-read int|null $visible_comments_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\CoachingFeedback> $latestCoachingFeedbacks\n * @property-read int|null $latest_coaching_feedbacks_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Log> $logs\n * @property-read int|null $logs_count\n * @property-read \\Jiminny\\Models\\Track|null $masterTrack\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Message> $messages\n * @property-read int|null $messages_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Moment> $moments\n * @property-read int|null $moments_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Note> $notes\n * @property-read int|null $notes_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Participant\\Share> $participantShares\n * @property-read int|null $participant_shares_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, ParticipantSpeech> $participantSpeeches\n * @property-read int|null $participant_speeches_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Participant\\ParticipantStats> $participantStats\n * @property-read int|null $participant_stats_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Participant> $participants\n * @property-read int|null $participants_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, PlaylistActivity> $playlistActivities\n * @property-read int|null $playlist_activities_count\n * @property-read \\Kalnoy\\Nestedset\\Collection<int, \\Jiminny\\Models\\Playlist> $playlists\n * @property-read int|null $playlists_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Play> $plays\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Question> $questions\n * @property-read int|null $questions_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Session> $sessions\n * @property-read int|null $sessions_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Share> $shares\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Snapshot> $snapshots\n * @property-read int|null $snapshots_count\n * @property-read Stats|null $stats\n * @property-read \\Jiminny\\Models\\Participant|null $to\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, TopicTrigger> $topicTriggers\n * @property-read int|null $topic_triggers_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Track> $tracks\n * @property-read int|null $tracks_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Track> $tracksWithTelephony\n * @property-read Transcription|null $transcription\n * @property-read \\Jiminny\\Models\\User $user\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Comment> $visibleComments\n *\n * @method static \\Illuminate\\Database\\Eloquent\\Collection<int, static> all($columns = ['*'])\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity chunkByIdDesc($count, callable $callback, $column = null, $alias = null)\n * @method static \\Database\\Factories\\ActivityFactory factory(...$parameters)\n * @method static \\Illuminate\\Database\\Eloquent\\Collection<int, static> get($columns = ['*'])\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity heldBetween(\\Carbon\\Carbon $start, \\Carbon\\Carbon $end)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity idOrUuId($idOrUuid, bool $first = true)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity newModelQuery()\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity newQuery()\n * @method static Builder|Activity onlyTrashed()\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity query()\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity scheduledBetween(\\Carbon\\Carbon $start, \\Carbon\\Carbon $end)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity inOpenDeals()\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity notInOpenDeals()\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity forTeam(int $teamId)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity search(callable $searchQuery, $key = null, $sortByResults = true)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity uuid(string $uuid, bool $first = true)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereAccountId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereActualEndTime($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereActualStartTime($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereAverageScore($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereCalendarEventId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereContactId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereCreatedAt($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereCrmConfigurationId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereCrmProviderId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereDeletedAt($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereDescription($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereDeviceId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereDuration($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereFromParticipantId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereHasRecordingPrompt($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereIsInstantInvite($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereIsInternal($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereIsLocked($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereIsPrivate($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereIsProcessed($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereIsRecording($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereLanguage($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereLeadId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereLocation($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereLogReminderSentAt($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereOnAir($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereOpportunityId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereOrganizerNotifiedAt($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity wherePlaybookCategoryId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity wherePosterPath($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereProvider($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereRecordingPreference($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereRecordingReasonCode($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereRecordingState($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereScheduledEndTime($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereScheduledStartTime($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereSource($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereExternalId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereStageId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereStatus($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereSummary($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereSummaryReminderSent($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereTelephonyProviderId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereTitle($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereToParticipantId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereTranscriptionId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereType($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereUpdatedAt($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereUploadedBy($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereUserId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereUuid($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereValue($value)\n * @method static Builder|Activity withTrashed()\n * @method static Builder|Activity withoutTrashed()\n *\n * @mixin \\Eloquent\n */\nclass Activity extends Model implements\n ElasticSearch\\Contract\\Searchable,\n Workflow\\Workflow\\WorkflowAwareInterface,\n Models\\Contracts\\ActivityContract,\n Contracts\\Model\\ActivityInterface,\n UuidAwareInterface\n{\n use HasFactory;\n\n use Enums;\n use SoftDeletes;\n use RequiresUUID;\n use BitwiseFlagTrait;\n use ElasticSearch\\Model\\Searchable;\n use ActivityElasticSearchTrait;\n\n use Workflow\\Workflow\\WorkflowAware {\n transitionTo as traitTransitionTo;\n }\n\n public const int FLAG_RECORDING_REASON_DEFAULT = 0;\n\n // Recording Prompted but never started\n public const int FLAG_RECORDING_REASON_COMPLIANCE_PROMPT = 1;\n public const int FLAG_RECORDING_REASON_COMPLIANCE_RESUMED = 2;\n public const int FLAG_RECORDING_REASON_NO_AUDIO = 3;\n\n // Recording Disabled by Organization\n public const int FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT = 4;\n\n // Recording was restricted to one-side recordings only\n public const int FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT_ONE_SIDE = 8;\n\n // Recording was not started because it was internal and team setting disabled that.\n public const int FLAG_RECORDING_REASON_TEAM_INTERNAL_DISABLED = 16;\n\n // Recording was not started because it was internal and user setting disabled that.\n public const int FLAG_RECORDING_REASON_USER_INTERNAL_DISABLED = 32;\n\n // Recording was not started because user setting disabled automatic recording.\n public const int FLAG_RECORDING_REASON_USER_AUTOMATIC_DISABLED = 64;\n\n // Recording was not started because team setting disabled automatic recording.\n public const int FLAG_RECORDING_REASON_TEAM_AUTOMATIC_DISABLED = 128;\n\n // Recording was not started because user has overriden default.\n public const int FLAG_RECORDING_REASON_PREFERENCE_OVERRIDE = 256;\n\n // Recording was not started because they don't want internal, and this meeting was not scheduled/imported in time.\n public const int FLAG_RECORDING_REASON_USER_INTERNAL_DISABLED_UNSCHEDULED = 512;\n\n // Recording was not started because their team setting does excludes the meeting type.\n public const int FLAG_RECORDING_REASON_UNSUPPORTED_TYPE = 1024;\n\n // Recording was not started because the external provider disabled it (or recording is missing etc).\n public const int FLAG_RECORDING_REASON_EXTERNALLY_DISABLED = 2048;\n\n // Recording was stopped externally (\"exit-meeting\" Pusher event)\n public const int FLAG_RECORDING_REASON_STOPPED_EXTERNALLY = 384;\n\n // Recording couldn't be started due to Zoom hosting conflict error\n public const int FLAG_RECORDING_REASON_HOSTING_CONFLICT = 448;\n\n // meeting.failed event with reason code BOT_DENIED_FROM_LOBBY\n public const int FLAG_RECORDING_REASON_MEETING_BOT_DENIED_FROM_LOBBY = 4096;\n\n // meeting.failed event with reason code LOBBY_TIMEOUT\n public const int FLAG_RECORDING_REASON_MEETING_BOT_LOBBY_TIMEOUT = 8192;\n\n // meeting.failed event with reason code BOT_KICKED\n public const int FLAG_RECORDING_REASON_MEETING_BOT_KICKED = 16384;\n\n // meeting.failed event with reason code UNKNOWN\n public const int FLAG_RECORDING_REASON_MEETING_BOT_UNKNOWN = 32768;\n\n public const int FLAG_RECORDING_REASON_CONSENT_DENIED = 65536;\n\n // Invalid meeting (e.g. URL is invalid, or the meeting is not found)\n public const int FLAG_RECORDING_REASON_MEETING_BOT_INVALID = 131072;\n\n // The host stopped the recording.\n public const int FLAG_RECORDING_REASON_USER_STOPPED = 262144;\n\n // Recording was not started because an alternative vendor disabled it (or overrode it).\n public const int FLAG_RECORDING_REASON_VENDOR_OVERRIDE = 1048576;\n\n // Login required meeting.failed code\n public const int FLAG_RECORDING_REASON_LOGIN_REQUIRED = 524288;\n\n // Password for meeting was not provided - meeting.failed code\n public const int FLAG_RECORDING_REASON_MEETING_PASSWORD_NOT_PROVIDED = 2097152;\n\n // meeting.failed - when the meeting is locked\n public const int FLAG_RECORDING_REASON_MEETING_IS_LOCKED = 4194304;\n\n // max recording duration reached\n public const int FLAG_RECORDING_REASON_MAX_DURATION_REACHED = 8388608;\n\n // recording size is too small\n public const int FLAG_RECORDING_REASON_EMPTY_RECORDING = 16777216;\n\n // meeting.failed - when bot is redirected to sign in page multiple times\n public const int FLAG_RECORDING_REASON_MAX_RESTART_COUNT_IS_REACHED = 33554432;\n\n // meeting.failed event with reason code CONNECTION_LOST\n public const int FLAG_RECORDING_REASON_MEETING_BOT_CONNECTION_LOST = 67108864;\n\n // recording is corrupted.\n public const int FLAG_RECORDING_REASON_MEDIA_FILE_UNSUPPORTED_MIME_TYPE = 134217728;\n\n // meeting ended in lobby\n public const int FLAG_RECORDING_REASON_MEETING_ENDED_IN_LOBBY = 268435456;\n\n // meeting not started\n public const int FLAG_RECORDING_REASON_REASON_MEETING_NOT_STARTED = 536870912;\n\n // unfinished zoom custom disclaimer\n public const int FLAG_RECORDING_REASON_FEATURE_RULE_NOT_FOUND_ERROR = 1073741824;\n\n // recording download failed - server error\n public const int FLAG_RECORDING_REASON_SERVER_ERROR = 2147483648;\n\n // recording download failed - client code 404\n public const int FLAG_RECORDING_REASON_NOT_FOUND = 2147483649;\n\n // recording download failed - client code 401, 403\n public const int FLAG_RECORDING_REASON_ACCESS_DENIED = 2147483650;\n\n // recording download failed - client code 429\n public const int FLAG_RECORDING_REASON_TOO_MANY_REQUESTS = 2147483651;\n\n // recording download failed - unknown client error\n public const int FLAG_RECORDING_REASON_CLIENT_ERROR = 2147483652;\n\n // recording download failed - unknown error\n public const int FLAG_RECORDING_REASON_UNKNOWN_ERROR = 2147483653;\n\n // It has been setup ahead of time through calendar\n public const string STATUS_SCHEDULED = 'scheduled';\n\n // It is awaiting audio.\n public const string STATUS_PENDING = 'pending';\n\n // Participant(s) dialed in, awaiting organizer.\n public const string STATUS_RINGING = 'ringing';\n\n // Call is in progress.\n public const string STATUS_IN_PROGRESS = 'in-progress';\n\n // It has ended.\n public const string STATUS_COMPLETED = 'completed';\n\n // Cancelled prior to starting.\n public const string STATUS_CANCELLED = 'canceled';\n\n public const string STATUS_DUPLICATED = 'duplicated'; // duplicated conference\n\n public const string STATUS_STARTING_SOON = 'starting-soon';\n\n public const string STATUS_BOT_CREATE_SENT = 'bot-create-sent';\n\n public const string STATUS_BOT_INSTANCE_WORKER_ASSIGNED = 'worker-assigned';\n\n public const string STATUS_BOT_INSTANCE_STARTED = 'bot-started';\n\n // When bot instance is waiting in lobby\n public const string STATUS_BOT_INSTANCE_WAITING_LOBBY = 'bot-waiting';\n\n public const string STATUS_BUSY = 'busy';\n public const string STATUS_NO_ANSWER = 'no-answer';\n public const string STATUS_FAILED = 'failed'; // Used by SMS too\n\n // SMS related\n public const string STATUS_ACCEPTED = 'accepted';\n public const string STATUS_QUEUED = 'queued';\n public const string STATUS_SENDING = 'sending';\n public const string STATUS_SENT = 'sent';\n public const string STATUS_DELIVERED = 'delivered';\n public const string STATUS_UNDELIVERED = 'undelivered';\n public const string STATUS_RECEIVING = 'receiving';\n public const string STATUS_RECEIVED = 'received';\n public const string STATUS_RESENT = 'resent';\n\n public const array SMS_STATUSES = [\n Activity::STATUS_RECEIVED,\n Activity::STATUS_SENT,\n Activity::STATUS_DELIVERED,\n ];\n\n public const array SOFT_PHONE_CONFERENCE_STATUSES = [\n Activity::STATUS_IN_PROGRESS,\n Activity::STATUS_COMPLETED,\n ];\n\n // @todo refactor prefix from `TYPE_` to `CHANNEL_`\n public const string TYPE_SOFTPHONE = 'softphone';\n public const string TYPE_SOFTPHONE_INBOUND = 'softphone-inbound';\n public const string TYPE_CONFERENCE = 'conference';\n public const string TYPE_SMS_INBOUND = 'sms-inbound';\n public const string TYPE_SMS_OUTBOUND = 'sms-outbound';\n public const string TYPE_EMAIL_INBOUND = 'email-inbound';\n public const string TYPE_EMAIL_OUTBOUND = 'email-outbound';\n\n public const array CHANNELS = [\n self::TYPE_SOFTPHONE,\n self::TYPE_SOFTPHONE_INBOUND,\n self::TYPE_CONFERENCE,\n self::TYPE_SMS_INBOUND,\n self::TYPE_SMS_OUTBOUND,\n self::TYPE_EMAIL_INBOUND,\n self::TYPE_EMAIL_OUTBOUND,\n ];\n\n public const array PLAYABLE_CHANNELS = [\n self::TYPE_SOFTPHONE,\n self::TYPE_SOFTPHONE_INBOUND,\n self::TYPE_CONFERENCE,\n ];\n\n // Recording States\n public const string RECORDING_OFF = 'off'; // Default state\n public const string RECORDING_IN_PROGRESS = 'in-progress';\n public const string RECORDING_PAUSED = 'paused';\n public const string RECORDING_STOPPED = 'stopped'; // To never be resumed.\n public const string RECORDING_RECORDED = 'recorded'; // At least some portion of it was recorded.\n public const string RECORDING_FAILED = 'failed'; // Recording was attempted but failed for some reason.\n\n // Live Stream States\n public const int ON_AIR_DEFAULT = 0;\n public const int ON_AIR_READY = 1;\n public const int ON_AIR_PREPARING = 2;\n public const int ON_AIR_STREAMING = 3;\n public const int ON_AIR_FINISHED = 4;\n public const int ON_AIR_NOT_STREAMED = 5;\n public const int ON_AIR_ERROR = -1;\n\n public const string SOURCE_GONG = 'gong';\n public const string SOURCE_CHORUS = 'chorus';\n public const string SOURCE_OUTLOOK = 'outlook';\n public const string SOURCE_GOOGLE = 'google';\n\n // Activity Providers\n public const string PROVIDER_TWILIO = 'twilio'; // XXX: This is run via the Jiminny Provider.\n public const string PROVIDER_OUTREACH = 'outreach';\n public const string PROVIDER_ZOOM_BOT = 'zoom-bot';\n public const string PROVIDER_SALESLOFT = 'salesloft';\n public const string PROVIDER_GOOGLE = 'google';\n public const string PROVIDER_AIRCALL = 'aircall';\n public const string PROVIDER_JUSTCALL = 'justcall';\n public const string PROVIDER_GOOGLE_MEET = 'google-meet';\n public const string PROVIDER_GONG = 'gong';\n public const string PROVIDER_HUBSPOT = 'hubspot';\n public const string PROVIDER_CLOSE = 'close';\n public const string PROVIDER_TEAMS = 'ms-teams';\n public const string PROVIDER_SALESFORCE = 'salesforce';\n public const string PROVIDER_GROOVE = 'groove';\n public const string PROVIDER_XANT = 'xant';\n public const string PROVIDER_OFFICE = 'office';\n public const string PROVIDER_NATTERBOX = 'natterbox';\n public const string PROVIDER_RINGCENTRAL = 'ringcentral';\n public const string PROVIDER_RINGCENTRAL_VIDEO = 'ringcentral-video';\n public const string PROVIDER_GOTOMEETING = 'go-to-meeting';\n public const string PROVIDER_DEMODESK = 'demo-desk';\n public const string PROVIDER_DIALPAD = 'dialpad';\n public const string PROVIDER_ZOOM_PHONE = 'zoom-phone';\n public const string PROVIDER_CLOUDCALL = 'cloudcall';\n public const string PROVIDER_CLOUDCALL_US = 'cloudcall-us';\n public const string PROVIDER_EIGHT_BY_EIGHT = 'eight-by-eight'; // \"8x8\" UK\n public const string PROVIDER_EIGHT_BY_EIGHT_CA = 'eight-by-eight-ca'; // \"8x8\" Canada\n public const string PROVIDER_EIGHT_BY_EIGHT_AP = 'eight-by-eight-ap'; // \"8x8\" Australia\n public const string PROVIDER_EIGHT_BY_EIGHT_US_EAST = 'eight-by-eight-use'; // \"8x8\" US East\n public const string PROVIDER_EIGHT_BY_EIGHT_US_WEST = 'eight-by-eight-usw'; // \"8x8\" US West\n public const string PROVIDER_CONNECT_AND_SELL = 'connect-and-sell';\n public const string PROVIDER_CLOUD_TALK = 'cloud-talk';\n public const string PROVIDER_AMAZON_CONNECT = 'amazon-connect';\n public const string PROVIDER_VONAGE = 'vonage';\n public const string PROVIDER_MIGRATOR = 'migrator';\n public const string PROVIDER_UPLOADER = 'uploader';\n public const string PROVIDER_TALKDESK = 'talkdesk';\n public const string PROVIDER_TWILIO_FLEX = 'twilio-flex';\n public const string PROVIDER_TWILIO_FLEX_DIRECT = 'twilio-flex-direct';\n public const string PROVIDER_TWILIO_VIDEO = 'twilio-video';\n public const string PROVIDER_AVAYA = 'avaya';\n public const string PROVIDER_TELUS = 'telus';\n public const string PROVIDER_FIVE_NINE = 'five-nine';\n public const string PROVIDER_APOLLO = 'apollo';\n public const string PROVIDER_ORUM = 'orum';\n public const string PROVIDER_BLOOBIRDS = 'bloobirds';\n\n /**\n * @const API_PROVIDERS\n * A list of integrations that import calls via API instead of webhooks\n */\n public const array API_PROVIDERS = [\n self::PROVIDER_OUTREACH,\n self::PROVIDER_SALESLOFT,\n self::PROVIDER_HUBSPOT,\n self::PROVIDER_GROOVE,\n self::PROVIDER_XANT,\n self::PROVIDER_NATTERBOX,\n self::PROVIDER_CLOUDCALL,\n self::PROVIDER_CLOUDCALL_US,\n self::PROVIDER_EIGHT_BY_EIGHT,\n self::PROVIDER_EIGHT_BY_EIGHT_CA,\n self::PROVIDER_EIGHT_BY_EIGHT_AP,\n self::PROVIDER_EIGHT_BY_EIGHT_US_EAST,\n self::PROVIDER_EIGHT_BY_EIGHT_US_WEST,\n self::PROVIDER_CONNECT_AND_SELL,\n self::PROVIDER_CLOUD_TALK,\n self::PROVIDER_AMAZON_CONNECT,\n self::PROVIDER_VONAGE,\n self::PROVIDER_TALKDESK,\n self::PROVIDER_TWILIO_VIDEO,\n self::PROVIDER_TWILIO_FLEX,\n self::PROVIDER_TWILIO_FLEX_DIRECT,\n self::PROVIDER_FIVE_NINE,\n self::PROVIDER_APOLLO,\n self::PROVIDER_ORUM,\n self::PROVIDER_BLOOBIRDS,\n self::PROVIDER_RINGCENTRAL,\n self::PROVIDER_AVAYA,\n self::PROVIDER_TELUS,\n ];\n\n public const array FINITE_STATES = [\n self::TYPE_SOFTPHONE => [\n self::STATUS_COMPLETED,\n self::STATUS_FAILED,\n self::STATUS_NO_ANSWER,\n self::STATUS_BUSY,\n ],\n self::TYPE_SOFTPHONE_INBOUND => [\n self::STATUS_COMPLETED,\n self::STATUS_FAILED,\n self::STATUS_NO_ANSWER,\n self::STATUS_BUSY,\n ],\n self::TYPE_CONFERENCE => self::FINITE_STATES_CONFERENCE,\n ];\n\n public const array FINITE_STATES_CONFERENCE = [\n self::STATUS_COMPLETED,\n self::STATUS_FAILED,\n self::STATUS_CANCELLED,\n ];\n\n public const array MEETING_BOT_JOIN_ATTEMPTED = [\n self::STATUS_BOT_INSTANCE_WAITING_LOBBY,\n self::STATUS_BOT_INSTANCE_STARTED,\n ];\n\n public static array $enumStatuses = [\n self::STATUS_SCHEDULED,\n self::STATUS_PENDING,\n self::STATUS_RINGING,\n self::STATUS_IN_PROGRESS,\n self::STATUS_COMPLETED,\n self::STATUS_CANCELLED,\n self::STATUS_BUSY,\n self::STATUS_NO_ANSWER,\n self::STATUS_FAILED,\n self::STATUS_ACCEPTED,\n self::STATUS_QUEUED,\n self::STATUS_SENDING,\n self::STATUS_SENT,\n self::STATUS_RESENT,\n self::STATUS_DELIVERED,\n self::STATUS_UNDELIVERED,\n self::STATUS_RECEIVING,\n self::STATUS_RECEIVED,\n self::STATUS_BOT_INSTANCE_WAITING_LOBBY,\n self::STATUS_STARTING_SOON,\n self::STATUS_BOT_INSTANCE_WORKER_ASSIGNED,\n self::STATUS_BOT_INSTANCE_STARTED,\n self::STATUS_DUPLICATED,\n ];\n\n public static array $enumProviders = [\n self::PROVIDER_TWILIO,\n self::PROVIDER_OUTREACH,\n self::PROVIDER_ZOOM_BOT,\n self::PROVIDER_SALESLOFT,\n self::PROVIDER_AIRCALL,\n self::PROVIDER_JUSTCALL,\n self::PROVIDER_GOOGLE_MEET,\n self::PROVIDER_GONG,\n self::PROVIDER_HUBSPOT,\n self::PROVIDER_CLOSE,\n self::PROVIDER_TEAMS,\n self::PROVIDER_SALESFORCE,\n self::PROVIDER_GROOVE,\n self::PROVIDER_XANT,\n self::PROVIDER_GOOGLE,\n self::PROVIDER_OFFICE,\n self::PROVIDER_NATTERBOX,\n self::PROVIDER_RINGCENTRAL,\n self::PROVIDER_RINGCENTRAL_VIDEO,\n self::PROVIDER_GOTOMEETING,\n self::PROVIDER_DEMODESK,\n self::PROVIDER_DIALPAD,\n self::PROVIDER_ZOOM_PHONE,\n self::PROVIDER_CLOUDCALL,\n self::PROVIDER_CLOUDCALL_US,\n self::PROVIDER_EIGHT_BY_EIGHT,\n self::PROVIDER_EIGHT_BY_EIGHT_CA,\n self::PROVIDER_EIGHT_BY_EIGHT_AP,\n self::PROVIDER_EIGHT_BY_EIGHT_US_EAST,\n self::PROVIDER_EIGHT_BY_EIGHT_US_WEST,\n self::PROVIDER_CONNECT_AND_SELL,\n self::PROVIDER_CLOUD_TALK,\n self::PROVIDER_AMAZON_CONNECT,\n self::PROVIDER_VONAGE,\n self::PROVIDER_TALKDESK,\n self::PROVIDER_TWILIO_FLEX,\n self::PROVIDER_TWILIO_FLEX_DIRECT,\n self::PROVIDER_TWILIO_VIDEO,\n self::PROVIDER_AVAYA,\n self::PROVIDER_TELUS,\n self::PROVIDER_FIVE_NINE,\n self::PROVIDER_APOLLO,\n self::PROVIDER_ORUM,\n self::PROVIDER_BLOOBIRDS,\n ];\n\n public static $enumRecordingStates = [\n self::RECORDING_OFF, // Default state\n self::RECORDING_IN_PROGRESS,\n self::RECORDING_PAUSED,\n self::RECORDING_STOPPED,\n self::RECORDING_RECORDED,\n self::RECORDING_FAILED,\n ];\n\n // @Important:\n // This collection is not used anywhere, and is fully duplicated by the Channels const.\n // Validate if it is referred somehow via the enum trait, and if not, remove it entirely.\n // An even better strategy will be to move all those constants to a dedicated class\n protected array $enumTypes = [\n self::TYPE_SOFTPHONE,\n self::TYPE_SOFTPHONE_INBOUND,\n self::TYPE_CONFERENCE,\n self::TYPE_SMS_INBOUND,\n self::TYPE_SMS_OUTBOUND,\n self::TYPE_EMAIL_INBOUND,\n self::TYPE_EMAIL_OUTBOUND,\n ];\n\n protected static $enumFailedStatuses = [\n self::STATUS_NO_ANSWER,\n self::STATUS_FAILED,\n self::STATUS_BUSY,\n self::STATUS_CANCELLED,\n ];\n\n protected $table = 'activities';\n\n protected $fillable = [\n // Type of activity.\n 'type', // @todo refactor to `channel`\n // The activity type.\n 'playbook_category_id',\n // User who hosts the activity.\n 'user_id',\n // Related Lead record (if applicable)\n 'lead_id',\n // Related Account record (if applicable)\n 'account_id',\n // Related Contact record (if applicable)\n 'contact_id',\n // Related Opportunity record (if applicable)\n 'opportunity_id',\n // Stage of activity.\n 'stage_id',\n // Value of opportunity.\n 'value',\n // If the activity relates to a CRM task.\n 'crm_provider_id',\n // If the activity was created through an external device.\n 'device_id',\n // the activity's language code\n 'language',\n // transcription id\n 'transcription_id',\n // Duration of the call, with microseconds precision.\n 'duration',\n // One of enumStatuses above.\n 'status',\n // Have we reminded them to log the call?\n 'log_reminder_sent_at',\n // If activity is private or inter-org, flagged here.\n 'is_internal',\n // Managers and above can mark a call as private, to exclude it from other team members\n 'is_private',\n 'is_processed',\n // Boolean for this activity being instant invite handled.\n 'is_instant_invite',\n // If activity is in recording state, flagged here.\n 'recording_state',\n // If activity recording is overidden from default.\n 'recording_preference',\n // if recording did (not) happen, why that is\n 'recording_reason_code',\n // Average score, updated during\n 'average_score',\n // Summary that the organizer has taken after the call.\n 'summary',\n // Subject of the activity, usually taken from calendar event.\n 'title',\n // Description of the activity, usually taken from calendar event.\n 'description',\n // Start time, usually taken from calendar event.\n 'scheduled_start_time',\n // End time, usually taken from calendar event.\n 'scheduled_end_time',\n // When the call actually started.\n 'actual_start_time',\n // When the call actually ended.\n 'actual_end_time',\n // SMS: Message reference\n 'telephony_provider_id',\n // SMS: Participant who sent message\n 'from_participant_id',\n // SMS: Participant who should receive the message\n 'to_participant_id',\n // When an external guest joins an organizers meeting room and the organizer is not present,\n // send them an SMS notification that someone has joined.\n 'organizer_notified_at',\n // where was the activity imported from\n 'source',\n // The id in the source system (e.g. the bot id in Recall.ai)\n 'external_id',\n // The provider, by default it is twilio.\n 'provider',\n // Meeting location url\n 'location',\n // The snapshot for displaying a poster image.\n 'poster_path',\n 'crm_configuration_id',\n // If there is an automated message that the conversation is being recorded\n 'has_recording_prompt',\n // If the activity is being live-streamed\n 'on_air',\n 'calendar_event_id',\n ];\n\n protected $appends = [\n 'id_string',\n 'organizer',\n ];\n\n protected $hidden = [\n 'uuid',\n ];\n\n protected $visible = [\n 'id_string',\n 'type',\n 'duration',\n 'average_score',\n 'status',\n 'log_reminder_sent_at',\n 'title',\n 'description',\n 'is_internal',\n 'scheduled_start_time',\n 'scheduled_end_time',\n 'actual_start_time',\n 'actual_end_time',\n 'user',\n 'category',\n 'account',\n 'contact',\n 'opportunity',\n 'lead',\n 'stage',\n 'stats',\n 'participants',\n 'playlists',\n 'tracks',\n 'comments',\n 'plays',\n 'coachingFeedbacks',\n 'shares',\n 'favorites',\n 'language',\n 'transcription',\n 'is_private',\n 'is_instant_invite',\n 'on_air',\n 'calendar_event_id',\n ];\n\n protected function casts(): array\n {\n return [\n 'scheduled_start_time' => 'datetime',\n 'scheduled_end_time' => 'datetime',\n 'actual_start_time' => 'datetime',\n 'actual_end_time' => 'datetime',\n 'organizer_notified_at' => 'datetime',\n 'log_reminder_sent_at' => 'datetime',\n 'is_internal' => 'boolean',\n 'duration' => 'integer',\n 'average_score' => 'decimal:2',\n 'is_private' => 'boolean',\n 'is_processed' => 'boolean',\n 'is_instant_invite' => 'boolean',\n 'value' => 'decimal:2',\n 'recording_preference' => 'boolean',\n 'recording_reason_code' => 'integer',\n 'has_recording_prompt' => 'boolean',\n 'on_air' => 'integer',\n ];\n }\n\n protected static function boot()\n {\n parent::boot();\n\n static::updated(static function (Activity $activity) {\n // If activity is about to start (pending, ringing, in-progress) or event is scheduled in less than 1 week\n if (in_array($activity->status, [Activity::STATUS_PENDING, Activity::STATUS_RINGING, Activity::STATUS_IN_PROGRESS], true) ||\n ($activity->scheduled_start_time && (int) $activity->scheduled_start_time->diffInWeeks(new Carbon(), true) < 1)) {\n if ($activity->isDirty('status')) {\n event(new StatusUpdated($activity));\n }\n\n if ($activity->isDirty('stage_id')) {\n event(new StageUpdated($activity));\n }\n\n if ($activity->isDirty(['lead_id', 'account_id', 'contact_id'])) {\n event(new ProspectUpdated($activity));\n }\n\n if ($activity->isDirty('opportunity_id')) {\n event(new ActivityUpdated($activity, 'activity.opportunity-updated', Auth::user()));\n }\n\n if ($activity->isDirty('title')) {\n event(new TitleUpdated($activity));\n }\n }\n\n if ($activity->isDirty('playbook_category_id')) {\n event(new ActivityTypeUpdated($activity));\n }\n });\n\n static::deleted(static function (Activity $activity) {\n // Hard delete associated playlistActivities\n $activity->playlistActivities()->delete();\n });\n }\n\n public function getOrganizerAttribute(): ?Participant\n {\n $participant = $this->participants()->where('user_id', $this->user_id)->first();\n\n if (! $participant instanceof Participant && $participant !== null) {\n throw new RuntimeException(sprintf('$participant must be an instance of \"%s\" or null', Participant::class));\n }\n\n return $participant;\n }\n\n public function getFormattedValueAttribute()\n {\n $currencyCode = 'USD';\n if ($this->opportunity) {\n $currencyCode = $this->opportunity->getCurrencyCode();\n }\n\n $formatter = new CurrencyFormatter();\n $formatter->setTextAttribute(NumberFormatter::CURRENCY_CODE, $currencyCode);\n $formatter->setAttribute(NumberFormatter::MAX_FRACTION_DIGITS, 0);\n\n return $formatter->format($this->value, $currencyCode);\n }\n\n public function getProspectNameAttribute(): ?string\n {\n $prospectName = null;\n\n if ($this->lead_id) {\n $prospectName = $this->lead->name;\n } elseif ($this->contact_id) {\n $prospectName = $this->contact->name;\n } elseif ($this->account_id) {\n $prospectName = $this->account->name;\n }\n\n return $prospectName;\n }\n\n public function getProspectName(): ?string\n {\n /** @var string|null */\n return $this->getAttribute('prospect_name');\n }\n\n /**\n * Get activity title depending on prospect or title\n */\n public function getActivityTitleAttribute(): ?string\n {\n $activityTitle = null;\n if ($this->prospect && $this->prospect->getName()) {\n if ($this->account_id) {\n $activityTitle = $this->account->name;\n } elseif ($this->lead_id) {\n $activityTitle = $this->lead->company;\n } elseif ($this->contact_id) {\n $activityTitle = $this->contact->account ? $this->contact->account->name : $this->contact->name;\n }\n } elseif ($this->title) {\n $activityTitle = $this->title;\n }\n\n return $activityTitle;\n }\n\n public function wasRecentlyCreated(): bool\n {\n return $this->wasRecentlyCreated;\n }\n\n public function getProspectTypeAttribute()\n {\n $prospectType = null;\n\n if ($this->lead_id) {\n $prospectType = 'Lead';\n } elseif ($this->contact_id) {\n $prospectType = 'Contact';\n } elseif ($this->account_id) {\n $prospectType = 'Account';\n }\n\n return $prospectType;\n }\n\n /**\n * Return the best match for prospect. Results are in the following order of priority:\n * 1. Lead\n * 2. Contact\n * 3. Account\n * 4. NULL\n */\n public function getProspectAttribute(): ?ProspectInterface\n {\n if ($this->hasLead()) {\n return $this->getLead();\n }\n\n if ($this->hasContact()) {\n return $this->getContact();\n }\n\n if ($this->hasAccount()) {\n return $this->getAccount();\n }\n\n return null;\n }\n\n public function getTitleAttribute($value): ?string\n {\n return \\getActivityTitleAttribute(\n $this->user->name,\n $this->getType(),\n $value,\n $this->prospect->name ?? null,\n $this->from->national_phone_number ?? null\n );\n }\n\n public function getTitle(): ?string\n {\n return $this->getAttribute('title');\n }\n\n public function getSummary(): ?string\n {\n return $this->getAttribute('summary');\n }\n\n public function isInternal(): bool\n {\n return $this->getAttribute('is_internal');\n }\n\n public function getIsPrivate(): bool\n {\n return $this->getAttribute('is_private');\n }\n\n public function getDescription(): ?string\n {\n return $this->getAttribute('description');\n }\n\n public function hasTitle(): bool\n {\n return $this->getOriginal('title') !== null;\n }\n\n public function getPlayCountAttribute()\n {\n return $this->getPlaysCountAttribute();\n }\n\n public function getPlaysCountAttribute()\n {\n if (! isset($this->attributes['plays_count'])) {\n $this->loadCount('plays');\n }\n\n return $this->attributes['plays_count'];\n }\n\n public function getCommentCountAttribute()\n {\n return $this->getCommentsCountAttribute();\n }\n\n public function getCommentsCountAttribute()\n {\n if (! isset($this->attributes['comments_count'])) {\n $this->loadCount('comments');\n }\n\n return $this->attributes['comments_count'];\n }\n\n public function getVisibleCommentsCountAttribute()\n {\n if (! isset($this->attributes['visible_comments_count'])) {\n $activityCommentsService = app(ActivityCommentService::class);\n $user = Auth::user() instanceof User ? Auth::user() : null;\n $this->attributes['visible_comments_count'] = $activityCommentsService\n ->getVisibleCommentsCount($this, $user);\n }\n\n return $this->attributes['visible_comments_count'];\n }\n\n public function getShareCountAttribute()\n {\n return $this->getSharesCountAttribute();\n }\n\n public function getSharesCountAttribute()\n {\n if (! isset($this->attributes['shares_count'])) {\n $this->loadCount('shares');\n }\n\n return $this->attributes['shares_count'];\n }\n\n\n /**\n * Get the count of favorites playlists this activity appears in\n */\n public function getFavoriteCountAttribute(): int\n {\n return $this->getFavoritesCountAttribute();\n }\n\n public function getFavoritesCountAttribute()\n {\n if (! isset($this->attributes['favorites_count'])) {\n $this->loadCount('favorites');\n }\n\n return $this->attributes['favorites_count'];\n }\n\n public function getActiveParticipantsCountAttribute()\n {\n if (! isset($this->attributes['active_participants_count'])) {\n $this->loadCount('activeParticipants');\n }\n\n return $this->attributes['active_participants_count'];\n }\n\n public function getTracksWithTelephonyCountAttribute()\n {\n if (! isset($this->attributes['tracks_with_telephony_count'])) {\n $this->loadCount('tracksWithTelephony');\n }\n\n return $this->attributes['tracks_with_telephony_count'];\n }\n\n /**\n * @TEMP\n * $this->loadCount('tracksWithTelephony') throws null pointer exception\n */\n public function countTracksWithTelephony(): int\n {\n return $this->tracks()->whereNotNull('telephony_provider_id')->count();\n }\n\n public function getDuration(): float\n {\n return $this->getAttribute('duration');\n }\n\n public function getDurationForHumansAttribute()\n {\n return Carbon::now()->subSeconds($this->duration)->diffForHumans(now(), true);\n }\n\n public function getDurationForHumansShortAttribute(): string\n {\n return Carbon::now()->subSeconds($this->duration)->diffForHumans(now(), true, true);\n }\n\n public function hasRecordingPreference(): bool\n {\n return $this->getAttribute('recording_preference') !== null;\n }\n\n public function getRecordingPreference()\n {\n return $this->getAttribute('recording_preference');\n }\n\n /** @return BelongsTo<User, self> */\n public function user(): BelongsTo\n {\n return $this->belongsTo(User::class)->with('group');\n }\n\n public function device()\n {\n return $this->belongsTo(Device::class);\n }\n\n public function category()\n {\n return $this->belongsTo(PlaybookCategory::class, 'playbook_category_id');\n }\n\n public function getCategory(): ?PlaybookCategory\n {\n return $this->getAttribute('category');\n }\n\n public function getPlaybookCategoryId(): ?int\n {\n return $this->getAttribute('playbook_category_id');\n }\n\n public function hasStats(): bool\n {\n return $this->getAttribute('stats') !== null;\n }\n\n public function getStats(): ?Stats\n {\n return $this->getAttribute('stats');\n }\n\n public function stats(): HasOne\n {\n return $this->hasOne(Stats::class);\n }\n\n public function participantStats(): Eloquent\\Relations\\HasManyThrough\n {\n return $this->hasManyThrough(\n Models\\Participant\\ParticipantStats::class,\n Participant::class,\n 'activity_id',\n 'participant_id'\n );\n }\n\n public function getParticipantStats(): Eloquent\\Collection\n {\n return $this->getAttribute('participantStats');\n }\n\n public function account()\n {\n return $this->belongsTo(Account::class);\n }\n\n public function contact()\n {\n return $this->belongsTo(Contact::class)->with(['account']);\n }\n\n public function lead()\n {\n return $this->belongsTo(Lead::class)->with(['stage', 'recordType']);\n }\n\n /**\n * @return BelongsTo<Opportunity, self>\n */\n public function opportunity(): BelongsTo\n {\n /** @var BelongsTo<Opportunity, self> */\n return $this->belongsTo(Opportunity::class);\n }\n\n public function stage()\n {\n return $this->belongsTo(Stage::class);\n }\n\n /**\n * @return HasMany<Session>\n */\n public function sessions(): HasMany\n {\n return $this->hasMany(Session::class);\n }\n\n /**\n * @return HasMany|ParticipantSpeech[]|Eloquent\\Collection\n */\n public function participantSpeeches()\n {\n return $this->hasMany(ParticipantSpeech::class);\n }\n\n public function getParticipantSpeeches(): Eloquent\\Collection\n {\n return $this->getAttribute('participantSpeeches');\n }\n\n /**\n * @return HasMany|Log[]|Eloquent\\Collection\n */\n public function logs()\n {\n return $this->hasMany(Log::class);\n }\n\n /**\n * @return HasMany|Moment[]|Eloquent\\Collection\n */\n public function moments()\n {\n return $this->hasMany(Moment::class);\n }\n\n /**\n * @return HasMany|Note[]|Eloquent\\Collection\n */\n public function notes()\n {\n return $this->hasMany(Note::class);\n }\n\n /**\n * @return Eloquent\\Collection|Note[]\n */\n public function getNotes(): Eloquent\\Collection\n {\n return $this->getAttribute('notes');\n }\n\n /**\n * @return HasMany|Message[]|Eloquent\\Collection\n */\n public function messages()\n {\n return $this->hasMany(Message::class);\n }\n\n public function coachingMessages(): HasMany\n {\n return $this->hasMany(Message::class)\n ->where('is_private', 1);\n }\n\n public function getCoachingMessages(): Eloquent\\Collection\n {\n return $this->getAttribute('coachingMessages');\n }\n\n public function participants(): HasMany\n {\n return $this->hasMany(Participant::class);\n }\n\n public function getSnapshots(): Eloquent\\Collection\n {\n return $this->getAttribute('snapshots');\n }\n\n /** @return HasMany<Track> */\n public function tracks(): HasMany\n {\n return $this->hasMany(Track::class);\n }\n\n public function tracksWithTelephony(): HasMany\n {\n return $this->hasMany(Track::class)->whereNotNull('telephony_provider_id');\n }\n\n public function getTracksWithTelephony(): Eloquent\\Collection\n {\n return $this->getAttribute('tracksWithTelephony');\n }\n\n /** @return Collection|Track[] */\n public function getTracks(): Eloquent\\Collection\n {\n return $this->getAttribute('tracks');\n }\n\n public function masterTrack(): HasOne\n {\n return $this->hasOne(Track::class)->where('is_master', 1)\n ->whereIn('format', [Track::FORMAT_WAV, Track::FORMAT_M3U8])\n ->latest();\n }\n\n public function getMasterTrack(): ?Track\n {\n /** @var Track|null */\n return $this->getAttribute('masterTrack');\n }\n\n public function transcription(): Eloquent\\Relations\\BelongsTo\n {\n return $this->belongsTo(Transcription::class, 'transcription_id');\n }\n\n public function findTranscriptionPromptSummaries(): Collection\n {\n $transcriptionId = $this->getTranscriptionId();\n if (is_null($transcriptionId)) {\n return new Collection();\n }\n\n return Models\\AiPrompt::query()\n ->where('transcription_id', $transcriptionId)\n ->get();\n }\n\n public function getTranscription(): Transcription\n {\n return $this->getAttribute('transcription');\n }\n\n public function hasTranscription(): bool\n {\n return $this->getAttribute('transcription') !== null;\n }\n\n public function setTranscriptionId(int $transcriptionId): Activity\n {\n $this->setAttribute('transcription_id', $transcriptionId);\n\n return $this;\n }\n\n public function unsetTranscriptionId(): self\n {\n $this->setAttribute('transcription_id', null);\n\n return $this;\n }\n\n public function getTranscriptionId(): ?int\n {\n return $this->getAttribute('transcription_id');\n }\n\n /** @deprecated */\n public function hasTranscriptionId(): bool\n {\n return $this->getAttribute('transcription_id') !== null;\n }\n\n public function coachRequests()\n {\n return $this->hasMany(CoachRequest::class);\n }\n\n public function availabilityNotifications()\n {\n return $this->hasMany(AvailabilityNotification::class);\n }\n\n public function processingStates()\n {\n return $this->hasMany(Models\\Activity\\ActivityProcessingState::class);\n }\n\n public function uploadSettings()\n {\n return $this->hasMany(ActivityUploadSetting::class);\n }\n\n public function comments()\n {\n return $this->hasMany(Comment::class);\n }\n\n public function getComments(): Eloquent\\Collection\n {\n return $this->getAttribute('comments');\n }\n\n public function visibleComments()\n {\n $rel = $this->hasMany(Comment::class);\n // Doesn't have auth()->user() in some tests, breaks the build\n if ($user = auth()->user()) {\n return $rel->visibleThreads($user->id);\n }\n\n return $rel;\n }\n\n public function snapshots(): HasMany\n {\n return $this->hasMany(Snapshot::class);\n }\n\n public function calendarEvent()\n {\n return $this->belongsTo(CalendarEvent::class);\n }\n\n public function getCalendarEvent(): ?CalendarEvent\n {\n return $this->getAttribute('calendarEvent');\n }\n\n public function latestCoachingFeedbacks(): HasMany\n {\n return $this->hasMany(CoachingFeedback::class)->latest();\n }\n\n public function playlists(): BelongsToMany\n {\n return $this->belongsToMany(Playlist::class, 'playlist_activities')\n ->withPivot('id', 'uuid', 'user_id', 'start_time', 'end_time')\n ->using(PlaylistActivity::class)\n ->withTimestamps();\n }\n\n public function coachingFeedbacks(): HasMany\n {\n return $this->hasMany(CoachingFeedback::class);\n }\n\n /**\n * @return Eloquent\\Collection|CoachingFeedback[]\n */\n public function getCoachingFeedback(?int $visibility = null): Eloquent\\Collection\n {\n $feedbacks = $this->coachingFeedbacks();\n if ($visibility !== null) {\n $feedbacks = $feedbacks->where('visibility', $visibility);\n }\n\n return $feedbacks->get();\n }\n\n /** @return Eloquent\\Collection<int, PlaylistActivity> */\n public function favoritedBy(User $user): Eloquent\\Collection\n {\n return $this->favorites()->where('user_id', $user->getId())->get();\n }\n\n /**\n * Checks whether consumer has added this activity to their favorites playlist\n * In addition a default playlist gets created if not already present\n */\n public function wasFavoritedBy(User $user): bool\n {\n $playlist = $user->favoritePlaylist();\n\n return $playlist\n ->activities()\n ->where('activity_id', '=', $this->getId())\n ->exists();\n }\n\n /**\n * @return HasMany<PlaylistActivity>\n */\n public function playlistActivities(): HasMany\n {\n return $this->hasMany(PlaylistActivity::class);\n }\n\n /**\n * @return HasManyThrough<Playlist>\n */\n public function favoritePlaylists(): HasManyThrough\n {\n return $this->hasManyThrough(\n Playlist::class,\n PlaylistActivity::class,\n 'activity_id',\n 'id',\n 'id',\n 'playlist_id'\n )->where('is_default', 1);\n }\n\n /**\n * @return Eloquent\\Collection<int, Playlist>\n */\n public function getFavoritePlaylists(): Eloquent\\Collection\n {\n return $this->getAttribute('favoritePlaylists');\n }\n\n /**\n * Get activities from the default/favorite playlist\n *\n * @return Eloquent\\Builder|static\n */\n public function favorites()\n {\n return $this->playlistActivities()->whereHas('playlist', function ($query) {\n $query->where('is_default', 1);\n });\n }\n\n /**\n * @return Model|SubscriptionSet|null|object\n */\n public function subscribedBy(User $user)\n {\n if ($this->prospect === null) {\n return null;\n }\n\n return SubscriptionSet::select('activity_subscription_sets.*')\n ->where('user_id', $user->id)\n ->join('activity_subscriptions', function ($join) {\n $join\n ->on('subscription_set_id', '=', 'activity_subscription_sets.id');\n\n if ($this->account_id) {\n if ($this->opportunity_id) {\n $join\n ->where('followable_type', 'opportunity')\n ->where('followable_id', $this->opportunity_id);\n } else {\n $join\n ->where('followable_type', 'account')\n ->where('followable_id', $this->account_id);\n }\n } elseif ($this->contact_id) {\n $join\n ->where('followable_type', 'contact')\n ->where('followable_id', $this->contact_id);\n } elseif ($this->lead_id) {\n $join\n ->where('followable_type', 'lead')\n ->where('followable_id', $this->lead_id);\n }\n })\n ->first();\n }\n\n /**\n * @return array|Eloquent\\Builder[]|Eloquent\\Collection|SubscriptionSet[]\n */\n public function subscribers()\n {\n if ($this->prospect === null) {\n return [];\n }\n\n return SubscriptionSet::with(['subscriptions', 'user'])\n ->whereHas('subscriptions', function ($query) {\n if ($this->account_id) {\n if ($this->opportunity_id) {\n $query\n ->where('followable_type', 'opportunity')\n ->where('followable_id', $this->opportunity_id);\n } else {\n $query\n ->where('followable_type', 'account')\n ->where('followable_id', $this->account_id);\n }\n } elseif ($this->contact_id) {\n $query\n ->where('followable_type', 'contact')\n ->where('followable_id', $this->contact_id);\n } elseif ($this->lead_id) {\n $query\n ->where('followable_type', 'lead')\n ->where('followable_id', $this->lead_id);\n } else {\n // Nothing to join on?\n // refactor - use Jiminny specific exception\n throw new InvalidArgumentException('Cannot join on a specific customer filter.');\n }\n })\n ->whereHas('user', function ($query) {\n $query\n ->where('team_id', $this->user->team_id)\n ->where('status', User::STATUS_ACTIVE);\n })\n ->get();\n }\n\n /**\n * @return HasMany|Builder|Eloquent\\Collection|Play[]\n */\n public function plays()\n {\n return $this->hasMany(Play::class);\n }\n\n public function getPlays(): Eloquent\\Collection\n {\n return $this->getAttribute('plays');\n }\n\n public function playsBy(User $user)\n {\n /** @var Builder $builder */\n $builder = $this->plays()->where('user_id', $user->id);\n\n return $builder->get();\n }\n\n /**\n * Check if activity was played by a user\n */\n public function wasPlayedBy(User $user): bool\n {\n return $this->plays()->where('user_id', $user->id)->exists();\n }\n\n public function shares()\n {\n return $this->hasMany(Share::class);\n }\n\n /** @return BelongsTo<Participant, self> */\n public function from(): BelongsTo\n {\n return $this->belongsTo(Participant::class, 'from_participant_id');\n }\n\n /** @return BelongsTo<Participant, self> */\n public function to(): BelongsTo\n {\n return $this->belongsTo(Participant::class, 'to_participant_id');\n }\n\n /**\n * Get all of the connections through the participants.\n */\n public function connections()\n {\n return $this->hasManyThrough(\n Connection::class,\n Participant::class,\n 'activity_id',\n 'participant_id'\n );\n }\n\n public function getConnections(): Eloquent\\Collection\n {\n return $this->getAttribute('connections');\n }\n\n /**\n * Get all of the shares through the participants.\n */\n public function participantShares()\n {\n return $this->hasManyThrough(\n Participant\\Share::class,\n Participant::class,\n 'activity_id',\n 'participant_id'\n );\n }\n\n public function getParticipantShares(): Eloquent\\Collection\n {\n return $this->getAttribute('participantShares');\n }\n\n public function topicTriggers(): HasMany\n {\n return $this->hasMany(TopicTrigger::class);\n }\n\n public function activityScorecardRuleTriggers(): HasMany\n {\n return $this->hasMany(Models\\Scorecard\\ActivityScorecardRuleTrigger::class);\n }\n\n public function activityScorecardRules(): HasMany\n {\n return $this->hasMany(Models\\Scorecard\\ActivityScorecardRule::class);\n }\n\n public function questions(): HasMany\n {\n return $this->hasMany(Question::class);\n }\n\n /**\n * Get all the custom data attached to it.\n */\n public function data(): HasMany\n {\n return $this->hasMany(FieldData::class);\n }\n\n public function getData(): Eloquent\\Collection\n {\n /** @var Eloquent\\Collection */\n return $this->getAttribute('data');\n }\n\n #[Scope]\n protected function heldBetween($query, Carbon $start, Carbon $end)\n {\n // Sanity check.\n $from = min($start, $end);\n $until = max($start, $end);\n\n return $query\n ->where('actual_start_date', '>=', $from)\n ->where('actual_end_date', '<=', $until);\n }\n\n #[Scope]\n protected function scheduledBetween($query, Carbon $start, Carbon $end)\n {\n // Sanity check.\n $from = min($start, $end);\n $until = max($start, $end);\n\n return $query\n ->where('scheduled_start_date', '>=', $from)\n ->where('scheduled_end_date', '<=', $until);\n }\n\n /**\n * @param Builder<self> $query\n *\n * @return Builder<self>\n */\n #[Scope]\n protected function forTeam(Builder $query, int $teamId): Builder\n {\n /** @var Builder<self> */\n return $query->whereHas('user', static function (Builder $query) use ($teamId): void {\n $query->where('team_id', $teamId);\n });\n }\n\n /**\n * @param Builder<self> $query\n *\n * @return Builder<self>\n */\n #[Scope]\n protected function inOpenDeals(Builder $query): Builder\n {\n /** @var Builder<self> */\n return $query->whereHas(\n 'opportunity',\n static fn (Builder $query): Builder => $query\n ->where('is_closed', false)\n ->where('deleted_at', '=', null),\n );\n }\n\n /**\n * @param Builder<self> $query\n *\n * @return Builder<self>\n */\n #[Scope]\n protected function notInOpenDeals(Builder $query): Builder\n {\n /** @var Builder<self> */\n return $query->where(\n static fn (Builder $query): Builder => $query->whereNull('opportunity_id')\n ->orWhereHas(\n 'opportunity',\n static fn (Builder $query): Builder => $query->where('is_closed', true),\n )\n ->orWhereHas(\n 'opportunity',\n static fn (Builder $query): Builder => $query->withTrashed()->where('deleted_at', '!=', null),\n ),\n );\n }\n\n /**\n * Finds a participant and updates it with data. If participant doesn't exist creates a new participant from data.\n *\n * @param array $data participant data used to identify the participant and update it\n * @param bool $enterRoom true if participant is entering the room. false if we just want to update some participant data\n * @param Carbon|null $enterTime if $enterNow is true then this is the join time when the actual enter has occurred\n */\n public function updateOrCreateParticipant(\n array $data,\n bool $enterRoom = true,\n ?Carbon $enterTime = null,\n bool $nameMatching = false,\n ): Participant {\n $search = [];\n $participant = null;\n\n if (isset($data['user_id'])) {\n // Check if they already exist based on their ID.\n $search['user_id'] = $data['user_id'];\n } elseif (isset($data['provider_id'])) {\n $search['provider_id'] = $data['provider_id'];\n } elseif ($nameMatching && isset($data['name'])) {\n $search['name'] = $data['name'];\n }\n\n if (! empty($data['email'])) {\n $search['email'] = $data['email'];\n\n // If we have their email, this should be unique enough to lookup (e.g. calendar event based participant).\n unset($search['provider_id']);\n }\n\n // Search by phone number only in case nothing else is available to search by.\n if (array_key_exists('phone_number', $data) && empty($search)) {\n $search['phone_number'] = $data['phone_number'];\n }\n\n if (! empty($search)) {\n // Do a lookup now to see if we have a match on the provided data.\n $lookup = array_map(static function ($key, $value): array {\n return [$key, $value];\n }, array_keys($search), $search);\n\n $participant = $this->participants()->withTrashed()->where($lookup)->first();\n }\n\n // Do a partial match on the name and search in the team members.\n if (! $participant instanceof Participant && $nameMatching && ! empty($data['name'])) {\n $participantMatcher = app(MeetingBot\\Service\\ParticipantMatcher::class);\n\n if (! $participantMatcher instanceof MeetingBot\\Service\\ParticipantMatcher) {\n throw new LogicException('Expecting ParticipantMatcher service instance');\n }\n\n $participant = $participantMatcher->match($this, $data['name']);\n\n // If we've found good participant, avoid data overwrite in `$participant->fill($data)` below.\n if ($participant instanceof Models\\Participant && $participant->hasName()) {\n unset($data['name']); // Thoughts: should we unset also $data['user_id'] and $data['email'] ?\n }\n }\n\n if (! $participant instanceof Participant) {\n // If no match, create a new participant.\n if (empty($search)) {\n $participant = $this->participants()->create();\n } else {\n // If no match, create a new participant but avoid creating duplicates\n $participant = $this->participants()->withTrashed()->firstOrNew($search);\n }\n }\n\n // If we have just recycled a deleted participant\n if ($participant->trashed()) {\n $participant->deleted_at = null;\n }\n\n // Deal with the case when calendar syncs the event while it's in progress.\n // We should prevent change of the participant name, because speeches mapping will fail.\n if ($enterRoom === false\n && $this->isInProgress()\n && $participant->hasName()\n && isset($data['name'])\n && $data['name'] !== $participant->getName()\n ) {\n unset($data['name']);\n }\n\n // Upsert with new data.\n $participant->fill($data);\n\n if ($enterRoom) {\n if ($enterTime === null) {\n $enterTime = now();\n }\n\n // Participant enters room for the first time\n if ($participant->enter_time === null) {\n $participant->enter_time = $enterTime;\n }\n\n // If there is an exit time and it's prior to new enter_time\n if ($participant->exit_time && $participant->exit_time->lt($enterTime)) {\n // Participant has re-joined\n $participant->exit_time = null;\n }\n }\n\n $participant->save();\n\n return $participant;\n }\n\n /**\n * Updates participant CRM data\n *\n * @param array{\n * Lead|null,\n * Account|null,\n * Opportunity|null,\n * Contact|null,\n * Stage|null,\n * string|null\n *} $records\n * @param Participant $participant participant the CRM data is associated with\n */\n public function updateParticipantCrmData(array $records, Participant $participant): void\n {\n // Extract the records.\n [$lead, , , $contact] = $records;\n\n $resolver = $this->getUpdateCrmDataResolver();\n $strategy = $resolver->resolveForParticipant($lead, $contact);\n\n if ($strategy == UpdateCrmDataByStrategy::Lead) {\n if (! $participant->hasName()) {\n $participant->name = $lead->name;\n }\n\n if (! $participant->hasEmailAddress()) {\n $participant->email = $lead->email;\n }\n\n if (! $participant->hasPhoneNumber()) {\n $participant->phone_number = $lead->phone;\n }\n\n $participant->lead_id = $lead->id;\n $participant->save();\n } elseif ($strategy == UpdateCrmDataByStrategy::Contact) {\n if (! $participant->hasName()) {\n $participant->name = $contact->name;\n }\n\n if (! $participant->hasEmailAddress()) {\n $participant->email = $contact->email;\n }\n\n if (! $participant->hasPhoneNumber()) {\n $participant->phone_number = $contact->phone;\n }\n\n $participant->contact_id = $contact->id;\n $participant->save();\n }\n }\n\n /**\n * Updates activity CRM data\n *\n * @param array{\n * Lead|null,\n * Account|null,\n * Opportunity|null,\n * Contact|null,\n * Stage|null,\n * string|null\n *} $records\n */\n public function updateActivityCrmData(array $records): void\n {\n // Extract the records.\n [$lead, $account, $opportunity, $contact, $stage] = $records;\n\n $resolver = $this->getUpdateCrmDataResolver();\n $strategy = $resolver->resolveForActivity($lead, $contact, $account);\n\n if ($strategy == UpdateCrmDataByStrategy::Lead) {\n // Also update the parent activity if required, checking we don't create a mixed lead/account record.\n if ($this->account_id === null && $this->contact_id === null && $this->lead_id === null) {\n $this->lead_id = $lead->id;\n\n if ($this->stage_id === null && $stage) {\n $this->stage_id = $stage->id;\n }\n\n $this->save();\n }\n } elseif ($strategy == UpdateCrmDataByStrategy::Contact) {\n // Also update the parent activity if required, checking we don't create a mixed lead/account record.\n $this->lead_id = null;\n if ($this->stage && $this->stage->getType() === Stage::TYPE_LEAD) {\n $this->stage_id = null;\n }\n\n // Don't trust previous matched account_id as it might have been changed in the CRM\n if ($account && $account->id !== $this->account_id) {\n $this->account_id = $account->id;\n }\n\n if ($opportunity && $this->opportunity_id !== $opportunity->id) {\n $this->opportunity_id = $opportunity->id;\n // Reset stage_id when opportunity changes to avoid stale stage from previous opportunity match\n $this->stage_id = $stage?->id;\n }\n\n if ($this->stage_id === null && $stage) {\n $this->stage_id = $stage->id;\n }\n\n if ($opportunity && $this->value !== $opportunity->value) {\n $this->value = $opportunity->value;\n }\n\n // Always set contact_id when available, regardless of account_id status\n if ($this->contact_id === null && $contact) {\n $this->contact_id = $contact->id;\n }\n\n $this->save();\n } elseif ($strategy == UpdateCrmDataByStrategy::Account && $this->account_id === null) {\n // Also update the parent activity if required, checking we don't create a mixed lead/account record.\n $this->lead_id = null;\n if ($this->stage && $this->stage->getType() === Stage::TYPE_LEAD) {\n $this->stage_id = null;\n }\n\n // Update the account and opportunity on the activity record if possible.\n $this->account_id = $account->id;\n\n if ($this->stage_id === null && $stage) {\n $this->stage_id = $stage->id;\n }\n\n if ($this->opportunity_id === null && $opportunity) {\n $this->opportunity_id = $opportunity->id;\n $this->value = $opportunity->value;\n }\n\n $this->save();\n }\n }\n\n public function getActivityProspectData(): array\n {\n return [\n 'lead' => $this->lead_id,\n 'contact' => $this->contact_id,\n 'account' => $this->account_id,\n 'opportunity' => $this->opportunity_id,\n 'stage' => $this->stage_id,\n ];\n }\n\n public function isOrganizer(User $user): bool\n {\n return $this->user_id && $this->user_id === $user->id;\n }\n\n public function isJoinable(): bool\n {\n return \\in_array($this->status, [\n self::STATUS_SCHEDULED,\n self::STATUS_PENDING,\n self::STATUS_RINGING,\n self::STATUS_IN_PROGRESS,\n ], true);\n }\n\n public function isAttemptedForBotJoin(): bool\n {\n return in_array($this->getAttribute('status'), self::MEETING_BOT_JOIN_ATTEMPTED, true);\n }\n\n /**\n * Check if the activity can be saved to CRM (manual or autolog)\n */\n public function isLoggable(): bool\n {\n if ($this->getUser()->getTeam()->hasFeature(FeatureEnum::SIDEKICK_SETTINGS)) {\n $sidekickService = app(SidekickService::class);\n\n if (! $sidekickService->isSidekickEnabledForUser($this->getUser())) {\n return false;\n }\n }\n\n // If we don't know the activity type, don't try to log.\n if ($this->playbook_category_id === null) {\n return false;\n }\n\n if ($this->user->crm_required === false) {\n return false;\n }\n\n // Don't prompt for internal meetings.\n if ($this->is_internal) {\n return false;\n }\n\n // If we don't know who we are trying to log to, don't try to log.\n if ($this->prospect === null) {\n return false;\n }\n\n $validStatus = false;\n switch ($this->type) {\n case self::TYPE_SOFTPHONE:\n case self::TYPE_SOFTPHONE_INBOUND:\n $validStatus = true;\n\n break;\n case self::TYPE_CONFERENCE:\n $validStatus = in_array($this->status, [\n self::STATUS_BUSY,\n self::STATUS_NO_ANSWER,\n self::STATUS_COMPLETED,\n self::STATUS_CANCELLED,\n ], true);\n\n break;\n case self::TYPE_SMS_INBOUND:\n case self::TYPE_SMS_OUTBOUND:\n $validStatus = in_array($this->status, [\n self::STATUS_QUEUED,\n self::STATUS_SENT,\n self::STATUS_UNDELIVERED,\n self::STATUS_DELIVERED,\n self::STATUS_RECEIVED,\n ], true);\n\n break;\n }\n\n // Depending on the activity channel, we should not try to log.\n return $validStatus;\n }\n\n public function isScheduled(): bool\n {\n return $this->status === self::STATUS_SCHEDULED;\n }\n\n public function scheduledDuration(): int\n {\n if ($this->scheduled_start_time && $this->scheduled_end_time) {\n return $this->scheduled_end_time->timestamp - $this->scheduled_start_time->timestamp;\n }\n\n return 0;\n }\n\n public function isPending(): bool\n {\n return $this->status === self::STATUS_PENDING;\n }\n\n public function isCompleted(): bool\n {\n return $this->status === self::STATUS_COMPLETED;\n }\n\n public function isRinging(): bool\n {\n return $this->status === self::STATUS_RINGING;\n }\n\n public function isInProgress(): bool\n {\n return $this->status === self::STATUS_IN_PROGRESS;\n }\n\n public function isBusy(): bool\n {\n return $this->status === self::STATUS_BUSY;\n }\n\n public function isNoAnswer(): bool\n {\n return $this->status === self::STATUS_NO_ANSWER;\n }\n\n public function isFailed(): bool\n {\n return $this->status === self::STATUS_FAILED;\n }\n\n public function isCancelled(): bool\n {\n return $this->status === self::STATUS_CANCELLED;\n }\n\n public function hasEnded(int $gracePeriodMinutes = 15): bool\n {\n if ($this->isCompleted()) {\n return true;\n }\n\n if (($this->isFailed() || $this->isCancelled()) && $this->hasScheduledEndTime()) {\n return $this->getScheduledEndTime()->addMinutes($gracePeriodMinutes)->isPast();\n }\n\n return false;\n }\n\n public function hasStarted(): bool\n {\n return $this->hasActualStartTime();\n }\n\n public function isOngoing(): bool\n {\n return $this->hasActualStartTime() && ! $this->hasActualEndTime();\n }\n\n public function isTypeSmsInbound(): bool\n {\n return $this->getType() === self::TYPE_SMS_INBOUND;\n }\n\n public function isTypeSmsOutbound(): bool\n {\n return $this->getType() === self::TYPE_SMS_OUTBOUND;\n }\n\n public function isTypeSoftPhone(): bool\n {\n return $this->getType() === self::TYPE_SOFTPHONE;\n }\n\n public function isTypeSoftphoneInbound(): bool\n {\n return $this->getType() === self::TYPE_SOFTPHONE_INBOUND;\n }\n\n public function isTypeConference(): bool\n {\n return $this->getType() === self::TYPE_CONFERENCE;\n }\n\n /**\n * Get a conference elapsed time in seconds.\n *\n * @return int seconds count\n */\n public function secondsTimeElapsed(): int\n {\n if (empty($this->actual_start_time)) {\n return 0;\n }\n\n // Get number of seconds since conference actual start time\n return (int) abs(Carbon::now()->diffInRealSeconds($this->actual_start_time));\n }\n\n /**\n * Get a conference elapsed time formatted as \"1:30:20\" if more than 1 hour or \"30:20\" otherwise.\n */\n public function formattedTimeElapsed(): string\n {\n // Get number of seconds since conference actual start time.\n $elapsedSeconds = $this->secondsTimeElapsed();\n $elapsedTime = Carbon::createFromTimestampUTC($elapsedSeconds);\n\n // Format conference start time.\n return $elapsedTime->format($elapsedSeconds < 3600 ? 'i:s' : 'G:i:s');\n }\n\n public function wasScheduled(): bool\n {\n return $this->calendarEvent !== null || in_array($this->getSource(), [self::SOURCE_OUTLOOK, self::SOURCE_GOOGLE]);\n }\n\n public function isInstant(): bool\n {\n return ! $this->wasScheduled();\n }\n\n /**\n * GETTERS AND SETTERS FOLLOW BELOW\n */\n\n public function getUuid(): string\n {\n return $this->getAttribute('id_string');\n }\n\n public function getId(): int\n {\n return $this->getAttribute('id');\n }\n\n public function getFromParticipantId(): ?int\n {\n return $this->getAttribute('from_participant_id');\n }\n\n public function getFromParticipant(): ?Participant\n {\n return $this->getAttribute('from');\n }\n\n public function getToParticipantId(): ?int\n {\n return $this->getAttribute('to_participant_id');\n }\n\n public function getToParticipant(): ?Participant\n {\n return $this->getAttribute('to');\n }\n\n public function hasScheduledStartTime(): bool\n {\n return $this->getAttribute('scheduled_start_time') !== null;\n }\n\n public function getScheduledStartTime(): ?Carbon\n {\n return $this->getAttribute('scheduled_start_time');\n }\n\n public function setScheduledStartTime(DateTimeInterface $dateTime): self\n {\n $this->setAttribute('scheduled_start_time', $dateTime);\n\n return $this;\n }\n\n public function getScheduledEndTime(): ?DateTimeInterface\n {\n return $this->getAttribute('scheduled_end_time');\n }\n\n public function hasScheduledEndTime(): bool\n {\n return $this->getAttribute('scheduled_end_time') !== null;\n }\n\n public function setScheduledEndTime(DateTimeInterface $dateTime): self\n {\n $this->setAttribute('scheduled_end_time', $dateTime);\n\n return $this;\n }\n\n public function getActualStartTime(): ?Carbon\n {\n return $this->getAttribute('actual_start_time');\n }\n\n public function hasActualStartTime(): bool\n {\n return $this->getAttribute('actual_start_time') !== null;\n }\n\n public function getActualEndTime(): ?Carbon\n {\n return $this->getAttribute('actual_end_time');\n }\n\n public function hasActualEndTime(): bool\n {\n return $this->getAttribute('actual_end_time') !== null;\n }\n\n public function getType(): ?string\n {\n return $this->getAttribute('type');\n }\n\n public function getStatus(): string\n {\n return $this->getAttribute('status');\n }\n\n public function setStatus(string $status): self\n {\n $this->setAttribute('status', $status);\n\n return $this;\n }\n\n public function setActualStartTime(DateTimeInterface $dateTime): self\n {\n $this->setAttribute('actual_start_time', $dateTime);\n\n return $this;\n }\n\n public function setActualEndTime(DateTimeInterface $dateTime, bool $shouldUpdateDuration = true): self\n {\n $this->setAttribute('actual_end_time', $dateTime);\n\n if (! $shouldUpdateDuration) {\n return $this;\n }\n\n return $this->updateDuration();\n }\n\n public function updateDuration(): self\n {\n if (! $this->hasActualStartTime() || ! $this->hasActualEndTime()) {\n return $this;\n }\n\n return $this->setDuration(\n (int) abs($this->getActualStartTime()->diffInRealSeconds($this->getActualEndTime()))\n );\n }\n\n public function setDuration(int $duration): self\n {\n $this->setAttribute('duration', $duration);\n\n return $this;\n }\n\n public function getRecordingState(): string\n {\n return $this->getAttribute('recording_state');\n }\n\n public function isRecordingState(string $recordingState): bool\n {\n return $this->getRecordingState() === $recordingState;\n }\n\n public function setRecordingState(string $recordingState): self\n {\n $this->setAttribute('recording_state', $recordingState);\n\n return $this;\n }\n\n public function hasActivityType(): bool\n {\n return $this->getAttribute('category') !== null;\n }\n\n public function getActivityType(): ?PlaybookCategory\n {\n return $this->getAttribute('category');\n }\n\n public function setActivityType(int $playbookCategoryId): self\n {\n $this->setAttribute('playbook_category_id', $playbookCategoryId);\n\n return $this;\n }\n\n public function hasStage(): bool\n {\n return $this->getAttribute('stage') !== null;\n }\n\n public function getStage(): ?Stage\n {\n return $this->getAttribute('stage');\n }\n\n public function getStageId(): ?int\n {\n return $this->getAttribute('stage_id');\n }\n\n public function setStageId(?int $stageId): void\n {\n $this->setAttribute('stage_id', $stageId);\n }\n\n public function hasOpportunity(): bool\n {\n return $this->getAttribute('opportunity') !== null;\n }\n\n public function getOpportunity(): ?Opportunity\n {\n return $this->getAttribute('opportunity');\n }\n\n public function getOpportunityId(): ?int\n {\n return $this->getAttribute('opportunity_id');\n }\n\n public function setOpportunityId(?int $opportunityId): void\n {\n $this->setAttribute('opportunity_id', $opportunityId);\n }\n\n public function hasContact(): bool\n {\n return $this->getAttribute('contact') !== null;\n }\n\n public function getContact(): ?Contact\n {\n return $this->getAttribute('contact');\n }\n\n public function getContactId(): ?int\n {\n return $this->getAttribute('contact_id');\n }\n\n public function setContactId(?int $contactId): void\n {\n $this->setAttribute('contact_id', $contactId);\n }\n\n public function hasLead(): bool\n {\n return $this->getAttribute('lead') !== null;\n }\n\n public function getLead(): ?Lead\n {\n return $this->getAttribute('lead');\n }\n\n public function getLeadId(): ?int\n {\n return $this->getAttribute('lead_id');\n }\n\n public function setLeadId(?int $leadId): void\n {\n $this->setAttribute('lead_id', $leadId);\n }\n\n public function hasAccount(): bool\n {\n return $this->getAttribute('account') !== null;\n }\n\n public function getAccount(): ?Account\n {\n return $this->getAttribute('account');\n }\n\n public function getAccountId(): ?int\n {\n return $this->getAttribute('account_id');\n }\n\n public function setAccountId(?int $accountId): void\n {\n $this->setAttribute('account_id', $accountId);\n }\n\n /**\n * This method exists to avoid confusion using ->participants() or ->participants. Use the getter instead.\n *\n * @return Collection<int, Participant>|Participant[]\n */\n public function getParticipants(): Collection\n {\n return $this->participants;\n }\n\n /**\n * @deprecated use ParticipantRepository::findParticipantRoomOwner() instead\n */\n public function findParticipantRoomOwner(): ?Participant\n {\n $roomOwnerId = $this->getUserId();\n\n return $this->getParticipants()\n ->filter(static fn (Participant $participant): bool => $participant->isSameUserId($roomOwnerId))\n ->first();\n }\n\n public function hasCrmProviderId(): bool\n {\n return $this->getAttribute('crm_provider_id') !== null;\n }\n\n public function getCrmProviderId(): ?string\n {\n return $this->getAttribute('crm_provider_id');\n }\n\n public function setCrmProviderId(?string $crmProviderId): void\n {\n $this->setAttribute('crm_provider_id', $crmProviderId);\n }\n\n public function getUserId(): ?int\n {\n return $this->getAttribute('user_id');\n }\n\n public function hasUser(): bool\n {\n return $this->user()->exists();\n }\n\n public function getUser(): User\n {\n return $this->getAttribute('user');\n }\n\n public function getCreatedAt(): Carbon\n {\n return $this->getAttribute('created_at');\n }\n\n public function isInFiniteState(): bool\n {\n return $this->isFiniteState($this->getStatus());\n }\n\n public function isFiniteState(string $status): bool\n {\n $finiteStates = self::FINITE_STATES[$this->getType()] ?? [];\n\n return in_array($status, $finiteStates, true);\n }\n\n public function getParticipant(Authenticatable $user): Participant\n {\n return $this->findParticipant($user);\n }\n\n public function findParticipant(Authenticatable $user): ?Participant\n {\n if ($user instanceof User) {\n /** @var User $user */\n return $this->participants()->where('user_id', '=', $user->getId())->first();\n }\n\n throw new LogicException(sprintf('Unsupported Authenticatable implementation %s', get_class($user)));\n }\n\n public function hasLanguageCode(): bool\n {\n return $this->getAttribute('language') !== null;\n }\n\n public function getLanguageCode(): ?string\n {\n /** @var string|null */\n return $this->getAttribute('language');\n }\n\n public function getLanguageCodeHyphenated(): string\n {\n return str_replace('_', '-', $this->getLanguageCode() ?? '');\n }\n\n public function getLanguageCodeLocale(): string\n {\n [ $language ] = explode('_', $this->getLanguageCode() ?? '');\n\n return $language;\n }\n\n public function setLanguageCode(string $value): self\n {\n return $this->setAttribute('language', $value);\n }\n\n public function hasSource(): bool\n {\n return $this->getAttribute('source') !== null;\n }\n\n public function setSource(?string $source): self\n {\n return $this->setAttribute('source', $source);\n }\n\n public function isSource(string $source): bool\n {\n return $this->getAttribute('source') === $source;\n }\n\n public function getSource(): ?string\n {\n return $this->getAttribute('source');\n }\n\n public function isSourceGong(): bool\n {\n return $this->isSource(self::SOURCE_GONG);\n }\n\n public function getExternalId(): ?string\n {\n return $this->getAttribute('external_id');\n }\n\n public function setExternalId(?string $externalId): self\n {\n return $this->setAttribute('external_id', $externalId);\n }\n\n public function hasExternalId(): bool\n {\n return $this->getAttribute('external_id') !== null;\n }\n\n public function getProvider(): string\n {\n return $this->getAttribute('provider');\n }\n\n public function hasTelephonyProviderId(): bool\n {\n return $this->getAttribute('telephony_provider_id') !== null;\n }\n\n public function getTelephonyProviderId(): ?string\n {\n return $this->getAttribute('telephony_provider_id');\n }\n\n public function setTelephonyProviderId(?string $telephonyProviderId): self\n {\n return $this->setAttribute('telephony_provider_id', $telephonyProviderId);\n }\n\n public function getLocation(): ?string\n {\n return $this->getAttribute('location');\n }\n\n public function setLocation(?string $location): self\n {\n return $this->setAttribute('location', $location);\n }\n\n public function isDeleted(): bool\n {\n return $this->getAttribute('deleted_at') !== null;\n }\n\n /**\n * Check if activity recording is on and activity status is not one of the failed statuses.\n */\n public function canReviewActivity(): bool\n {\n $failedStatuses = self::$enumFailedStatuses;\n\n return (! in_array($this->recording_state, [self::RECORDING_OFF, self::RECORDING_STOPPED], true) &&\n ! in_array($this->status, $failedStatuses, true));\n }\n\n public function hasReasonCodeBotKicked(): bool\n {\n return $this->getFlag('recording_reason_code', self::FLAG_RECORDING_REASON_MEETING_BOT_KICKED);\n }\n\n public function hasReasonCodeNotCompliant(): bool\n {\n return $this->getFlag('recording_reason_code', self::FLAG_RECORDING_REASON_CONSENT_DENIED);\n }\n\n public function hasTopicTriggers(): bool\n {\n return $this->topicTriggers()->count() !== 0;\n }\n\n public function getTopicTriggers(): Collection\n {\n return $this->topicTriggers;\n }\n\n public function getTopicTriggersSorted(): Collection\n {\n $this->loadMissing([\n 'topicTriggers.participant',\n 'topicTriggers.playbackThemeTopicTrigger',\n 'topicTriggers.playbackThemeTopicTrigger',\n 'topicTriggers.playbackThemeTopicTrigger.playbackThemeTopic',\n 'topicTriggers.playbackThemeTopicTrigger.playbackThemeTopic.playbackTheme',\n ]);\n\n return $this->topicTriggers\n ->sortBy([\n 'playbackThemeTopicTrigger.playbackThemeTopic.playbackTheme.sort',\n 'playbackThemeTopicTrigger.playbackThemeTopic.sort',\n 'playbackThemeTopicTrigger.sort',\n ]);\n }\n\n public function hasQuestions(): bool\n {\n return $this->questions()->exists();\n }\n\n public function getQuestions(): Collection\n {\n return $this->questions;\n }\n\n public function hasValue(): bool\n {\n return $this->getAttribute('value') !== null;\n }\n\n public function getValue(): ?float\n {\n return $this->getAttribute('value');\n }\n\n public function setValue(?float $value): void\n {\n $this->setAttribute('value', $value);\n }\n\n public function transitionTo(string $newState, callable $callback, ?int $timeout = null): self\n {\n $newState = $this->getWorkflowStateFor(\n $this->getType(),\n $newState\n );\n\n return $this->traitTransitionTo($newState, $callback, $timeout);\n }\n\n public function getWorkflowState(): string\n {\n return $this->getWorkflowStateFor(\n $this->getType(),\n $this->getStatus()\n );\n }\n\n public function getActivityProviderDisplayName(): string\n {\n return \\Cache::remember('activity_provider_display_name-' . $this->getProvider(), 60 * 60 * 24, function () {\n $activityProviderRegistry = app()->make(ActivityProviderRegistry::class);\n\n try {\n return $activityProviderRegistry->get($this->getProvider())->getDisplayName();\n } catch (Exception $exception) {\n return ucfirst($this->getProvider());\n }\n });\n }\n\n private function getWorkflowStateFor(string $activityChannel, string $activityStatus): string\n {\n return sprintf(\n '%s::%s',\n $activityChannel,\n $activityStatus\n );\n }\n\n public function getWorkflow(): array\n {\n $map = [\n self::TYPE_SOFTPHONE => [\n self::STATUS_SCHEDULED => [\n self::STATUS_PENDING,\n self::STATUS_IN_PROGRESS,\n self::STATUS_FAILED,\n self::STATUS_BUSY,\n ],\n self::STATUS_PENDING => [\n self::STATUS_IN_PROGRESS,\n self::STATUS_FAILED,\n self::STATUS_BUSY,\n ],\n self::STATUS_RINGING => [\n self::STATUS_CANCELLED,\n self::STATUS_FAILED,\n self::STATUS_IN_PROGRESS,\n self::STATUS_BUSY,\n ],\n self::STATUS_IN_PROGRESS => [\n self::STATUS_COMPLETED,\n ],\n ],\n self::TYPE_SOFTPHONE_INBOUND => [\n self::STATUS_RINGING => [\n self::STATUS_IN_PROGRESS,\n self::STATUS_NO_ANSWER,\n self::STATUS_CANCELLED,\n self::STATUS_FAILED,\n self::STATUS_BUSY,\n ],\n self::STATUS_IN_PROGRESS => [\n self::STATUS_COMPLETED,\n ],\n ],\n ];\n\n return collect($map)\n ->mapWithKeys(function (array $currentStates, string $activityChannel): array {\n return [\n $activityChannel => collect($currentStates)\n ->mapWithKeys(function (array $possibleStates, $currentState) use ($activityChannel): array {\n $transitionName = $this->getWorkflowStateFor($activityChannel, $currentState);\n\n return [\n $transitionName => array_map(function (string $newState) use ($activityChannel) {\n return $this->getWorkflowStateFor($activityChannel, $newState);\n }, $possibleStates),\n ];\n }),\n ];\n })\n ->reduce(static function (array $carry, Collection $item): array {\n return array_merge($carry, $item->all());\n }, []);\n }\n\n public function hasPosterPath(): bool\n {\n return $this->getAttribute('poster_path') !== null;\n }\n\n public function getPosterPath(): ?string\n {\n return $this->getAttribute('poster_path');\n }\n\n /**\n * Take into account all recording settings and determine if we need to record this activity or not.\n */\n public function shouldRecord(): bool\n {\n return $this->determineRecordingReasonCode() === null;\n }\n\n public function determineRecordingReasonCode(): ?int\n {\n // Conference specific decisions.\n if ($this->isTypeConference()) {\n // If they have manually overridden the recording setting to not record.\n if ($this->hasRecordingPreference() && $this->getRecordingPreference() === false) {\n return self::FLAG_RECORDING_REASON_PREFERENCE_OVERRIDE;\n }\n\n // If they have manually overridden the recording setting to record.\n if ($this->hasRecordingPreference() && $this->getRecordingPreference() === true) {\n return null;\n }\n\n // If their team has disabled recording meetings, don't record.\n if ($this->user->team->isConferenceRecordPreferenceDisabled()) {\n return self::FLAG_RECORDING_REASON_TEAM_AUTOMATIC_DISABLED;\n }\n\n // If the host has disabled recording meetings, don't record.\n if ($this->user->checkConferenceRecordPreference() === false) {\n return self::FLAG_RECORDING_REASON_USER_AUTOMATIC_DISABLED;\n }\n\n // If it was marked internal...\n if ($this->is_internal) {\n // and their team has disabled recording internal meetings, don't record.\n if (\n $this->user->team->isConferenceRecordPreferenceEnabled()\n && ! $this->user->team->isConferenceRecordInternalPreferenceEnabled()\n ) {\n return self::FLAG_RECORDING_REASON_TEAM_INTERNAL_DISABLED;\n }\n\n // and the host has disabled recording internal meetings, don't record.\n if ($this->user->checkConferenceRecordInternalPreference() === false) {\n return self::FLAG_RECORDING_REASON_USER_INTERNAL_DISABLED;\n }\n }\n\n // If it was not scheduled and they disabled internal meetings, we cannot determine if it was internal.\n if ($this->wasScheduled() === false && $this->user->checkConferenceRecordInternalPreference() === false) {\n return self::FLAG_RECORDING_REASON_USER_INTERNAL_DISABLED_UNSCHEDULED;\n }\n }\n\n return null;\n }\n\n public function getRecordingReasonCode(): int\n {\n return $this->getAttribute('recording_reason_code');\n }\n\n public function setRecordingReasonCode(int $recordingReasonCode): self\n {\n $this->setAttribute('recording_reason_code', $recordingReasonCode);\n\n return $this;\n }\n\n // Not used today.\n public function getRecordingReasonString(): ?string\n {\n if ($this->hasRecordingReasonCompliancePrompted()) {\n return Team::COMPLIANCE_MODE_RECORDING_PROMPT;\n }\n\n if ($this->hasRecordingReasonComplianceRestricted()) {\n return Team::COMPLIANCE_MODE_RECORDING_RESTRICT;\n }\n\n if ($this->hasRecordingReasonComplianceRestrictedToOneSideRecording()) {\n return Team::COMPLIANCE_MODE_RECORDING_RESTRICT_ONE_SIDE;\n }\n\n return null;\n }\n\n public function hasRecordingReasonComplianceRestricted(): bool\n {\n return $this->getFlag('recording_reason_code', self::FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT);\n }\n\n public function hasRecordingReasonCompliancePrompted(): bool\n {\n return $this->getFlag('recording_reason_code', self::FLAG_RECORDING_REASON_COMPLIANCE_PROMPT);\n }\n\n public function hasRecordingReasonComplianceRestrictedToOneSideRecording(): bool\n {\n return $this->getFlag('recording_reason_code', self::FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT_ONE_SIDE);\n }\n\n public function getAudioTrack(): ?Track\n {\n /** @var Track|null */\n return $this->tracks()\n ->where('type', '=', Track::TYPE_AUDIO)\n ->first();\n }\n\n public function activeParticipants(): HasMany\n {\n return $this->hasMany(Participant::class)->active();\n }\n\n public function getActiveParticipants(): Eloquent\\Collection\n {\n return $this->getAttribute('activeParticipants');\n }\n\n public function crm(): Eloquent\\Relations\\BelongsTo\n {\n return $this->belongsTo(Configuration::class, 'crm_configuration_id');\n }\n\n public function activitySummaryLogs(): HasMany\n {\n return $this->hasMany(ActivitySummaryLog::class);\n }\n\n public function getCrm(): ?Configuration\n {\n return $this->getAttribute('crm');\n }\n\n public function hasCrmConfiguration(): bool\n {\n return $this->getAttribute('crm') !== null;\n }\n\n public function isProcessed(): ?bool\n {\n return $this->getAttribute('is_processed');\n }\n\n public function hasRecordingPrompt(): bool\n {\n return $this->getAttribute('has_recording_prompt') === true;\n }\n\n public function isOnAir(): bool\n {\n return $this->getAttribute('on_air') === self::ON_AIR_READY || $this->getAttribute('on_air') === self::ON_AIR_STREAMING;\n }\n\n public function setOnAir(int $onAir): self\n {\n $this->setAttribute('on_air', $onAir);\n\n return $this;\n }\n\n public function getOnAir(): ?int\n {\n return $this->getAttribute('on_air');\n }\n\n public function setTitleFromCallData(Call $call): void\n {\n $direction = $call->isOutbound() ? 'to' : 'from';\n\n $party = $this->prospect_name\n ?? $call->getContactName()\n ?? $call->getOtherPartyPhoneNumber()\n ;\n\n $this->update(['title' => sprintf('Call %s %s', $direction, $party)]);\n }\n\n /**\n * @param array{}|array{channels:string|null, format:string|null, type:string|null, status:string|null} $audioParams\n */\n public function createAudioTrack(\n string $telephonyProviderId,\n string $recordingUrl,\n array $audioParams = []\n ): Track {\n return $this->tracks()->updateOrCreate([\n 'telephony_provider_id' => $telephonyProviderId,\n ], [\n 'type' => $audioParams['type'] ?? Track::TYPE_AUDIO,\n 'status' => $audioParams['status'] ?? Track::STATUS_PENDING,\n 'format' => $audioParams['format'] ?? Track::FORMAT_WAV,\n 'provider_content_url' => $recordingUrl,\n 'start_time' => $this->actual_start_time,\n 'end_time' => $this->actual_end_time,\n ]);\n }\n\n public function createTrack(string $telephonyProviderId, array $params): Track\n {\n return $this->tracks()->updateOrCreate(\n [\n 'telephony_provider_id' => $telephonyProviderId,\n ],\n $params\n );\n }\n\n public function createOrganiserParticipant(Call $call): Participant\n {\n $user = $this->getUser();\n\n return $this->updateOrCreateParticipant([\n 'is_ghost' => 0,\n 'name' => $user->name,\n 'email' => $user->email,\n 'phone_number' => phone_e164(null, $call->getUserPhoneNumber()),\n 'enter_time' => $this->actual_start_time,\n 'exit_time' => $this->actual_end_time,\n 'user_id' => $user->id,\n ], false);\n }\n\n public function createProspectParticipant(Call $call): Participant\n {\n // not null 'name' is mandatory here to create a separate participant with 'nameMatching'\n // in case of the same phone_number with the Organiser\n $useNameMatching = $call->getUserPhoneNumber() === $call->getOtherPartyPhoneNumber();\n $defaultName = $useNameMatching ? '' : null;\n\n return $this->updateOrCreateParticipant(data: [\n 'is_ghost' => 0,\n 'name' => $this->prospect->name ?? $defaultName,\n 'email' => $this->prospect->email ?? null,\n 'phone_number' => phone_e164(null, $call->getOtherPartyPhoneNumber()),\n 'enter_time' => $this->actual_start_time,\n 'exit_time' => $this->actual_end_time,\n 'contact_id' => $this->contact_id ?? null,\n 'lead_id' => $this->lead_id ?? null,\n ], enterRoom: false, nameMatching: $useNameMatching);\n }\n\n public function updateParticipants(Participant $organiserParticipant, Participant $prospectParticipant): void\n {\n $this->update([\n 'from_participant_id' => $this->isTypeSoftPhone() ? $organiserParticipant->id : $prospectParticipant->id,\n 'to_participant_id' => $this->isTypeSoftPhone() ? $prospectParticipant->id : $organiserParticipant->id,\n ]);\n }\n\n public function hasProspect(): bool\n {\n return $this->getProspectAttribute() !== null;\n }\n\n public function isPrivate(): bool\n {\n return $this->getAttribute('is_private');\n }\n\n /** Create a new factory instance for the model. */\n protected static function newFactory(): Factory\n {\n return ActivityFactory::new();\n }\n\n public function getUpdatedAt(): Carbon\n {\n return $this->getAttribute('updated_at');\n }\n\n public function getActivitySummaryLogs(): Eloquent\\Collection\n {\n return $this->getAttribute('activitySummaryLogs');\n }\n\n public function hasProspectActivitySummaryLog(): bool\n {\n return $this->getActivitySummaryLogs()->contains(\n 'relation_type',\n ActivitySummaryLog::RELATION_OBJECT_TYPE_PROSPECT\n );\n }\n\n public function getTeam(): Team\n {\n return $this->getUser()->getTeam();\n }\n\n private function getUpdateCrmDataResolver(): UpdateCrmDataResolverInterface\n {\n $factory = app(UpdateCrmDataResolverFactory::class);\n\n return $factory->create($this);\n }\n\n public function getMeetingTrackProviderId(string $type): string\n {\n $label = match ($type) {\n Track::TYPE_VIDEO => 'v',\n Track::TYPE_AUDIO => 'a',\n default => throw new InvalidArgumentJiminnyException('Invalid track type'),\n };\n\n $startTimestamp = $this->getScheduledStartTime()?->getTimestamp();\n $teamId = $this->getTeam()->getId();\n\n return $this->getTelephonyProviderId() . ':' . $label . ':' . $startTimestamp . ':' . $teamId;\n }\n\n /**\n * Get all consent records associated with this activity\n *\n * @return \\Illuminate\\Database\\Eloquent\\Relations\\HasMany\n */\n public function participantConsents(): HasMany\n {\n return $this->hasMany(Participant\\Consent::class);\n }\n\n public function isDiallerCall(): bool\n {\n if ($this->getProvider() === Activity::PROVIDER_UPLOADER) {\n return false;\n }\n\n if (! in_array($this->getType(), [self::TYPE_SOFTPHONE, self::TYPE_SOFTPHONE_INBOUND])) {\n return false;\n }\n\n return $this->getProvider() !== self::PROVIDER_TWILIO;\n }\n\n public function getActivityDateWithFallback(): Carbon\n {\n if ($this->getActualStartTime() !== null) {\n return $this->getActualStartTime();\n }\n\n if ($this->getScheduledStartTime() !== null) {\n return $this->getScheduledStartTime();\n }\n\n return $this->getCreatedAt();\n }\n\n public function getCrmType(): ?string\n {\n // Treat uploader activities as conferences\n if ($this->getProvider() === Activity::PROVIDER_UPLOADER) {\n return Activity::TYPE_CONFERENCE;\n }\n\n return $this->getType();\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Execute","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Explain Plan","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Browse Query History","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"View Parameters","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Open Query Execution Settings…","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"In-Editor Results","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Tx: Auto","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Cancel Running Statements","depth":4,"on_screen":true,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Playground","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"jiminny","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"45","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"1","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"41","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"66","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"SELECT * FROM teams WHERE name LIKE '%litify%'; # 1069, 994, 24993\nSELECT * FROM users WHERE id = 25061;\nSELECT * FROM crm_profiles WHERE crm_configuration_id = 994;\nSELECT * FROM crm_profiles WHERE user_id = 25061;\n\nselect * from crm_configurations where id = 834;\nSELECT * FROM teams WHERE id = 882;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 882 and sa.provider = 'hubspot';\n\nSELECT * FROM crm_configurations where id = 882; # 933 - GoGlobal\nSELECT * FROM opportunities WHERE team_id = 933 order by updated_at desc;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 933 and sa.provider = 'hubspot';\n\nSELECT * FROM crm_configurations WHERE provider = 'hubspot' and crm_provider_id = 7270388;\n\nSELECT * FROM contacts where crm_configuration_id = 834;\nSELECT * FROM opportunities WHERE team_id = 933\n# AND crm_provider_id IN ('20131586060','46017317898','52543911090','53451356564','54101251892','54323768459');\nAND id IN (8482561,18352941,19042734,19232139,19445140,19472541);\nSELECT * FROM opportunity_contacts\nWHERE opportunity_id IN (8482561,18352941,19042734,19232139,19445140,19472541);\n\n# ********************************************************************\nSELECT * FROM crm_configurations where id = 485; #\nSELECT * FROM opportunities WHERE team_id = 933 order by updated_at desc;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 933 and sa.provider = 'hubspot';\n\nselect crm.provider, l.* from leads l join crm_configurations crm on l.crm_configuration_id = crm.id\nwhere crm.provider NOT IN ('salesforce', 'integration-app', 'bullhorn', 'copper')\n# and l.converted_at IS NOT NULL\n;\n\n# ********************************************************************\nSELECT * FROM activities a WHERE type IN ('email-inbound', 'email-outbound')\nand opportunity_id IS NULL\norder by id desc;\n\nSELECT * FROM teams WHERE id = 604; # 598\nSELECT * FROM activities WHERE id = 74410828; # chelseaw@allvoices.co\nSELECT * FROM accounts WHERE id = 20068382;\nSELECT * FROM accounts WHERE id = 35186038;\n\nSELECT * FROM contacts WHERE team_id = 852 and updated_at > '2026-01-23 12:30:00' order by updated_at desc;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 559 and sa.provider = 'hubspot';\n\nSELECT * FROM activities WHERE uuid_to_bin('cb6342b6-a183-401c-b0af-ede92b2ae763') = uuid;\nselect * from sidekick_settings where team_id = 781;\n\nSELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id = 26651871; # Teya\nSELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id = 7562435;\nSELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id = 8420347; # opflit 2100\n\nSELECT * FROM crm_layouts WHERE crm_configuration_id = 711;\nSELECT * FROM activities where crm_configuration_id = 711 and crm_provider_id IS NULL\nand is_internal = 0 and status = 'completed'\norder by id desc;\n\nSELECT * FROM crm_layout_entities\nWHERE crm_layout_id IN (2352, 2353);\n;\n\nSELECT * FROM crm_configurations where provider = 'hubspot' and id = 530;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 556 and sa.provider = 'hubspot';\n\nSELECT * FROM activities WHERE uuid_to_bin('c6ca4b22-7738-4563-a95d-b8a9598924ae') = uuid;\nSELECT * FROM activities WHERE uuid_to_bin('442abb2b-28bd-4be8-9c25-19e9bf02766d') = uuid;\nselect * from contacts\nwhere crm_configuration_id = 530\nand crm_provider_id = 872252;\n\nselect * from activities where crm_configuration_id = 530\nand user_id = 14343 and type like '%softphone%'\nand created_at between '2026-01-28 15:00:00' and '2026-01-28 15:10:00';\n\nSELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id = 25666868; # Teya\nSELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id = 8646335; # Teya\nSELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id IN (5933397);\n\n\nSELECT t.name, t.id, t.owner_id, c.id, c.provider, c.crm_base_url FROM teams t\nJOIN crm_configurations c ON t.id = c.team_id\nWHERE t.status = 'active';\n\nSELECT * FROM teams where id = 1091;\nSELECT * FROM crm_configurations where team_id = 1091;\nSELECT * FROM activity_providers where team_id = 1091;\nSELECT * FROM activities where crm_configuration_id = 1024 and type IN ('softphone', 'softphone-outbound')\nand provider NOT IN ('hubspot', 'aircall')\n# and telephony_provider_id = '019c1131-a22f-4792-b9ea-20adf6a02ed0'\norder by id desc;\n\n\nSELECT * FROM teams WHERE name LIKE '%Leadventure%';\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1091 and sa.provider = 'salesforce';\n\nSELECT * FROM teams WHERE name LIKE '%Wilson%'; # 862, 812\nSELECT * FROM teams where id = 862;\nSELECT * FROM crm_configurations where team_id = 862;\nSELECT * FROM activity_providers where team_id = 862;\nSELECT * FROM activities where crm_configuration_id = 812 and type IN ('softphone', 'softphone-outbound')\nand provider NOT IN ('hubspot', 'aircall')\n# and telephony_provider_id = '019c1131-a22f-4792-b9ea-20adf6a02ed0'\norder by id desc;\n\n\nSELECT t.id, crm.id, crm.provider, ap.* FROM teams t\njoin crm_configurations crm on t.id = crm.team_id\njoin activity_providers ap on t.id = ap.team_id\nwhere t.status = 'active' and ap.is_enabled = 1\nand crm.provider = 'hubspot'\nand ap.provider NOT IN ('hubspot', 'aircall', 'uploader', 'gong', 'twilio', 'zoom-bot', 'google-meet', 'ms-teams',\n 'outreach', 'close', 'ringcentral', 'dialpad', 'zoom-phone');\n\nSELECT * FROM teams where id = 1068;\nSELECT * FROM crm_configurations where team_id = 1068;\nSELECT * FROM activity_providers where team_id = 1068;\n\nSELECT * FROM activities a\nwhere crm_configuration_id = 993 and type IN ('softphone', 'softphone-outbound')\nand a.provider NOT IN ('hubspot', 'uploader', 'gong', 'twilio', 'google-meet', 'ms-teams','close'\n )\n# and telephony_provider_id = '019c1131-a22f-4792-b9ea-20adf6a02ed0'\norder by a.id desc;\n\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1068 and sa.provider = 'hubspot';\n\n# ********************************************************************\n# ********************************************************************\n# ********************************************************************\nSELECT * FROM crm_configurations where id = 882; # 933 - GoGlobal , portalId: 6017093\nSELECT * FROM opportunities WHERE team_id = 933 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 933 and updated_at > '2026-02-06 00:00:00' order by updated_at desc;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 933 and sa.provider = 'hubspot';\n\n# ********************************************************************\nSELECT * FROM crm_configurations where id = 834; # 882 - AnyVan , portalId: 5468262\nSELECT * FROM contacts WHERE crm_configuration_id = 834 and updated_at > '2026-03-30 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE crm_configuration_id = 834 and updated_at > '2026-03-04 08:00:00' order by updated_at desc;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 882 and sa.provider = 'hubspot';\nselect * from crm_layouts where crm_configuration_id = 834;\nselect * from crm_layout_entities where crm_layout_id = 2780;\nselect * from crm_fields where id IN (321153,321192,321193,321194);\n\nSELECT * FROM opportunities WHERE crm_configuration_id = 834 and id = 10993426;\n# ********************************************************************\nSELECT * FROM crm_configurations where id = 988; # 1057 - Teya (543ce4f4-168c-4571-91ea-5b35c253f06f) , portalId: 26651871\nSELECT * FROM opportunities WHERE team_id = 1057 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 1057 and updated_at > '2026-02-04 00:00:00' order by updated_at desc;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1057 and sa.provider = 'hubspot';\n\nSELECT * FROM crm_configurations where id = 533; # 559 - Connectd , portalId: 6710988\nSELECT * FROM opportunities WHERE team_id = 559 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 559 and updated_at > '2026-02-04 00:00:00' order by updated_at desc;\n\nSELECT * FROM crm_configurations where id = 801; # 852 - Rise Vision , portalId: 2700250\nSELECT * FROM opportunities WHERE team_id = 852 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 852 and updated_at > '2026-02-04 00:00:00' order by updated_at desc; # 6th last\n\nSELECT * FROM crm_configurations where id = 962; # 1034 - evergrowth.io , portalId: 143180990\nSELECT * FROM opportunities WHERE team_id = 1034 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 1034 and updated_at > '2026-02-04 00:00:00' order by updated_at desc;\n\n# ********************************************************************\nSELECT * FROM crm_configurations where id = 1037; # 1102 - Jibble , portalId: 6649755\nSELECT * FROM opportunities WHERE team_id = 1102 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 1102 and updated_at > '2026-02-04 12:30:00' order by updated_at desc; # 8\n\nSELECT * FROM crm_configurations where id = 1015; # 1049 - Travefy , portalId: 48904401\nSELECT * FROM opportunities WHERE team_id = 1049 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 1049 and updated_at > '2026-02-04 12:30:00' order by updated_at desc; # 20\n\nSELECT * FROM crm_configurations where id = 64; # 70 - SalaryFinance , portalId: 3404115\nSELECT * FROM opportunities WHERE team_id = 70 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 70 and updated_at > '2026-02-04 12:30:00' order by updated_at desc; # 6th last\n\nSELECT * FROM crm_configurations where id = 802; # 853 - Street Group , portalId: 7658438\nSELECT * FROM opportunities WHERE team_id = 853 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 853 and updated_at > '2026-02-04 12:30:00' order by updated_at desc; # 10\n\nSELECT * FROM crm_configurations where id = 872; # 921 - In Professional Development , portalId: 9238273\nSELECT * FROM opportunities WHERE team_id = 921 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 921 and updated_at > '2026-02-04 12:30:00' order by updated_at desc; # 2\n\n# ********************************************************************\nSELECT * FROM crm_configurations where id = 550; # 576 - SeedLegals , portalId: 3028661\nSELECT * FROM opportunities WHERE team_id = 576 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 576 and updated_at > '2026-02-05 14:00:00' order by updated_at desc;\n\nSELECT * FROM crm_configurations where id = 989; # 1058 - rtaoutdoor.com , portalId: 22371204\nSELECT * FROM opportunities WHERE team_id = 1058 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 1058 and updated_at > '2026-02-05 14:00:00' order by updated_at desc;\n\nSELECT * FROM crm_configurations where id = 896; # 946 - Mintago , portalId: 6621281\nSELECT * FROM opportunities WHERE team_id = 946 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 946 and updated_at > '2026-02-05 14:00:00' order by updated_at desc;\n\nSELECT * FROM crm_configurations where id = 617; # 641 - PCS , portalId: 5244937\nSELECT * FROM opportunities WHERE team_id = 641 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 641 and updated_at > '2026-02-05 14:00:00' order by updated_at desc; # 7th\n# ********************************************************************\nSELECT * FROM crm_configurations where id = 649; # 670 - Eventeny , portalId: 4492849\nSELECT * FROM opportunities WHERE team_id = 670 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 670 and updated_at > '2026-02-09 08:00:00' order by updated_at desc; #\n\nSELECT * FROM crm_configurations where id = 48; # 51 - CleanCloud , portalId: 4373137\nSELECT * FROM opportunities WHERE team_id = 51 and updated_at > '2026-03-04 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 51 and updated_at > '2026-02-09 08:00:00' order by updated_at desc;\nselect * from users where team_id = 51; # 7783\nSELECT * FROM groups WHERE uuid_to_bin('8a8d2cb6-8b55-4fa3-8b5c-5f0e3d8de59a') = uuid; # 1130\nselect * from activity_searches where user_id = 7783;\nselect * from activity_search_filters where activity_search_id IN (32291, 32292);\n\nSELECT asf.activity_search_id, asf.id, asf.value\nFROM activity_search_filters asf\nWHERE asf.filter = 'group_id'\nAND asf.value IN (\n SELECT CONCAT(\n HEX(SUBSTR(uuid, 5, 4)), '-',\n HEX(SUBSTR(uuid, 3, 2)), '-',\n HEX(SUBSTR(uuid, 1, 2)), '-',\n HEX(SUBSTR(uuid, 9, 2)), '-',\n HEX(SUBSTR(uuid, 11))\n )\n FROM groups\n WHERE deleted_at IS NOT NULL\n);\n\nSELECT * FROM crm_configurations where id = 272; # 290 - Bonham & Brook , portalId: 5705856\nSELECT * FROM opportunities WHERE team_id = 290 and updated_at > '2026-02-05 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 290 and updated_at > '2026-02-09 08:00:00' order by updated_at desc; # 6th\n# ********************************************************************\nSELECT * FROM crm_configurations where provider = 'hubspot';\nSELECT * FROM crm_configurations where id = 1056; # 1119 - Chromatic , portalId: 45602133\nSELECT * FROM opportunities WHERE team_id = 1119 and remotely_created_at > '2026-02-01 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 1119 and updated_at > '2026-02-09 09:00:00' order by updated_at desc; # null\n# ********************************************************************\n\nselect * from contacts where crm_provider_id = '003Uu00000ojD4NIAU';\nselect\n cp.*\n# DISTINCT t.id\n# cp.id, cp.user_id, t.id, cp.crm_configuration_id, cp.contact_fields\nFROM crm_profiles cp\nJOIN crm_configurations crm on crm.id = cp.crm_configuration_id\nJOIN users u on u.id = cp.user_id\nJOIN teams t ON t.id = crm.team_id\nWHERE crm.provider = 'salesforce' and t.status = 'active'\n and cp.archived_at IS NULL and u.deleted_at IS NULL\n and t.id NOT IN (1093)\n and t.id = 2\n and cp.contact_fields IS NULL;\n# and c.crm_provider_id = '003Uu00000ojD4NIAU';\n\nSELECT * FROM users WHERE id = 26484;\nSELECT * FROM crm_profiles WHERE user_id = 26484;\nSELECT * FROM social_accounts WHERE sociable_id = 26484;\nSELECT * FROM crm_configurations where provider = 'salesforce';\nselect * from users where id IN (10022, 10403);\nselect * from users where team_id IN (526);\nselect * from teams where id IN (526, 532);\nselect * from crm_configurations where id IN (500, 516);\nselect * from crm_profiles where crm_configuration_id IN (500, 516) and user_id IN (10022, 10403);\nselect * from contacts where crm_configuration_id IN (500, 516) and crm_provider_id = '003Uu00000ojD4NIAU';\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 526 and sa.provider = 'salesforce';\nselect * from team_settings where team_id IN (526, 532);\n\nselect * from users where id IN (22824);\nselect * from crm_profiles where crm_configuration_id IN (1026);\n\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1093 and sa.provider = 'salesforce';\n\nselect * from teams where id = 1099;\nselect * from users where id = 29643\n\nselect * from activity_processing_states;\n\nSELECT * FROM teams where name LIKE '%Fare%'; # 233\nSELECT * FROM opportunities where crm_configuration_id = 215\n# and crm_provider_id = 'oppo_ogESZf2P50nDrd1nGPvKDXeA6sSaTN5v51Lp4ayVzKR'\n;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1088 and sa.provider = 'hubspot';\n\nSELECT * FROM teams order by updated_at DESC\nSELECT * FROM crm_configurations WHERE id = 1019; # SimpleConsign 1088 - no social account\n\nselect * from crm_configurations where provider = 'pipedrive';\n\nselect * from teams where id = 957;\nselect * from crm_configurations where id = 957;\n\nSELECT * FROM teams WHERE name LIKE '%Prolific%'; # 544, 518, 10743\nSELECT * FROM opportunities where crm_configuration_id = 518 order by id desc;\n\nselect * from users where team_id = 1; # 26726 - Gabriela Dureva\nSELECT * FROM opportunities where user_id = 26726; # 16834447 - Prolific\nselect * from activities where user_id = 26726 order by id desc;\nselect * from contacts where crm_configuration_id = 1\nand email IN ('charlotte.ward@prolific.com', 'frankie.bryant@prolific.com'); # 2094416, 2093620\nSELECT * FROM contacts WHERE id = 6284931;\n\nSELECT p.* FROM activities a JOIN participants p ON a.id = p.activity_id\nWHERE a.user_id = 26726 and p.lead_id IN (2094416, 2093620) and a.created_at > '2026-01-01 00:00:00' order by p.email;\n\nselect * from activities where id IN (75509259,75509261,75509261,75511034,75026464,75517602,75517605);\nselect * from crm_configurations where id = 1;\n\n43801692-1aeb-32ce-acba-5b80a479701a\n44c3c9cf-6f5e-75f3-8179-bc9f75dd2b1b\n405975c0-b3d0-7aaa-821f-09d59cae6dd1\n4caf848d-4bed-2299-b248-7788d41f9fca\n49bedc3f-f196-eef3-89c3-dea6a3b4aa63\n43420989-a09d-b8f8-9806-c8bbf7a02aac\n\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1 and sa.provider = 'salesforce';\n\nSELECT * FROM activities WHERE id = 75461988;\n\nSELECT * FROM activities WHERE uuid_to_bin('d6c5052e-e972-49e9-8912-26f2f7d6c5f6') = uuid;\n\nselect * from contacts where id = 17900517;\n\nselect * from contact_roles cr join crm_configurations crm on cr.crm_configuration_id = crm.id\nwhere crm.provider != 'salesforce';\n\nselect * from users where id = 21047;\nSELECT * FROM crm_configurations WHERE id = 892;\nSELECT * FROM teams WHERE id = 942;\nselect * from opportunities where team_id = 942 order by updated_at desc;\nselect * from contacts where team_id = 942 order by updated_at desc;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 942 and sa.provider = 'hubspot';\n\nSELECT * FROM opportunities where team_id = 1 and crm_provider_id IN ('006Pq00000NeH6XIAV', '006Pq000007z8kdIAA'); # 10697889, 6621430\nSELECT * FROM crm_configurations WHERE id = 1;\nSELECT * FROM teams WHERE crm_id = 1;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1 and sa.provider = 'salesforce';\n\nselect id, user_id, opportunity_fields from crm_profiles where crm_configuration_id = 1\nSELECT * FROM opportunities where team_id = 1 order by updated_at desc; # 10697889, 6621430\n\nselect * from teams where id = 852;\nselect * from groups where id = 2286;\nselect * from sidekick_settings where team_id = 852;\nselect * from default_activity_types where team_id = 852;\n\n\nSELECT cc.provider, cc.id, p.id, u.*\nFROM users u\nLEFT JOIN crm_profiles p ON u.id = p.user_id AND p.id IS NULL -- no profile\nINNER JOIN teams t ON u.team_id = t.id AND t.status = 'active' -- team is active\nINNER JOIN crm_configurations cc ON t.crm_id = cc.id\nWHERE u.status = 1 AND u.deleted_at IS NULL\nAND u.crm_required = 1\nAND u.team_id = 1\nORDER BY u.team_id;\n\nSELECT * FROM crm_profiles cp where cp.crm_configuration_id = 1 and cp.user_id IN (\n18481\n );\n\nSELECT cc.provider, cc.id, p.id, u.*\nFROM users u\nLEFT JOIN crm_profiles p ON u.id = p.user_id\nINNER JOIN teams t ON u.team_id = t.id AND t.status = 'active'\nINNER JOIN crm_configurations cc ON t.crm_id = cc.id\nWHERE u.status = 1\n AND u.deleted_at IS NULL\n AND u.crm_required = 1\n# AND u.team_id = 1\n AND p.id IS NULL -- Move this condition to WHERE clause\nORDER BY u.team_id;\n\nSELECT * FROM opportunities WHERE id = 20002609;\nselect * from teams where id = 1122; # Velatir, 29953 - christian@velatir.com\nselect * from crm_configurations where id = 1060;\nselect * from crm_layouts where crm_configuration_id = 1060;\nSELECT * FROM crm_layout_entities WHERE crm_layout_id = 3596;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1122 and sa.provider = 'hubspot';\nselect * from opportunities where team_id = 1122 order by updated_at desc;\n\nselect * from crm_field_data where object_type = 'contact';\n\nSELECT * FROM activities WHERE uuid_to_bin('374fc8ed-3315-4c9f-9b25-318b7fd2928f') = uuid; # 76584262\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 248 and sa.provider = 'salesforce';\n\nSELECT * FROM crm_profiles where user_id = 24115; # 005QF000002CswMYAS\nSELECT * FROM users where id = 24115;\nSELECT * FROM accounts where id = 4002896;\nSELECT * FROM teams WHERE name LIKE '%adswerve%';\nSELECT * FROM opportunities where crm_configuration_id = 230 AND crm_provider_id IN (\"0069N000003GIQ9QAO\",\"0061r000019yGP9AAM\",\"0066900001S2KWlAAN\",\"0066900001TDpj2AAD\",\"0066900001b8uEwAAI\",\"0069N000001rQi0QAE\",\"006QF00000KD40mYAD\",\"006QF00000LzpRJYAZ\",\"0069N000002uomtQAA\",\"0069N000002xlMLQAY\",\"0066900001NV6ubAAD\",\"0061r00001HJp45AAD\",\"006QF00000uTlUoYAK\",\"006QF00000v0bZqYAI\");\nSELECT * FROM opportunities WHERE crm_configuration_id = 230 AND crm_provider_id = '0069N000003GIQ9QAO'; # 6272203\n\nSELECT u.id, u.email, ac.name, a.* FROM activities a\nJOIN users u ON a.user_id = u.id\nJOIN accounts ac ON a.account_id = ac.id\nWHERE\nuuid_to_bin('e3269598-b562-44fb-b5e9-9d2694dc63e0') = a.uuid or\nuuid_to_bin('66ddc3ab-4e15-45aa-af0c-248c1eece593') = a.uuid or\nuuid_to_bin('826bd328-e1cc-4213-b8d8-572454cacc07') = a.uuid;\n\nselect * from users where id = 5825;\nSELECT * FROM activities WHERE uuid_to_bin('e56aa2e8-231a-421b-ab1f-cb38ed2bf573') = uuid;\n\nselect * from activities where uuid_to_bin('91e13b2f-2d1b-45f8-b1fd-1141b6563782') = uuid;\n19594, 862\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 862 and sa.provider = 'salesforce';\n\nselect * from automated_reports where id = 36;\nselect ar.frequency, r.*, ar.* from automated_report_results r\njoin automated_reports ar on r.report_id = ar.id\nwhere ar.frequency != 'one_off';\n\nselect s.* from activity_searches s join users u ON s.user_id = u.id where u.team_id = 882;\nselect * from nudges n where n.activity_search_id\n\nselect * from teams where created_at > '2026-03-09';\nSELECT * FROM crm_layouts WHERE crm_configuration_id = 1065; # 1065\nSELECT * FROM crm_layout_entities WHERE crm_layout_id = 3617;\n\nselect * from users where team_id = 1 and name like '%Lukas%'; # 7160\n\nSELECT * FROM teams WHERE id = 575;\nselect * from opportunities where team_id = 575;\nSELECT * FROM teams WHERE name LIKE '%Integrum ESG%'; # 1126, 1065,\nselect * from opportunities where team_id = 1126;\nSELECT * FROM teams WHERE name LIKE '%Base%'; # 1125, 1063,\nselect * from opportunities where team_id = 1125;\nselect * from contacts c\nwhere c.team_id = 882;\n\nSELECT * FROM activities WHERE id = 76822967;\nSELECT * FROM crm_profiles WHERE user_id = 15440;\nSELECT * FROM crm_profiles WHERE crm_configuration_id = 555;\nSELECT * FROM crm_configurations WHERE id = 555;\nSELECT * FROM users WHERE id = 15440; # team. 581, gr. 15440, pl. 3911, act. field 162182\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 581 and sa.provider = 'salesforce';\n\nSELECT * FROM automated_report_results order by id desc;\n\nselect * from features;\nselect * from team_features where feature_id = 40;\n\nselect * from teams where id = 556;\n\nselect * from automated_reports;\nwhere id = 54; # 4fdd41f6-dcf0-30d0-b339-7345381b6044 , [\"pdf\",\"podcast\"]\nSELECT * FROM automated_report_results WHERE uuid_to_bin('822fa41b-afd3-43a9-a248-86b0e36f3131') = uuid;\nselect * from automated_report_results order by id desc;\nSELECT * FROM automated_report_results WHERE id = 1919;\n\nselect * from automated_report_results WHERE report_id = 54;\n\nselect * from opportunities where id = 7594349;\n\nSELECT * FROM teams WHERE name LIKE '%Les%'; # 711, 692, 16067 - jiminnyintegration@lesmills.com\nselect * from playbooks where team_id = 711; # event 226147\nSELECT * FROM playbook_categories WHERE playbook_id = 5515;\nSELECT * FROM crm_fields WHERE crm_configuration_id = 692 and object_type = 'event';\nSELECT * FROM crm_fields WHERE id = 226147;\nSELECT * FROM crm_field_values WHERE crm_field_id = 226147;\n\nSELECT * FROM crm_configurations WHERE id = 692;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 711 and sa.provider = 'salesforce';\n\nSELECT * FROM crm_profiles cp JOIN users u on u.id = cp.user_id WHERE u.team_id = 711;\n\nselect * from leads;\n\nselect * from calendars;\n\nSELECT\n t.id AS team_id,\n t.name,\n LOWER(SUBSTRING_INDEX(c.calendar_provider_id, '@', -1)) AS calendar_domain\nFROM teams t\nJOIN users u ON u.team_id = t.id\nJOIN calendars c ON c.user_id = u.id AND c.status = 'active' AND c.calendar_provider_id LIKE '%@%'\nLEFT JOIN team_domains td\n ON td.team_id = t.id\n AND td.deleted_at IS NULL\n AND td.domain = LOWER(SUBSTRING_INDEX(c.calendar_provider_id, '@', -1))\nGROUP BY t.id, t.name, calendar_domain\nORDER BY t.name, calendar_domain;\n\nselect * from users u join calendars c on c.user_id = u.id\nwhere u.team_id = 882;\n\n\nselect * from activities where id = 74049485; # team 563 crm 537\nselect * from activities where id = 73272382; # team 563 crm 537\nselect * from activities where id = 64400389; # team 563 crm 537\nselect * from activities where id = 58081273; # team 563 crm 537\nselect * from activities where id = 54520297; # team 563 crm 537\nselect * from participants where activity_id = 58081273;\n\nselect * from activities where crm_configuration_id = 537 and provider = 'aircall'\nand account_id = 19003658 order by updated_at desc;\n\nselect * from contacts where crm_configuration_id = 537 and id = 35957759;\nselect * from accounts where crm_configuration_id = 537 and id = 19003658;\n\nselect * from automated_report_results where id = 1976;\nselect * from automated_reports where id = 583;\nselect * from activity_searches where id = 87714;\nselect * from activity_search_filters where activity_search_id = 87714;\n\nSELECT * FROM activities WHERE uuid_to_bin('8827f672-202d-4162-9d04-73ff5f0566a9') = uuid\nor uuid_to_bin('47842446-af51-4bcb-854f-cc6560290101') = uuid;\n\nSELECT * FROM crm_configurations WHERE provider = 'hubspot';\nselect * from rate_limits;\n\nselect * from automated_report_results where media_type = 'pdf' and status = 2\nand id IN (18, 1872);\nselect * from automated_reports where id = 54;\nSELECT * FROM users WHERE id IN (24623,29443,29613);\n\nSELECT * FROM automated_report_results WHERE uuid_to_bin('822fa41b-afd3-43a9-a248-86b0e36f3131') = uuid;\n\nselect * from text_relays where created_at > '2026-05-01';\nand id IN (32415, 32416);\n# and id = 32412;\n\nselect * from users where team_id = 2 and email like '%scott%' and id = 29510;\n\nSELECT * FROM activities WHERE uuid_to_bin('67cebfc2-ed56-44a2-8c68-7a0286ed8618') = uuid; # 79763436\n\nSELECT email_provider_id, COUNT(*) as count, GROUP_CONCAT(id) as ids, GROUP_CONCAT(status) as statuses\nFROM text_relays\nWHERE email_provider_id IN ('19e2027868a64b42', '19e2033ed8ea6b10')\nGROUP BY email_provider_id;\nSELECT id, status, telephony_provider_id, created_at\nFROM activities\nWHERE id IN (80028719, 80028846);\nSELECT id, status, code, email_sent_at, created_at, updated_at\nFROM text_relays\nWHERE id IN (32415, 32416);\nSELECT id, status, code, sender, recipient, created_at\nFROM text_relays\nWHERE sender LIKE '%mario.georgiev%' OR sender LIKE '%stoyan.tomov%'\nORDER BY created_at DESC\nLIMIT 10;\n\nSELECT id, uuid, status, code, sender, recipient, created_at, updated_at\nFROM text_relays\nWHERE uuid = uuid_to_bin('0626141c-27a6-4d8c-aff8-7c8020a2c656');\n\n# ***************\nSELECT DISTINCT u.id, u.email, u.name, u.softphone_number, COUNT(a.id) as sms_count\nFROM users u\nINNER JOIN activities a ON u.id = .user_id\nWHERE a.type LIKE 'sms%'\nAND a.created_at > DATE_SUB(NOW(), INTERVAL 30 DAY)\nGROUP BY u.id, u.email, u.name, u.softphone_number\nORDER BY sms_count DESC;\n\nselect * from teams where id = 1;\n\nselect * from roles;\n\n\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1117 and sa.provider = 'hubspot';\nSELECT * FROM activities WHERE uuid_to_bin('8024fffb-2df7-4017-91f4-d9f896050248') = uuid; # 79933459 YES\nSELECT * FROM activities WHERE uuid_to_bin('25529043-8094-4781-927f-4f4da2a8185c') = uuid; # 80186192 NO\nSELECT * FROM crm_configurations WHERE id = 1053;\nSELECT * FROM teams WHERE id = 1117;\nselect * from users where id = 30249;\nselect * from playbooks where id = 5473;\nselect * from playbook_categories where id = 43783;\nselect * from playbook_categories where playbook_id = 5473;\nselect * from crm_fields where id = 659242;\nselect * from crm_field_values where crm_field_id = 659242;\n\nSELECT * FROM crm_field_data fd\n# JOIN crm_fields f ON fd.crm_field_id = f.id\n# JOIN activities a ON fd.activity_id = a.id\nWHERE activity_id = 79933459\n# AND f.crm_provider_id = 'hs_activity_type';\n\n\nSELECT * FROM activity_messages;\nselect * from text_relays where created_at > '2026-05-01';\nselect * from activities where user_id IN (7160, 18608) and created_at > '2026-05-22' order by id desc;\nselect * from users where team_id = 1 and id IN (18608, 13934, 7160);\nselect * from activities where user_id = 7160 order by id desc limit 10;\n\nselect * from accounts where team_id = 1 and name = 'Column5';\n\nselect * from users where name like '%Subra%'; # 31054, 1117\nselect * from teams where id = 1117;\nselect * from activity_searches where user_id = 31054;\nselect * from activity_search_filters where activity_search_id IN (88882, 88902);","depth":4,"on_screen":true,"value":"SELECT * FROM teams WHERE name LIKE '%litify%'; # 1069, 994, 24993\nSELECT * FROM users WHERE id = 25061;\nSELECT * FROM crm_profiles WHERE crm_configuration_id = 994;\nSELECT * FROM crm_profiles WHERE user_id = 25061;\n\nselect * from crm_configurations where id = 834;\nSELECT * FROM teams WHERE id = 882;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 882 and sa.provider = 'hubspot';\n\nSELECT * FROM crm_configurations where id = 882; # 933 - GoGlobal\nSELECT * FROM opportunities WHERE team_id = 933 order by updated_at desc;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 933 and sa.provider = 'hubspot';\n\nSELECT * FROM crm_configurations WHERE provider = 'hubspot' and crm_provider_id = 7270388;\n\nSELECT * FROM contacts where crm_configuration_id = 834;\nSELECT * FROM opportunities WHERE team_id = 933\n# AND crm_provider_id IN ('20131586060','46017317898','52543911090','53451356564','54101251892','54323768459');\nAND id IN (8482561,18352941,19042734,19232139,19445140,19472541);\nSELECT * FROM opportunity_contacts\nWHERE opportunity_id IN (8482561,18352941,19042734,19232139,19445140,19472541);\n\n# ********************************************************************\nSELECT * FROM crm_configurations where id = 485; #\nSELECT * FROM opportunities WHERE team_id = 933 order by updated_at desc;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 933 and sa.provider = 'hubspot';\n\nselect crm.provider, l.* from leads l join crm_configurations crm on l.crm_configuration_id = crm.id\nwhere crm.provider NOT IN ('salesforce', 'integration-app', 'bullhorn', 'copper')\n# and l.converted_at IS NOT NULL\n;\n\n# ********************************************************************\nSELECT * FROM activities a WHERE type IN ('email-inbound', 'email-outbound')\nand opportunity_id IS NULL\norder by id desc;\n\nSELECT * FROM teams WHERE id = 604; # 598\nSELECT * FROM activities WHERE id = 74410828; # chelseaw@allvoices.co\nSELECT * FROM accounts WHERE id = 20068382;\nSELECT * FROM accounts WHERE id = 35186038;\n\nSELECT * FROM contacts WHERE team_id = 852 and updated_at > '2026-01-23 12:30:00' order by updated_at desc;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 559 and sa.provider = 'hubspot';\n\nSELECT * FROM activities WHERE uuid_to_bin('cb6342b6-a183-401c-b0af-ede92b2ae763') = uuid;\nselect * from sidekick_settings where team_id = 781;\n\nSELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id = 26651871; # Teya\nSELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id = 7562435;\nSELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id = 8420347; # opflit 2100\n\nSELECT * FROM crm_layouts WHERE crm_configuration_id = 711;\nSELECT * FROM activities where crm_configuration_id = 711 and crm_provider_id IS NULL\nand is_internal = 0 and status = 'completed'\norder by id desc;\n\nSELECT * FROM crm_layout_entities\nWHERE crm_layout_id IN (2352, 2353);\n;\n\nSELECT * FROM crm_configurations where provider = 'hubspot' and id = 530;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 556 and sa.provider = 'hubspot';\n\nSELECT * FROM activities WHERE uuid_to_bin('c6ca4b22-7738-4563-a95d-b8a9598924ae') = uuid;\nSELECT * FROM activities WHERE uuid_to_bin('442abb2b-28bd-4be8-9c25-19e9bf02766d') = uuid;\nselect * from contacts\nwhere crm_configuration_id = 530\nand crm_provider_id = 872252;\n\nselect * from activities where crm_configuration_id = 530\nand user_id = 14343 and type like '%softphone%'\nand created_at between '2026-01-28 15:00:00' and '2026-01-28 15:10:00';\n\nSELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id = 25666868; # Teya\nSELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id = 8646335; # Teya\nSELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id IN (5933397);\n\n\nSELECT t.name, t.id, t.owner_id, c.id, c.provider, c.crm_base_url FROM teams t\nJOIN crm_configurations c ON t.id = c.team_id\nWHERE t.status = 'active';\n\nSELECT * FROM teams where id = 1091;\nSELECT * FROM crm_configurations where team_id = 1091;\nSELECT * FROM activity_providers where team_id = 1091;\nSELECT * FROM activities where crm_configuration_id = 1024 and type IN ('softphone', 'softphone-outbound')\nand provider NOT IN ('hubspot', 'aircall')\n# and telephony_provider_id = '019c1131-a22f-4792-b9ea-20adf6a02ed0'\norder by id desc;\n\n\nSELECT * FROM teams WHERE name LIKE '%Leadventure%';\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1091 and sa.provider = 'salesforce';\n\nSELECT * FROM teams WHERE name LIKE '%Wilson%'; # 862, 812\nSELECT * FROM teams where id = 862;\nSELECT * FROM crm_configurations where team_id = 862;\nSELECT * FROM activity_providers where team_id = 862;\nSELECT * FROM activities where crm_configuration_id = 812 and type IN ('softphone', 'softphone-outbound')\nand provider NOT IN ('hubspot', 'aircall')\n# and telephony_provider_id = '019c1131-a22f-4792-b9ea-20adf6a02ed0'\norder by id desc;\n\n\nSELECT t.id, crm.id, crm.provider, ap.* FROM teams t\njoin crm_configurations crm on t.id = crm.team_id\njoin activity_providers ap on t.id = ap.team_id\nwhere t.status = 'active' and ap.is_enabled = 1\nand crm.provider = 'hubspot'\nand ap.provider NOT IN ('hubspot', 'aircall', 'uploader', 'gong', 'twilio', 'zoom-bot', 'google-meet', 'ms-teams',\n 'outreach', 'close', 'ringcentral', 'dialpad', 'zoom-phone');\n\nSELECT * FROM teams where id = 1068;\nSELECT * FROM crm_configurations where team_id = 1068;\nSELECT * FROM activity_providers where team_id = 1068;\n\nSELECT * FROM activities a\nwhere crm_configuration_id = 993 and type IN ('softphone', 'softphone-outbound')\nand a.provider NOT IN ('hubspot', 'uploader', 'gong', 'twilio', 'google-meet', 'ms-teams','close'\n )\n# and telephony_provider_id = '019c1131-a22f-4792-b9ea-20adf6a02ed0'\norder by a.id desc;\n\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1068 and sa.provider = 'hubspot';\n\n# ********************************************************************\n# ********************************************************************\n# ********************************************************************\nSELECT * FROM crm_configurations where id = 882; # 933 - GoGlobal , portalId: 6017093\nSELECT * FROM opportunities WHERE team_id = 933 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 933 and updated_at > '2026-02-06 00:00:00' order by updated_at desc;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 933 and sa.provider = 'hubspot';\n\n# ********************************************************************\nSELECT * FROM crm_configurations where id = 834; # 882 - AnyVan , portalId: 5468262\nSELECT * FROM contacts WHERE crm_configuration_id = 834 and updated_at > '2026-03-30 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE crm_configuration_id = 834 and updated_at > '2026-03-04 08:00:00' order by updated_at desc;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 882 and sa.provider = 'hubspot';\nselect * from crm_layouts where crm_configuration_id = 834;\nselect * from crm_layout_entities where crm_layout_id = 2780;\nselect * from crm_fields where id IN (321153,321192,321193,321194);\n\nSELECT * FROM opportunities WHERE crm_configuration_id = 834 and id = 10993426;\n# ********************************************************************\nSELECT * FROM crm_configurations where id = 988; # 1057 - Teya (543ce4f4-168c-4571-91ea-5b35c253f06f) , portalId: 26651871\nSELECT * FROM opportunities WHERE team_id = 1057 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 1057 and updated_at > '2026-02-04 00:00:00' order by updated_at desc;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1057 and sa.provider = 'hubspot';\n\nSELECT * FROM crm_configurations where id = 533; # 559 - Connectd , portalId: 6710988\nSELECT * FROM opportunities WHERE team_id = 559 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 559 and updated_at > '2026-02-04 00:00:00' order by updated_at desc;\n\nSELECT * FROM crm_configurations where id = 801; # 852 - Rise Vision , portalId: 2700250\nSELECT * FROM opportunities WHERE team_id = 852 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 852 and updated_at > '2026-02-04 00:00:00' order by updated_at desc; # 6th last\n\nSELECT * FROM crm_configurations where id = 962; # 1034 - evergrowth.io , portalId: 143180990\nSELECT * FROM opportunities WHERE team_id = 1034 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 1034 and updated_at > '2026-02-04 00:00:00' order by updated_at desc;\n\n# ********************************************************************\nSELECT * FROM crm_configurations where id = 1037; # 1102 - Jibble , portalId: 6649755\nSELECT * FROM opportunities WHERE team_id = 1102 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 1102 and updated_at > '2026-02-04 12:30:00' order by updated_at desc; # 8\n\nSELECT * FROM crm_configurations where id = 1015; # 1049 - Travefy , portalId: 48904401\nSELECT * FROM opportunities WHERE team_id = 1049 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 1049 and updated_at > '2026-02-04 12:30:00' order by updated_at desc; # 20\n\nSELECT * FROM crm_configurations where id = 64; # 70 - SalaryFinance , portalId: 3404115\nSELECT * FROM opportunities WHERE team_id = 70 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 70 and updated_at > '2026-02-04 12:30:00' order by updated_at desc; # 6th last\n\nSELECT * FROM crm_configurations where id = 802; # 853 - Street Group , portalId: 7658438\nSELECT * FROM opportunities WHERE team_id = 853 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 853 and updated_at > '2026-02-04 12:30:00' order by updated_at desc; # 10\n\nSELECT * FROM crm_configurations where id = 872; # 921 - In Professional Development , portalId: 9238273\nSELECT * FROM opportunities WHERE team_id = 921 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 921 and updated_at > '2026-02-04 12:30:00' order by updated_at desc; # 2\n\n# ********************************************************************\nSELECT * FROM crm_configurations where id = 550; # 576 - SeedLegals , portalId: 3028661\nSELECT * FROM opportunities WHERE team_id = 576 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 576 and updated_at > '2026-02-05 14:00:00' order by updated_at desc;\n\nSELECT * FROM crm_configurations where id = 989; # 1058 - rtaoutdoor.com , portalId: 22371204\nSELECT * FROM opportunities WHERE team_id = 1058 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 1058 and updated_at > '2026-02-05 14:00:00' order by updated_at desc;\n\nSELECT * FROM crm_configurations where id = 896; # 946 - Mintago , portalId: 6621281\nSELECT * FROM opportunities WHERE team_id = 946 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 946 and updated_at > '2026-02-05 14:00:00' order by updated_at desc;\n\nSELECT * FROM crm_configurations where id = 617; # 641 - PCS , portalId: 5244937\nSELECT * FROM opportunities WHERE team_id = 641 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 641 and updated_at > '2026-02-05 14:00:00' order by updated_at desc; # 7th\n# ********************************************************************\nSELECT * FROM crm_configurations where id = 649; # 670 - Eventeny , portalId: 4492849\nSELECT * FROM opportunities WHERE team_id = 670 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 670 and updated_at > '2026-02-09 08:00:00' order by updated_at desc; #\n\nSELECT * FROM crm_configurations where id = 48; # 51 - CleanCloud , portalId: 4373137\nSELECT * FROM opportunities WHERE team_id = 51 and updated_at > '2026-03-04 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 51 and updated_at > '2026-02-09 08:00:00' order by updated_at desc;\nselect * from users where team_id = 51; # 7783\nSELECT * FROM groups WHERE uuid_to_bin('8a8d2cb6-8b55-4fa3-8b5c-5f0e3d8de59a') = uuid; # 1130\nselect * from activity_searches where user_id = 7783;\nselect * from activity_search_filters where activity_search_id IN (32291, 32292);\n\nSELECT asf.activity_search_id, asf.id, asf.value\nFROM activity_search_filters asf\nWHERE asf.filter = 'group_id'\nAND asf.value IN (\n SELECT CONCAT(\n HEX(SUBSTR(uuid, 5, 4)), '-',\n HEX(SUBSTR(uuid, 3, 2)), '-',\n HEX(SUBSTR(uuid, 1, 2)), '-',\n HEX(SUBSTR(uuid, 9, 2)), '-',\n HEX(SUBSTR(uuid, 11))\n )\n FROM groups\n WHERE deleted_at IS NOT NULL\n);\n\nSELECT * FROM crm_configurations where id = 272; # 290 - Bonham & Brook , portalId: 5705856\nSELECT * FROM opportunities WHERE team_id = 290 and updated_at > '2026-02-05 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 290 and updated_at > '2026-02-09 08:00:00' order by updated_at desc; # 6th\n# ********************************************************************\nSELECT * FROM crm_configurations where provider = 'hubspot';\nSELECT * FROM crm_configurations where id = 1056; # 1119 - Chromatic , portalId: 45602133\nSELECT * FROM opportunities WHERE team_id = 1119 and remotely_created_at > '2026-02-01 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 1119 and updated_at > '2026-02-09 09:00:00' order by updated_at desc; # null\n# ********************************************************************\n\nselect * from contacts where crm_provider_id = '003Uu00000ojD4NIAU';\nselect\n cp.*\n# DISTINCT t.id\n# cp.id, cp.user_id, t.id, cp.crm_configuration_id, cp.contact_fields\nFROM crm_profiles cp\nJOIN crm_configurations crm on crm.id = cp.crm_configuration_id\nJOIN users u on u.id = cp.user_id\nJOIN teams t ON t.id = crm.team_id\nWHERE crm.provider = 'salesforce' and t.status = 'active'\n and cp.archived_at IS NULL and u.deleted_at IS NULL\n and t.id NOT IN (1093)\n and t.id = 2\n and cp.contact_fields IS NULL;\n# and c.crm_provider_id = '003Uu00000ojD4NIAU';\n\nSELECT * FROM users WHERE id = 26484;\nSELECT * FROM crm_profiles WHERE user_id = 26484;\nSELECT * FROM social_accounts WHERE sociable_id = 26484;\nSELECT * FROM crm_configurations where provider = 'salesforce';\nselect * from users where id IN (10022, 10403);\nselect * from users where team_id IN (526);\nselect * from teams where id IN (526, 532);\nselect * from crm_configurations where id IN (500, 516);\nselect * from crm_profiles where crm_configuration_id IN (500, 516) and user_id IN (10022, 10403);\nselect * from contacts where crm_configuration_id IN (500, 516) and crm_provider_id = '003Uu00000ojD4NIAU';\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 526 and sa.provider = 'salesforce';\nselect * from team_settings where team_id IN (526, 532);\n\nselect * from users where id IN (22824);\nselect * from crm_profiles where crm_configuration_id IN (1026);\n\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1093 and sa.provider = 'salesforce';\n\nselect * from teams where id = 1099;\nselect * from users where id = 29643\n\nselect * from activity_processing_states;\n\nSELECT * FROM teams where name LIKE '%Fare%'; # 233\nSELECT * FROM opportunities where crm_configuration_id = 215\n# and crm_provider_id = 'oppo_ogESZf2P50nDrd1nGPvKDXeA6sSaTN5v51Lp4ayVzKR'\n;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1088 and sa.provider = 'hubspot';\n\nSELECT * FROM teams order by updated_at DESC\nSELECT * FROM crm_configurations WHERE id = 1019; # SimpleConsign 1088 - no social account\n\nselect * from crm_configurations where provider = 'pipedrive';\n\nselect * from teams where id = 957;\nselect * from crm_configurations where id = 957;\n\nSELECT * FROM teams WHERE name LIKE '%Prolific%'; # 544, 518, 10743\nSELECT * FROM opportunities where crm_configuration_id = 518 order by id desc;\n\nselect * from users where team_id = 1; # 26726 - Gabriela Dureva\nSELECT * FROM opportunities where user_id = 26726; # 16834447 - Prolific\nselect * from activities where user_id = 26726 order by id desc;\nselect * from contacts where crm_configuration_id = 1\nand email IN ('charlotte.ward@prolific.com', 'frankie.bryant@prolific.com'); # 2094416, 2093620\nSELECT * FROM contacts WHERE id = 6284931;\n\nSELECT p.* FROM activities a JOIN participants p ON a.id = p.activity_id\nWHERE a.user_id = 26726 and p.lead_id IN (2094416, 2093620) and a.created_at > '2026-01-01 00:00:00' order by p.email;\n\nselect * from activities where id IN (75509259,75509261,75509261,75511034,75026464,75517602,75517605);\nselect * from crm_configurations where id = 1;\n\n43801692-1aeb-32ce-acba-5b80a479701a\n44c3c9cf-6f5e-75f3-8179-bc9f75dd2b1b\n405975c0-b3d0-7aaa-821f-09d59cae6dd1\n4caf848d-4bed-2299-b248-7788d41f9fca\n49bedc3f-f196-eef3-89c3-dea6a3b4aa63\n43420989-a09d-b8f8-9806-c8bbf7a02aac\n\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1 and sa.provider = 'salesforce';\n\nSELECT * FROM activities WHERE id = 75461988;\n\nSELECT * FROM activities WHERE uuid_to_bin('d6c5052e-e972-49e9-8912-26f2f7d6c5f6') = uuid;\n\nselect * from contacts where id = 17900517;\n\nselect * from contact_roles cr join crm_configurations crm on cr.crm_configuration_id = crm.id\nwhere crm.provider != 'salesforce';\n\nselect * from users where id = 21047;\nSELECT * FROM crm_configurations WHERE id = 892;\nSELECT * FROM teams WHERE id = 942;\nselect * from opportunities where team_id = 942 order by updated_at desc;\nselect * from contacts where team_id = 942 order by updated_at desc;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 942 and sa.provider = 'hubspot';\n\nSELECT * FROM opportunities where team_id = 1 and crm_provider_id IN ('006Pq00000NeH6XIAV', '006Pq000007z8kdIAA'); # 10697889, 6621430\nSELECT * FROM crm_configurations WHERE id = 1;\nSELECT * FROM teams WHERE crm_id = 1;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1 and sa.provider = 'salesforce';\n\nselect id, user_id, opportunity_fields from crm_profiles where crm_configuration_id = 1\nSELECT * FROM opportunities where team_id = 1 order by updated_at desc; # 10697889, 6621430\n\nselect * from teams where id = 852;\nselect * from groups where id = 2286;\nselect * from sidekick_settings where team_id = 852;\nselect * from default_activity_types where team_id = 852;\n\n\nSELECT cc.provider, cc.id, p.id, u.*\nFROM users u\nLEFT JOIN crm_profiles p ON u.id = p.user_id AND p.id IS NULL -- no profile\nINNER JOIN teams t ON u.team_id = t.id AND t.status = 'active' -- team is active\nINNER JOIN crm_configurations cc ON t.crm_id = cc.id\nWHERE u.status = 1 AND u.deleted_at IS NULL\nAND u.crm_required = 1\nAND u.team_id = 1\nORDER BY u.team_id;\n\nSELECT * FROM crm_profiles cp where cp.crm_configuration_id = 1 and cp.user_id IN (\n18481\n );\n\nSELECT cc.provider, cc.id, p.id, u.*\nFROM users u\nLEFT JOIN crm_profiles p ON u.id = p.user_id\nINNER JOIN teams t ON u.team_id = t.id AND t.status = 'active'\nINNER JOIN crm_configurations cc ON t.crm_id = cc.id\nWHERE u.status = 1\n AND u.deleted_at IS NULL\n AND u.crm_required = 1\n# AND u.team_id = 1\n AND p.id IS NULL -- Move this condition to WHERE clause\nORDER BY u.team_id;\n\nSELECT * FROM opportunities WHERE id = 20002609;\nselect * from teams where id = 1122; # Velatir, 29953 - christian@velatir.com\nselect * from crm_configurations where id = 1060;\nselect * from crm_layouts where crm_configuration_id = 1060;\nSELECT * FROM crm_layout_entities WHERE crm_layout_id = 3596;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1122 and sa.provider = 'hubspot';\nselect * from opportunities where team_id = 1122 order by updated_at desc;\n\nselect * from crm_field_data where object_type = 'contact';\n\nSELECT * FROM activities WHERE uuid_to_bin('374fc8ed-3315-4c9f-9b25-318b7fd2928f') = uuid; # 76584262\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 248 and sa.provider = 'salesforce';\n\nSELECT * FROM crm_profiles where user_id = 24115; # 005QF000002CswMYAS\nSELECT * FROM users where id = 24115;\nSELECT * FROM accounts where id = 4002896;\nSELECT * FROM teams WHERE name LIKE '%adswerve%';\nSELECT * FROM opportunities where crm_configuration_id = 230 AND crm_provider_id IN (\"0069N000003GIQ9QAO\",\"0061r000019yGP9AAM\",\"0066900001S2KWlAAN\",\"0066900001TDpj2AAD\",\"0066900001b8uEwAAI\",\"0069N000001rQi0QAE\",\"006QF00000KD40mYAD\",\"006QF00000LzpRJYAZ\",\"0069N000002uomtQAA\",\"0069N000002xlMLQAY\",\"0066900001NV6ubAAD\",\"0061r00001HJp45AAD\",\"006QF00000uTlUoYAK\",\"006QF00000v0bZqYAI\");\nSELECT * FROM opportunities WHERE crm_configuration_id = 230 AND crm_provider_id = '0069N000003GIQ9QAO'; # 6272203\n\nSELECT u.id, u.email, ac.name, a.* FROM activities a\nJOIN users u ON a.user_id = u.id\nJOIN accounts ac ON a.account_id = ac.id\nWHERE\nuuid_to_bin('e3269598-b562-44fb-b5e9-9d2694dc63e0') = a.uuid or\nuuid_to_bin('66ddc3ab-4e15-45aa-af0c-248c1eece593') = a.uuid or\nuuid_to_bin('826bd328-e1cc-4213-b8d8-572454cacc07') = a.uuid;\n\nselect * from users where id = 5825;\nSELECT * FROM activities WHERE uuid_to_bin('e56aa2e8-231a-421b-ab1f-cb38ed2bf573') = uuid;\n\nselect * from activities where uuid_to_bin('91e13b2f-2d1b-45f8-b1fd-1141b6563782') = uuid;\n19594, 862\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 862 and sa.provider = 'salesforce';\n\nselect * from automated_reports where id = 36;\nselect ar.frequency, r.*, ar.* from automated_report_results r\njoin automated_reports ar on r.report_id = ar.id\nwhere ar.frequency != 'one_off';\n\nselect s.* from activity_searches s join users u ON s.user_id = u.id where u.team_id = 882;\nselect * from nudges n where n.activity_search_id\n\nselect * from teams where created_at > '2026-03-09';\nSELECT * FROM crm_layouts WHERE crm_configuration_id = 1065; # 1065\nSELECT * FROM crm_layout_entities WHERE crm_layout_id = 3617;\n\nselect * from users where team_id = 1 and name like '%Lukas%'; # 7160\n\nSELECT * FROM teams WHERE id = 575;\nselect * from opportunities where team_id = 575;\nSELECT * FROM teams WHERE name LIKE '%Integrum ESG%'; # 1126, 1065,\nselect * from opportunities where team_id = 1126;\nSELECT * FROM teams WHERE name LIKE '%Base%'; # 1125, 1063,\nselect * from opportunities where team_id = 1125;\nselect * from contacts c\nwhere c.team_id = 882;\n\nSELECT * FROM activities WHERE id = 76822967;\nSELECT * FROM crm_profiles WHERE user_id = 15440;\nSELECT * FROM crm_profiles WHERE crm_configuration_id = 555;\nSELECT * FROM crm_configurations WHERE id = 555;\nSELECT * FROM users WHERE id = 15440; # team. 581, gr. 15440, pl. 3911, act. field 162182\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 581 and sa.provider = 'salesforce';\n\nSELECT * FROM automated_report_results order by id desc;\n\nselect * from features;\nselect * from team_features where feature_id = 40;\n\nselect * from teams where id = 556;\n\nselect * from automated_reports;\nwhere id = 54; # 4fdd41f6-dcf0-30d0-b339-7345381b6044 , [\"pdf\",\"podcast\"]\nSELECT * FROM automated_report_results WHERE uuid_to_bin('822fa41b-afd3-43a9-a248-86b0e36f3131') = uuid;\nselect * from automated_report_results order by id desc;\nSELECT * FROM automated_report_results WHERE id = 1919;\n\nselect * from automated_report_results WHERE report_id = 54;\n\nselect * from opportunities where id = 7594349;\n\nSELECT * FROM teams WHERE name LIKE '%Les%'; # 711, 692, 16067 - jiminnyintegration@lesmills.com\nselect * from playbooks where team_id = 711; # event 226147\nSELECT * FROM playbook_categories WHERE playbook_id = 5515;\nSELECT * FROM crm_fields WHERE crm_configuration_id = 692 and object_type = 'event';\nSELECT * FROM crm_fields WHERE id = 226147;\nSELECT * FROM crm_field_values WHERE crm_field_id = 226147;\n\nSELECT * FROM crm_configurations WHERE id = 692;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 711 and sa.provider = 'salesforce';\n\nSELECT * FROM crm_profiles cp JOIN users u on u.id = cp.user_id WHERE u.team_id = 711;\n\nselect * from leads;\n\nselect * from calendars;\n\nSELECT\n t.id AS team_id,\n t.name,\n LOWER(SUBSTRING_INDEX(c.calendar_provider_id, '@', -1)) AS calendar_domain\nFROM teams t\nJOIN users u ON u.team_id = t.id\nJOIN calendars c ON c.user_id = u.id AND c.status = 'active' AND c.calendar_provider_id LIKE '%@%'\nLEFT JOIN team_domains td\n ON td.team_id = t.id\n AND td.deleted_at IS NULL\n AND td.domain = LOWER(SUBSTRING_INDEX(c.calendar_provider_id, '@', -1))\nGROUP BY t.id, t.name, calendar_domain\nORDER BY t.name, calendar_domain;\n\nselect * from users u join calendars c on c.user_id = u.id\nwhere u.team_id = 882;\n\n\nselect * from activities where id = 74049485; # team 563 crm 537\nselect * from activities where id = 73272382; # team 563 crm 537\nselect * from activities where id = 64400389; # team 563 crm 537\nselect * from activities where id = 58081273; # team 563 crm 537\nselect * from activities where id = 54520297; # team 563 crm 537\nselect * from participants where activity_id = 58081273;\n\nselect * from activities where crm_configuration_id = 537 and provider = 'aircall'\nand account_id = 19003658 order by updated_at desc;\n\nselect * from contacts where crm_configuration_id = 537 and id = 35957759;\nselect * from accounts where crm_configuration_id = 537 and id = 19003658;\n\nselect * from automated_report_results where id = 1976;\nselect * from automated_reports where id = 583;\nselect * from activity_searches where id = 87714;\nselect * from activity_search_filters where activity_search_id = 87714;\n\nSELECT * FROM activities WHERE uuid_to_bin('8827f672-202d-4162-9d04-73ff5f0566a9') = uuid\nor uuid_to_bin('47842446-af51-4bcb-854f-cc6560290101') = uuid;\n\nSELECT * FROM crm_configurations WHERE provider = 'hubspot';\nselect * from rate_limits;\n\nselect * from automated_report_results where media_type = 'pdf' and status = 2\nand id IN (18, 1872);\nselect * from automated_reports where id = 54;\nSELECT * FROM users WHERE id IN (24623,29443,29613);\n\nSELECT * FROM automated_report_results WHERE uuid_to_bin('822fa41b-afd3-43a9-a248-86b0e36f3131') = uuid;\n\nselect * from text_relays where created_at > '2026-05-01';\nand id IN (32415, 32416);\n# and id = 32412;\n\nselect * from users where team_id = 2 and email like '%scott%' and id = 29510;\n\nSELECT * FROM activities WHERE uuid_to_bin('67cebfc2-ed56-44a2-8c68-7a0286ed8618') = uuid; # 79763436\n\nSELECT email_provider_id, COUNT(*) as count, GROUP_CONCAT(id) as ids, GROUP_CONCAT(status) as statuses\nFROM text_relays\nWHERE email_provider_id IN ('19e2027868a64b42', '19e2033ed8ea6b10')\nGROUP BY email_provider_id;\nSELECT id, status, telephony_provider_id, created_at\nFROM activities\nWHERE id IN (80028719, 80028846);\nSELECT id, status, code, email_sent_at, created_at, updated_at\nFROM text_relays\nWHERE id IN (32415, 32416);\nSELECT id, status, code, sender, recipient, created_at\nFROM text_relays\nWHERE sender LIKE '%mario.georgiev%' OR sender LIKE '%stoyan.tomov%'\nORDER BY created_at DESC\nLIMIT 10;\n\nSELECT id, uuid, status, code, sender, recipient, created_at, updated_at\nFROM text_relays\nWHERE uuid = uuid_to_bin('0626141c-27a6-4d8c-aff8-7c8020a2c656');\n\n# ***************\nSELECT DISTINCT u.id, u.email, u.name, u.softphone_number, COUNT(a.id) as sms_count\nFROM users u\nINNER JOIN activities a ON u.id = .user_id\nWHERE a.type LIKE 'sms%'\nAND a.created_at > DATE_SUB(NOW(), INTERVAL 30 DAY)\nGROUP BY u.id, u.email, u.name, u.softphone_number\nORDER BY sms_count DESC;\n\nselect * from teams where id = 1;\n\nselect * from roles;\n\n\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1117 and sa.provider = 'hubspot';\nSELECT * FROM activities WHERE uuid_to_bin('8024fffb-2df7-4017-91f4-d9f896050248') = uuid; # 79933459 YES\nSELECT * FROM activities WHERE uuid_to_bin('25529043-8094-4781-927f-4f4da2a8185c') = uuid; # 80186192 NO\nSELECT * FROM crm_configurations WHERE id = 1053;\nSELECT * FROM teams WHERE id = 1117;\nselect * from users where id = 30249;\nselect * from playbooks where id = 5473;\nselect * from playbook_categories where id = 43783;\nselect * from playbook_categories where playbook_id = 5473;\nselect * from crm_fields where id = 659242;\nselect * from crm_field_values where crm_field_id = 659242;\n\nSELECT * FROM crm_field_data fd\n# JOIN crm_fields f ON fd.crm_field_id = f.id\n# JOIN activities a ON fd.activity_id = a.id\nWHERE activity_id = 79933459\n# AND f.crm_provider_id = 'hs_activity_type';\n\n\nSELECT * FROM activity_messages;\nselect * from text_relays where created_at > '2026-05-01';\nselect * from activities where user_id IN (7160, 18608) and created_at > '2026-05-22' order by id desc;\nselect * from users where team_id = 1 and id IN (18608, 13934, 7160);\nselect * from activities where user_id = 7160 order by id desc limit 10;\n\nselect * from accounts where team_id = 1 and name = 'Column5';\n\nselect * from users where name like '%Subra%'; # 31054, 1117\nselect * from teams where id = 1117;\nselect * from activity_searches where user_id = 31054;\nselect * from activity_search_filters where activity_search_id IN (88882, 88902);","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
3375247957313029282
|
8179532969745786684
|
visual_change
|
accessibility
|
NULL
|
Project: faVsco.js, menu
#12121 on JY-20963-fix-im Project: faVsco.js, menu
#12121 on JY-20963-fix-import-on-deleted-entity, menu
Start Listening for PHP Debug Connections
ServiceTest
Run 'ServiceTest'
Debug 'ServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
4
388
9
103
4
Previous Highlighted Error
Next Highlighted Error
<?php
namespace Jiminny\Models;
use Carbon\Carbon;
use Database\Factories\ActivityFactory;
use DateTimeInterface;
use Exception;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Database\Eloquent;
use Illuminate\Database\Eloquent\Attributes\Scope;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Auth;
use InvalidArgumentException;
use Jiminny\Component\ElasticSearch;
use Jiminny\Component\MeetingBot;
use Jiminny\Component\Model\BitwiseFlagTrait;
use Jiminny\Component\PlaybackPage\Comments\Services\ActivityCommentService;
use Jiminny\Component\Sidekick\SidekickService;
use Jiminny\Component\Uuid\UuidAwareInterface;
use Jiminny\Component\Workflow;
use Jiminny\Contracts;
use Jiminny\Contracts\Crm\ProspectInterface;
use Jiminny\DTO\ImportCall\Call;
use Jiminny\Events\Activities\ActivityTypeUpdated;
use Jiminny\Events\Activities\ActivityUpdated;
use Jiminny\Events\Activities\ProspectUpdated;
use Jiminny\Events\Activities\StageUpdated;
use Jiminny\Events\Activities\StatusUpdated;
use Jiminny\Events\Activities\TitleUpdated;
use Jiminny\Exceptions\InvalidArgumentException as InvalidArgumentJiminnyException;
use Jiminny\Exceptions\LogicException;
use Jiminny\Exceptions\RuntimeException;
use Jiminny\Models;
use Jiminny\Models\Activity\ActivitySummaryLog;
use Jiminny\Models\Activity\ActivityUploadSetting;
use Jiminny\Models\Activity\AvailabilityNotification;
use Jiminny\Models\Activity\CoachRequest;
use Jiminny\Models\Activity\Comment;
use Jiminny\Models\Activity\Log;
use Jiminny\Models\Activity\Message;
use Jiminny\Models\Activity\Moment;
use Jiminny\Models\Activity\Note;
use Jiminny\Models\Activity\ParticipantSpeech;
use Jiminny\Models\Activity\Play;
use Jiminny\Models\Activity\Question;
use Jiminny\Models\Activity\Share;
use Jiminny\Models\Activity\Snapshot;
use Jiminny\Models\Activity\Stats;
use Jiminny\Models\Activity\SubscriptionSet;
use Jiminny\Models\Activity\TopicTrigger;
use Jiminny\Models\Activity\Transcription;
use Jiminny\Models\Calendar\CalendarEvent;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Models\Crm\FieldData;
use Jiminny\Models\ElasticSearch\ActivityElasticSearchTrait;
use Jiminny\Models\Feature\FeatureEnum;
use Jiminny\Models\Participant\Connection;
use Jiminny\Models\Playlist\Activity as PlaylistActivity;
use Jiminny\Services\Activity\ActivityProviderRegistry;
use Jiminny\Services\Activity\Import\DataResolvers\UpdateCrmDataByStrategy;
use Jiminny\Services\Activity\Import\DataResolvers\UpdateCrmDataResolverFactory;
use Jiminny\Services\Activity\Import\DataResolvers\UpdateCrmDataResolverInterface;
use Jiminny\Traits\Enums;
use Jiminny\Traits\RequiresUUID;
use Jiminny\Utils\CurrencyFormatter;
use NumberFormatter;
use function in_array;
/**
* Jiminny\Models\Activity
*
* @property null|int $auto_score filled from ES hydrator, not in DB!
* @property-read Account|null $account
* @property-read CalendarEvent|null $calendarEvent
* @property-read Contact|null $contact
* @property-read Lead|null $lead
* @property-read Opportunity|null $opportunity
* @property-read Stage|null $stage
* @property int $id
* @property mixed|null $uuid
* @property string|null $source
* @property string|null $external_id
* @property string $provider
* @property string|null $location
* @property string|null $telephony_provider_id
* @property int|null $from_participant_id
* @property int|null $to_participant_id
* @property int|null $device_id
* @property string|null $type
* @property int|null $playbook_category_id
* @property int $user_id
* @property int|null $lead_id
* @property int|null $account_id
* @property int|null $contact_id
* @property int|null $opportunity_id
* @property int|null $stage_id
* @property string|null $value
* @property int|null $crm_configuration_id
* @property string|null $crm_provider_id
* @property string|null $language
* @property int|null $transcription_id
* @property int $duration
* @property string $status
* @property int|null $on_air
* @property int|null $calendar_event_id
* @property string $recording_state
* @property bool|null $recording_preference
* @property int $recording_reason_code
* @property int $summary_reminder_sent
* @property \Illuminate\Support\Carbon|null $log_reminder_sent_at
* @property \Illuminate\Support\Carbon|null $organizer_notified_at
* @property bool|null $has_recording_prompt
* @property bool $is_internal
* @property int $is_locked
* @property int $is_recording
* @property bool|null $is_processed
* @property bool $is_private
* @property bool $is_instant_invite
* @property string|null $poster_path
* @property string|null $summary
* @property string|null $title
* @property string|null $description
* @property \Illuminate\Support\Carbon|null $scheduled_start_time
* @property \Illuminate\Support\Carbon|null $scheduled_end_time
* @property \Illuminate\Support\Carbon|null $actual_start_time
* @property \Illuminate\Support\Carbon|null $actual_end_time
* @property int|null $uploaded_by
* @property \Illuminate\Support\Carbon|null $deleted_at
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property string|null $average_score
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Participant> $activeParticipants
* @property-read int|null $active_participants_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Scorecard\ActivityScorecardRuleTrigger> $activityScorecardRuleTriggers
* @property-read int|null $activity_scorecard_rule_triggers_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Scorecard\ActivityScorecardRule> $activityScorecardRules
* @property-read int|null $activity_scorecard_rules_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, AvailabilityNotification> $availabilityNotifications
* @property-read int|null $availability_notifications_count
* @property-read \Jiminny\Models\PlaybookCategory|null $category
* @property-read \Illuminate\Database\Eloquent\Collection<int, CoachRequest> $coachRequests
* @property-read int|null $coach_requests_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\CoachingFeedback> $coachingFeedbacks
* @property-read int|null $coaching_feedbacks_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, Message> $coachingMessages
* @property-read int|null $coaching_messages_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, Comment> $comments
* @property-read int|null $comments_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, Connection> $connections
* @property-read int|null $connections_count
* @property-read Configuration|null $crm
* @property-read \Illuminate\Database\Eloquent\Collection<int, FieldData> $data
* @property-read int|null $data_count
* @property-read \Jiminny\Models\Device|null $device
* @property-read \Kalnoy\Nestedset\Collection<int, \Jiminny\Models\Playlist> $favoritePlaylists
* @property-read int|null $favorite_playlists_count
* @property-read \Jiminny\Models\Participant|null $from
* @property-read string|null $activity_title
* @property-read mixed $comment_count
* @property-read mixed $duration_for_humans
* @property-read string $duration_for_humans_short
* @property-read int $favorite_count
* @property-read mixed $favorites_count
* @property-read mixed $formatted_value
* @property-read string $id_string
* @property-read \Jiminny\Models\Participant|null $organizer
* @property-read mixed $play_count
* @property-read int|null $plays_count
* @property-read ?ProspectInterface $prospect
* @property-read string|null $prospect_name
* @property-read mixed $prospect_type
* @property-read mixed $share_count
* @property-read int|null $shares_count
* @property-read int|null $tracks_with_telephony_count
* @property-read int|null $visible_comments_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\CoachingFeedback> $latestCoachingFeedbacks
* @property-read int|null $latest_coaching_feedbacks_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, Log> $logs
* @property-read int|null $logs_count
* @property-read \Jiminny\Models\Track|null $masterTrack
* @property-read \Illuminate\Database\Eloquent\Collection<int, Message> $messages
* @property-read int|null $messages_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, Moment> $moments
* @property-read int|null $moments_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, Note> $notes
* @property-read int|null $notes_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Participant\Share> $participantShares
* @property-read int|null $participant_shares_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, ParticipantSpeech> $participantSpeeches
* @property-read int|null $participant_speeches_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Participant\ParticipantStats> $participantStats
* @property-read int|null $participant_stats_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Participant> $participants
* @property-read int|null $participants_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, PlaylistActivity> $playlistActivities
* @property-read int|null $playlist_activities_count
* @property-read \Kalnoy\Nestedset\Collection<int, \Jiminny\Models\Playlist> $playlists
* @property-read int|null $playlists_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, Play> $plays
* @property-read \Illuminate\Database\Eloquent\Collection<int, Question> $questions
* @property-read int|null $questions_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Session> $sessions
* @property-read int|null $sessions_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, Share> $shares
* @property-read \Illuminate\Database\Eloquent\Collection<int, Snapshot> $snapshots
* @property-read int|null $snapshots_count
* @property-read Stats|null $stats
* @property-read \Jiminny\Models\Participant|null $to
* @property-read \Illuminate\Database\Eloquent\Collection<int, TopicTrigger> $topicTriggers
* @property-read int|null $topic_triggers_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Track> $tracks
* @property-read int|null $tracks_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Track> $tracksWithTelephony
* @property-read Transcription|null $transcription
* @property-read \Jiminny\Models\User $user
* @property-read \Illuminate\Database\Eloquent\Collection<int, Comment> $visibleComments
*
* @method static \Illuminate\Database\Eloquent\Collection<int, static> all($columns = ['*'])
* @method static \Jiminny\Component\Eloquent\Builder|Activity chunkByIdDesc($count, callable $callback, $column = null, $alias = null)
* @method static \Database\Factories\ActivityFactory factory(...$parameters)
* @method static \Illuminate\Database\Eloquent\Collection<int, static> get($columns = ['*'])
* @method static \Jiminny\Component\Eloquent\Builder|Activity heldBetween(\Carbon\Carbon $start, \Carbon\Carbon $end)
* @method static \Jiminny\Component\Eloquent\Builder|Activity idOrUuId($idOrUuid, bool $first = true)
* @method static \Jiminny\Component\Eloquent\Builder|Activity newModelQuery()
* @method static \Jiminny\Component\Eloquent\Builder|Activity newQuery()
* @method static Builder|Activity onlyTrashed()
* @method static \Jiminny\Component\Eloquent\Builder|Activity query()
* @method static \Jiminny\Component\Eloquent\Builder|Activity scheduledBetween(\Carbon\Carbon $start, \Carbon\Carbon $end)
* @method static \Jiminny\Component\Eloquent\Builder|Activity inOpenDeals()
* @method static \Jiminny\Component\Eloquent\Builder|Activity notInOpenDeals()
* @method static \Jiminny\Component\Eloquent\Builder|Activity forTeam(int $teamId)
* @method static \Jiminny\Component\Eloquent\Builder|Activity search(callable $searchQuery, $key = null, $sortByResults = true)
* @method static \Jiminny\Component\Eloquent\Builder|Activity uuid(string $uuid, bool $first = true)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereAccountId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereActualEndTime($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereActualStartTime($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereAverageScore($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereCalendarEventId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereContactId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereCreatedAt($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereCrmConfigurationId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereCrmProviderId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereDeletedAt($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereDescription($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereDeviceId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereDuration($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereFromParticipantId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereHasRecordingPrompt($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereIsInstantInvite($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereIsInternal($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereIsLocked($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereIsPrivate($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereIsProcessed($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereIsRecording($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereLanguage($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereLeadId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereLocation($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereLogReminderSentAt($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereOnAir($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereOpportunityId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereOrganizerNotifiedAt($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity wherePlaybookCategoryId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity wherePosterPath($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereProvider($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereRecordingPreference($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereRecordingReasonCode($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereRecordingState($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereScheduledEndTime($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereScheduledStartTime($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereSource($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereExternalId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereStageId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereStatus($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereSummary($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereSummaryReminderSent($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereTelephonyProviderId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereTitle($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereToParticipantId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereTranscriptionId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereType($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereUpdatedAt($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereUploadedBy($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereUserId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereUuid($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereValue($value)
* @method static Builder|Activity withTrashed()
* @method static Builder|Activity withoutTrashed()
*
* @mixin \Eloquent
*/
class Activity extends Model implements
ElasticSearch\Contract\Searchable,
Workflow\Workflow\WorkflowAwareInterface,
Models\Contracts\ActivityContract,
Contracts\Model\ActivityInterface,
UuidAwareInterface
{
use HasFactory;
use Enums;
use SoftDeletes;
use RequiresUUID;
use BitwiseFlagTrait;
use ElasticSearch\Model\Searchable;
use ActivityElasticSearchTrait;
use Workflow\Workflow\WorkflowAware {
transitionTo as traitTransitionTo;
}
public const int FLAG_RECORDING_REASON_DEFAULT = 0;
// Recording Prompted but never started
public const int FLAG_RECORDING_REASON_COMPLIANCE_PROMPT = 1;
public const int FLAG_RECORDING_REASON_COMPLIANCE_RESUMED = 2;
public const int FLAG_RECORDING_REASON_NO_AUDIO = 3;
// Recording Disabled by Organization
public const int FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT = 4;
// Recording was restricted to one-side recordings only
public const int FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT_ONE_SIDE = 8;
// Recording was not started because it was internal and team setting disabled that.
public const int FLAG_RECORDING_REASON_TEAM_INTERNAL_DISABLED = 16;
// Recording was not started because it was internal and user setting disabled that.
public const int FLAG_RECORDING_REASON_USER_INTERNAL_DISABLED = 32;
// Recording was not started because user setting disabled automatic recording.
public const int FLAG_RECORDING_REASON_USER_AUTOMATIC_DISABLED = 64;
// Recording was not started because team setting disabled automatic recording.
public const int FLAG_RECORDING_REASON_TEAM_AUTOMATIC_DISABLED = 128;
// Recording was not started because user has overriden default.
public const int FLAG_RECORDING_REASON_PREFERENCE_OVERRIDE = 256;
// Recording was not started because they don't want internal, and this meeting was not scheduled/imported in time.
public const int FLAG_RECORDING_REASON_USER_INTERNAL_DISABLED_UNSCHEDULED = 512;
// Recording was not started because their team setting does excludes the meeting type.
public const int FLAG_RECORDING_REASON_UNSUPPORTED_TYPE = 1024;
// Recording was not started because the external provider disabled it (or recording is missing etc).
public const int FLAG_RECORDING_REASON_EXTERNALLY_DISABLED = 2048;
// Recording was stopped externally ("exit-meeting" Pusher event)
public const int FLAG_RECORDING_REASON_STOPPED_EXTERNALLY = 384;
// Recording couldn't be started due to Zoom hosting conflict error
public const int FLAG_RECORDING_REASON_HOSTING_CONFLICT = 448;
// meeting.failed event with reason code BOT_DENIED_FROM_LOBBY
public const int FLAG_RECORDING_REASON_MEETING_BOT_DENIED_FROM_LOBBY = 4096;
// meeting.failed event with reason code LOBBY_TIMEOUT
public const int FLAG_RECORDING_REASON_MEETING_BOT_LOBBY_TIMEOUT = 8192;
// meeting.failed event with reason code BOT_KICKED
public const int FLAG_RECORDING_REASON_MEETING_BOT_KICKED = 16384;
// meeting.failed event with reason code UNKNOWN
public const int FLAG_RECORDING_REASON_MEETING_BOT_UNKNOWN = 32768;
public const int FLAG_RECORDING_REASON_CONSENT_DENIED = 65536;
// Invalid meeting (e.g. URL is invalid, or the meeting is not found)
public const int FLAG_RECORDING_REASON_MEETING_BOT_INVALID = 131072;
// The host stopped the recording.
public const int FLAG_RECORDING_REASON_USER_STOPPED = 262144;
// Recording was not started because an alternative vendor disabled it (or overrode it).
public const int FLAG_RECORDING_REASON_VENDOR_OVERRIDE = 1048576;
// Login required meeting.failed code
public const int FLAG_RECORDING_REASON_LOGIN_REQUIRED = 524288;
// Password for meeting was not provided - meeting.failed code
public const int FLAG_RECORDING_REASON_MEETING_PASSWORD_NOT_PROVIDED = 2097152;
// meeting.failed - when the meeting is locked
public const int FLAG_RECORDING_REASON_MEETING_IS_LOCKED = 4194304;
// max recording duration reached
public const int FLAG_RECORDING_REASON_MAX_DURATION_REACHED = 8388608;
// recording size is too small
public const int FLAG_RECORDING_REASON_EMPTY_RECORDING = 16777216;
// meeting.failed - when bot is redirected to sign in page multiple times
public const int FLAG_RECORDING_REASON_MAX_RESTART_COUNT_IS_REACHED = 33554432;
// meeting.failed event with reason code CONNECTION_LOST
public const int FLAG_RECORDING_REASON_MEETING_BOT_CONNECTION_LOST = 67108864;
// recording is corrupted.
public const int FLAG_RECORDING_REASON_MEDIA_FILE_UNSUPPORTED_MIME_TYPE = 134217728;
// meeting ended in lobby
public const int FLAG_RECORDING_REASON_MEETING_ENDED_IN_LOBBY = 268435456;
// meeting not started
public const int FLAG_RECORDING_REASON_REASON_MEETING_NOT_STARTED = 536870912;
// unfinished zoom custom disclaimer
public const int FLAG_RECORDING_REASON_FEATURE_RULE_NOT_FOUND_ERROR = 1073741824;
// recording download failed - server error
public const int FLAG_RECORDING_REASON_SERVER_ERROR = 2147483648;
// recording download failed - client code 404
public const int FLAG_RECORDING_REASON_NOT_FOUND = 2147483649;
// recording download failed - client code 401, 403
public const int FLAG_RECORDING_REASON_ACCESS_DENIED = 2147483650;
// recording download failed - client code 429
public const int FLAG_RECORDING_REASON_TOO_MANY_REQUESTS = 2147483651;
// recording download failed - unknown client error
public const int FLAG_RECORDING_REASON_CLIENT_ERROR = 2147483652;
// recording download failed - unknown error
public const int FLAG_RECORDING_REASON_UNKNOWN_ERROR = 2147483653;
// It has been setup ahead of time through calendar
public const string STATUS_SCHEDULED = 'scheduled';
// It is awaiting audio.
public const string STATUS_PENDING = 'pending';
// Participant(s) dialed in, awaiting organizer.
public const string STATUS_RINGING = 'ringing';
// Call is in progress.
public const string STATUS_IN_PROGRESS = 'in-progress';
// It has ended.
public const string STATUS_COMPLETED = 'completed';
// Cancelled prior to starting.
public const string STATUS_CANCELLED = 'canceled';
public const string STATUS_DUPLICATED = 'duplicated'; // duplicated conference
public const string STATUS_STARTING_SOON = 'starting-soon';
public const string STATUS_BOT_CREATE_SENT = 'bot-create-sent';
public const string STATUS_BOT_INSTANCE_WORKER_ASSIGNED = 'worker-assigned';
public const string STATUS_BOT_INSTANCE_STARTED = 'bot-started';
// When bot instance is waiting in lobby
public const string STATUS_BOT_INSTANCE_WAITING_LOBBY = 'bot-waiting';
public const string STATUS_BUSY = 'busy';
public const string STATUS_NO_ANSWER = 'no-answer';
public const string STATUS_FAILED = 'failed'; // Used by SMS too
// SMS related
public const string STATUS_ACCEPTED = 'accepted';
public const string STATUS_QUEUED = 'queued';
public const string STATUS_SENDING = 'sending';
public const string STATUS_SENT = 'sent';
public const string STATUS_DELIVERED = 'delivered';
public const string STATUS_UNDELIVERED = 'undelivered';
public const string STATUS_RECEIVING = 'receiving';
public const string STATUS_RECEIVED = 'received';
public const string STATUS_RESENT = 'resent';
public const array SMS_STATUSES = [
Activity::STATUS_RECEIVED,
Activity::STATUS_SENT,
Activity::STATUS_DELIVERED,
];
public const array SOFT_PHONE_CONFERENCE_STATUSES = [
Activity::STATUS_IN_PROGRESS,
Activity::STATUS_COMPLETED,
];
// @todo refactor prefix from `TYPE_` to `CHANNEL_`
public const string TYPE_SOFTPHONE = 'softphone';
public const string TYPE_SOFTPHONE_INBOUND = 'softphone-inbound';
public const string TYPE_CONFERENCE = 'conference';
public const string TYPE_SMS_INBOUND = 'sms-inbound';
public const string TYPE_SMS_OUTBOUND = 'sms-outbound';
public const string TYPE_EMAIL_INBOUND = 'email-inbound';
public const string TYPE_EMAIL_OUTBOUND = 'email-outbound';
public const array CHANNELS = [
self::TYPE_SOFTPHONE,
self::TYPE_SOFTPHONE_INBOUND,
self::TYPE_CONFERENCE,
self::TYPE_SMS_INBOUND,
self::TYPE_SMS_OUTBOUND,
self::TYPE_EMAIL_INBOUND,
self::TYPE_EMAIL_OUTBOUND,
];
public const array PLAYABLE_CHANNELS = [
self::TYPE_SOFTPHONE,
self::TYPE_SOFTPHONE_INBOUND,
self::TYPE_CONFERENCE,
];
// Recording States
public const string RECORDING_OFF = 'off'; // Default state
public const string RECORDING_IN_PROGRESS = 'in-progress';
public const string RECORDING_PAUSED = 'paused';
public const string RECORDING_STOPPED = 'stopped'; // To never be resumed.
public const string RECORDING_RECORDED = 'recorded'; // At least some portion of it was recorded.
public const string RECORDING_FAILED = 'failed'; // Recording was attempted but failed for some reason.
// Live Stream States
public const int ON_AIR_DEFAULT = 0;
public const int ON_AIR_READY = 1;
public const int ON_AIR_PREPARING = 2;
public const int ON_AIR_STREAMING = 3;
public const int ON_AIR_FINISHED = 4;
public const int ON_AIR_NOT_STREAMED = 5;
public const int ON_AIR_ERROR = -1;
public const string SOURCE_GONG = 'gong';
public const string SOURCE_CHORUS = 'chorus';
public const string SOURCE_OUTLOOK = 'outlook';
public const string SOURCE_GOOGLE = 'google';
// Activity Providers
public const string PROVIDER_TWILIO = 'twilio'; // XXX: This is run via the Jiminny Provider.
public const string PROVIDER_OUTREACH = 'outreach';
public const string PROVIDER_ZOOM_BOT = 'zoom-bot';
public const string PROVIDER_SALESLOFT = 'salesloft';
public const string PROVIDER_GOOGLE = 'google';
public const string PROVIDER_AIRCALL = 'aircall';
public const string PROVIDER_JUSTCALL = 'justcall';
public const string PROVIDER_GOOGLE_MEET = 'google-meet';
public const string PROVIDER_GONG = 'gong';
public const string PROVIDER_HUBSPOT = 'hubspot';
public const string PROVIDER_CLOSE = 'close';
public const string PROVIDER_TEAMS = 'ms-teams';
public const string PROVIDER_SALESFORCE = 'salesforce';
public const string PROVIDER_GROOVE = 'groove';
public const string PROVIDER_XANT = 'xant';
public const string PROVIDER_OFFICE = 'office';
public const string PROVIDER_NATTERBOX = 'natterbox';
public const string PROVIDER_RINGCENTRAL = 'ringcentral';
public const string PROVIDER_RINGCENTRAL_VIDEO = 'ringcentral-video';
public const string PROVIDER_GOTOMEETING = 'go-to-meeting';
public const string PROVIDER_DEMODESK = 'demo-desk';
public const string PROVIDER_DIALPAD = 'dialpad';
public const string PROVIDER_ZOOM_PHONE = 'zoom-phone';
public const string PROVIDER_CLOUDCALL = 'cloudcall';
public const string PROVIDER_CLOUDCALL_US = 'cloudcall-us';
public const string PROVIDER_EIGHT_BY_EIGHT = 'eight-by-eight'; // "8x8" UK
public const string PROVIDER_EIGHT_BY_EIGHT_CA = 'eight-by-eight-ca'; // "8x8" Canada
public const string PROVIDER_EIGHT_BY_EIGHT_AP = 'eight-by-eight-ap'; // "8x8" Australia
public const string PROVIDER_EIGHT_BY_EIGHT_US_EAST = 'eight-by-eight-use'; // "8x8" US East
public const string PROVIDER_EIGHT_BY_EIGHT_US_WEST = 'eight-by-eight-usw'; // "8x8" US West
public const string PROVIDER_CONNECT_AND_SELL = 'connect-and-sell';
public const string PROVIDER_CLOUD_TALK = 'cloud-talk';
public const string PROVIDER_AMAZON_CONNECT = 'amazon-connect';
public const string PROVIDER_VONAGE = 'vonage';
public const string PROVIDER_MIGRATOR = 'migrator';
public const string PROVIDER_UPLOADER = 'uploader';
public const string PROVIDER_TALKDESK = 'talkdesk';
public const string PROVIDER_TWILIO_FLEX = 'twilio-flex';
public const string PROVIDER_TWILIO_FLEX_DIRECT = 'twilio-flex-direct';
public const string PROVIDER_TWILIO_VIDEO = 'twilio-video';
public const string PROVIDER_AVAYA = 'avaya';
public const string PROVIDER_TELUS = 'telus';
public const string PROVIDER_FIVE_NINE = 'five-nine';
public const string PROVIDER_APOLLO = 'apollo';
public const string PROVIDER_ORUM = 'orum';
public const string PROVIDER_BLOOBIRDS = 'bloobirds';
/**
* @const API_PROVIDERS
* A list of integrations that import calls via API instead of webhooks
*/
public const array API_PROVIDERS = [
self::PROVIDER_OUTREACH,
self::PROVIDER_SALESLOFT,
self::PROVIDER_HUBSPOT,
self::PROVIDER_GROOVE,
self::PROVIDER_XANT,
self::PROVIDER_NATTERBOX,
self::PROVIDER_CLOUDCALL,
self::PROVIDER_CLOUDCALL_US,
self::PROVIDER_EIGHT_BY_EIGHT,
self::PROVIDER_EIGHT_BY_EIGHT_CA,
self::PROVIDER_EIGHT_BY_EIGHT_AP,
self::PROVIDER_EIGHT_BY_EIGHT_US_EAST,
self::PROVIDER_EIGHT_BY_EIGHT_US_WEST,
self::PROVIDER_CONNECT_AND_SELL,
self::PROVIDER_CLOUD_TALK,
self::PROVIDER_AMAZON_CONNECT,
self::PROVIDER_VONAGE,
self::PROVIDER_TALKDESK,
self::PROVIDER_TWILIO_VIDEO,
self::PROVIDER_TWILIO_FLEX,
self::PROVIDER_TWILIO_FLEX_DIRECT,
self::PROVIDER_FIVE_NINE,
self::PROVIDER_APOLLO,
self::PROVIDER_ORUM,
self::PROVIDER_BLOOBIRDS,
self::PROVIDER_RINGCENTRAL,
self::PROVIDER_AVAYA,
self::PROVIDER_TELUS,
];
public const array FINITE_STATES = [
self::TYPE_SOFTPHONE => [
self::STATUS_COMPLETED,
self::STATUS_FAILED,
self::STATUS_NO_ANSWER,
self::STATUS_BUSY,
],
self::TYPE_SOFTPHONE_INBOUND => [
self::STATUS_COMPLETED,
self::STATUS_FAILED,
self::STATUS_NO_ANSWER,
self::STATUS_BUSY,
],
self::TYPE_CONFERENCE => self::FINITE_STATES_CONFERENCE,
];
public const array FINITE_STATES_CONFERENCE = [
self::STATUS_COMPLETED,
self::STATUS_FAILED,
self::STATUS_CANCELLED,
];
public const array MEETING_BOT_JOIN_ATTEMPTED = [
self::STATUS_BOT_INSTANCE_WAITING_LOBBY,
self::STATUS_BOT_INSTANCE_STARTED,
];
public static array $enumStatuses = [
self::STATUS_SCHEDULED,
self::STATUS_PENDING,
self::STATUS_RINGING,
self::STATUS_IN_PROGRESS,
self::STATUS_COMPLETED,
self::STATUS_CANCELLED,
self::STATUS_BUSY,
self::STATUS_NO_ANSWER,
self::STATUS_FAILED,
self::STATUS_ACCEPTED,
self::STATUS_QUEUED,
self::STATUS_SENDING,
self::STATUS_SENT,
self::STATUS_RESENT,
self::STATUS_DELIVERED,
self::STATUS_UNDELIVERED,
self::STATUS_RECEIVING,
self::STATUS_RECEIVED,
self::STATUS_BOT_INSTANCE_WAITING_LOBBY,
self::STATUS_STARTING_SOON,
self::STATUS_BOT_INSTANCE_WORKER_ASSIGNED,
self::STATUS_BOT_INSTANCE_STARTED,
self::STATUS_DUPLICATED,
];
public static array $enumProviders = [
self::PROVIDER_TWILIO,
self::PROVIDER_OUTREACH,
self::PROVIDER_ZOOM_BOT,
self::PROVIDER_SALESLOFT,
self::PROVIDER_AIRCALL,
self::PROVIDER_JUSTCALL,
self::PROVIDER_GOOGLE_MEET,
self::PROVIDER_GONG,
self::PROVIDER_HUBSPOT,
self::PROVIDER_CLOSE,
self::PROVIDER_TEAMS,
self::PROVIDER_SALESFORCE,
self::PROVIDER_GROOVE,
self::PROVIDER_XANT,
self::PROVIDER_GOOGLE,
self::PROVIDER_OFFICE,
self::PROVIDER_NATTERBOX,
self::PROVIDER_RINGCENTRAL,
self::PROVIDER_RINGCENTRAL_VIDEO,
self::PROVIDER_GOTOMEETING,
self::PROVIDER_DEMODESK,
self::PROVIDER_DIALPAD,
self::PROVIDER_ZOOM_PHONE,
self::PROVIDER_CLOUDCALL,
self::PROVIDER_CLOUDCALL_US,
self::PROVIDER_EIGHT_BY_EIGHT,
self::PROVIDER_EIGHT_BY_EIGHT_CA,
self::PROVIDER_EIGHT_BY_EIGHT_AP,
self::PROVIDER_EIGHT_BY_EIGHT_US_EAST,
self::PROVIDER_EIGHT_BY_EIGHT_US_WEST,
self::PROVIDER_CONNECT_AND_SELL,
self::PROVIDER_CLOUD_TALK,
self::PROVIDER_AMAZON_CONNECT,
self::PROVIDER_VONAGE,
self::PROVIDER_TALKDESK,
self::PROVIDER_TWILIO_FLEX,
self::PROVIDER_TWILIO_FLEX_DIRECT,
self::PROVIDER_TWILIO_VIDEO,
self::PROVIDER_AVAYA,
self::PROVIDER_TELUS,
self::PROVIDER_FIVE_NINE,
self::PROVIDER_APOLLO,
self::PROVIDER_ORUM,
self::PROVIDER_BLOOBIRDS,
];
public static $enumRecordingStates = [
self::RECORDING_OFF, // Default state
self::RECORDING_IN_PROGRESS,
self::RECORDING_PAUSED,
self::RECORDING_STOPPED,
self::RECORDING_RECORDED,
self::RECORDING_FAILED,
];
// @Important:
// This collection is not used anywhere, and is fully duplicated by the Channels const.
// Validate if it is referred somehow via the enum trait, and if not, remove it entirely.
// An even better strategy will be to move all those constants to a dedicated class
protected array $enumTypes = [
self::TYPE_SOFTPHONE,
self::TYPE_SOFTPHONE_INBOUND,
self::TYPE_CONFERENCE,
self::TYPE_SMS_INBOUND,
self::TYPE_SMS_OUTBOUND,
self::TYPE_EMAIL_INBOUND,
self::TYPE_EMAIL_OUTBOUND,
];
protected static $enumFailedStatuses = [
self::STATUS_NO_ANSWER,
self::STATUS_FAILED,
self::STATUS_BUSY,
self::STATUS_CANCELLED,
];
protected $table = 'activities';
protected $fillable = [
// Type of activity.
'type', // @todo refactor to `channel`
// The activity type.
'playbook_category_id',
// User who hosts the activity.
'user_id',
// Related Lead record (if applicable)
'lead_id',
// Related Account record (if applicable)
'account_id',
// Related Contact record (if applicable)
'contact_id',
// Related Opportunity record (if applicable)
'opportunity_id',
// Stage of activity.
'stage_id',
// Value of opportunity.
'value',
// If the activity relates to a CRM task.
'crm_provider_id',
// If the activity was created through an external device.
'device_id',
// the activity's language code
'language',
// transcription id
'transcription_id',
// Duration of the call, with microseconds precision.
'duration',
// One of enumStatuses above.
'status',
// Have we reminded them to log the call?
'log_reminder_sent_at',
// If activity is private or inter-org, flagged here.
'is_internal',
// Managers and above can mark a call as private, to exclude it from other team members
'is_private',
'is_processed',
// Boolean for this activity being instant invite handled.
'is_instant_invite',
// If activity is in recording state, flagged here.
'recording_state',
// If activity recording is overidden from default.
'recording_preference',
// if recording did (not) happen, why that is
'recording_reason_code',
// Average score, updated during
'average_score',
// Summary that the organizer has taken after the call.
'summary',
// Subject of the activity, usually taken from calendar event.
'title',
// Description of the activity, usually taken from calendar event.
'description',
// Start time, usually taken from calendar event.
'scheduled_start_time',
// End time, usually taken from calendar event.
'scheduled_end_time',
// When the call actually started.
'actual_start_time',
// When the call actually ended.
'actual_end_time',
// SMS: Message reference
'telephony_provider_id',
// SMS: Participant who sent message
'from_participant_id',
// SMS: Participant who should receive the message
'to_participant_id',
// When an external guest joins an organizers meeting room and the organizer is not present,
// send them an SMS notification that someone has joined.
'organizer_notified_at',
// where was the activity imported from
'source',
// The id in the source system (e.g. the bot id in Recall.ai)
'external_id',
// The provider, by default it is twilio.
'provider',
// Meeting location url
'location',
// The snapshot for displaying a poster image.
'poster_path',
'crm_configuration_id',
// If there is an automated message that the conversation is being recorded
'has_recording_prompt',
// If the activity is being live-streamed
'on_air',
'calendar_event_id',
];
protected $appends = [
'id_string',
'organizer',
];
protected $hidden = [
'uuid',
];
protected $visible = [
'id_string',
'type',
'duration',
'average_score',
'status',
'log_reminder_sent_at',
'title',
'description',
'is_internal',
'scheduled_start_time',
'scheduled_end_time',
'actual_start_time',
'actual_end_time',
'user',
'category',
'account',
'contact',
'opportunity',
'lead',
'stage',
'stats',
'participants',
'playlists',
'tracks',
'comments',
'plays',
'coachingFeedbacks',
'shares',
'favorites',
'language',
'transcription',
'is_private',
'is_instant_invite',
'on_air',
'calendar_event_id',
];
protected function casts(): array
{
return [
'scheduled_start_time' => 'datetime',
'scheduled_end_time' => 'datetime',
'actual_start_time' => 'datetime',
'actual_end_time' => 'datetime',
'organizer_notified_at' => 'datetime',
'log_reminder_sent_at' => 'datetime',
'is_internal' => 'boolean',
'duration' => 'integer',
'average_score' => 'decimal:2',
'is_private' => 'boolean',
'is_processed' => 'boolean',
'is_instant_invite' => 'boolean',
'value' => 'decimal:2',
'recording_preference' => 'boolean',
'recording_reason_code' => 'integer',
'has_recording_prompt' => 'boolean',
'on_air' => 'integer',
];
}
protected static function boot()
{
parent::boot();
static::updated(static function (Activity $activity) {
// If activity is about to start (pending, ringing, in-progress) or event is scheduled in less than 1 week
if (in_array($activity->status, [Activity::STATUS_PENDING, Activity::STATUS_RINGING, Activity::STATUS_IN_PROGRESS], true) ||
($activity->scheduled_start_time && (int) $activity->scheduled_start_time->diffInWeeks(new Carbon(), true) < 1)) {
if ($activity->isDirty('status')) {
event(new StatusUpdated($activity));
}
if ($activity->isDirty('stage_id')) {
event(new StageUpdated($activity));
}
if ($activity->isDirty(['lead_id', 'account_id', 'contact_id'])) {
event(new ProspectUpdated($activity));
}
if ($activity->isDirty('opportunity_id')) {
event(new ActivityUpdated($activity, 'activity.opportunity-updated', Auth::user()));
}
if ($activity->isDirty('title')) {
event(new TitleUpdated($activity));
}
}
if ($activity->isDirty('playbook_category_id')) {
event(new ActivityTypeUpdated($activity));
}
});
static::deleted(static function (Activity $activity) {
// Hard delete associated playlistActivities
$activity->playlistActivities()->delete();
});
}
public function getOrganizerAttribute(): ?Participant
{
$participant = $this->participants()->where('user_id', $this->user_id)->first();
if (! $participant instanceof Participant && $participant !== null) {
throw new RuntimeException(sprintf('$participant must be an instance of "%s" or null', Participant::class));
}
return $participant;
}
public function getFormattedValueAttribute()
{
$currencyCode = 'USD';
if ($this->opportunity) {
$currencyCode = $this->opportunity->getCurrencyCode();
}
$formatter = new CurrencyFormatter();
$formatter->setTextAttribute(NumberFormatter::CURRENCY_CODE, $currencyCode);
$formatter->setAttribute(NumberFormatter::MAX_FRACTION_DIGITS, 0);
return $formatter->format($this->value, $currencyCode);
}
public function getProspectNameAttribute(): ?string
{
$prospectName = null;
if ($this->lead_id) {
$prospectName = $this->lead->name;
} elseif ($this->contact_id) {
$prospectName = $this->contact->name;
} elseif ($this->account_id) {
$prospectName = $this->account->name;
}
return $prospectName;
}
public function getProspectName(): ?string
{
/** @var string|null */
return $this->getAttribute('prospect_name');
}
/**
* Get activity title depending on prospect or title
*/
public function getActivityTitleAttribute(): ?string
{
$activityTitle = null;
if ($this->prospect && $this->prospect->getName()) {
if ($this->account_id) {
$activityTitle = $this->account->name;
} elseif ($this->lead_id) {
$activityTitle = $this->lead->company;
} elseif ($this->contact_id) {
$activityTitle = $this->contact->account ? $this->contact->account->name : $this->contact->name;
}
} elseif ($this->title) {
$activityTitle = $this->title;
}
return $activityTitle;
}
public function wasRecentlyCreated(): bool
{
return $this->wasRecentlyCreated;
}
public function getProspectTypeAttribute()
{
$prospectType = null;
if ($this->lead_id) {
$prospectType = 'Lead';
} elseif ($this->contact_id) {
$prospectType = 'Contact';
} elseif ($this->account_id) {
$prospectType = 'Account';
}
return $prospectType;
}
/**
* Return the best match for prospect. Results are in the following order of priority:
* 1. Lead
* 2. Contact
* 3. Account
* 4. NULL
*/
public function getProspectAttribute(): ?ProspectInterface
{
if ($this->hasLead()) {
return $this->getLead();
}
if ($this->hasContact()) {
return $this->getContact();
}
if ($this->hasAccount()) {
return $this->getAccount();
}
return null;
}
public function getTitleAttribute($value): ?string
{
return \getActivityTitleAttribute(
$this->user->name,
$this->getType(),
$value,
$this->prospect->name ?? null,
$this->from->national_phone_number ?? null
);
}
public function getTitle(): ?string
{
return $this->getAttribute('title');
}
public function getSummary(): ?string
{
return $this->getAttribute('summary');
}
public function isInternal(): bool
{
return $this->getAttribute('is_internal');
}
public function getIsPrivate(): bool
{
return $this->getAttribute('is_private');
}
public function getDescription(): ?string
{
return $this->getAttribute('description');
}
public function hasTitle(): bool
{
return $this->getOriginal('title') !== null;
}
public function getPlayCountAttribute()
{
return $this->getPlaysCountAttribute();
}
public function getPlaysCountAttribute()
{
if (! isset($this->attributes['plays_count'])) {
$this->loadCount('plays');
}
return $this->attributes['plays_count'];
}
public function getCommentCountAttribute()
{
return $this->getCommentsCountAttribute();
}
public function getCommentsCountAttribute()
{
if (! isset($this->attributes['comments_count'])) {
$this->loadCount('comments');
}
return $this->attributes['comments_count'];
}
public function getVisibleCommentsCountAttribute()
{
if (! isset($this->attributes['visible_comments_count'])) {
$activityCommentsService = app(ActivityCommentService::class);
$user = Auth::user() instanceof User ? Auth::user() : null;
$this->attributes['visible_comments_count'] = $activityCommentsService
->getVisibleCommentsCount($this, $user);
}
return $this->attributes['visible_comments_count'];
}
public function getShareCountAttribute()
{
return $this->getSharesCountAttribute();
}
public function getSharesCountAttribute()
{
if (! isset($this->attributes['shares_count'])) {
$this->loadCount('shares');
}
return $this->attributes['shares_count'];
}
/**
* Get the count of favorites playlists this activity appears in
*/
public function getFavoriteCountAttribute(): int
{
return $this->getFavoritesCountAttribute();
}
public function getFavoritesCountAttribute()
{
if (! isset($this->attributes['favorites_count'])) {
$this->loadCount('favorites');
}
return $this->attributes['favorites_count'];
}
public function getActiveParticipantsCountAttribute()
{
if (! isset($this->attributes['active_participants_count'])) {
$this->loadCount('activeParticipants');
}
return $this->attributes['active_participants_count'];
}
public function getTracksWithTelephonyCountAttribute()
{
if (! isset($this->attributes['tracks_with_telephony_count'])) {
$this->loadCount('tracksWithTelephony');
}
return $this->attributes['tracks_with_telephony_count'];
}
/**
* @TEMP
* $this->loadCount('tracksWithTelephony') throws null pointer exception
*/
public function countTracksWithTelephony(): int
{
return $this->tracks()->whereNotNull('telephony_provider_id')->count();
}
public function getDuration(): float
{
return $this->getAttribute('duration');
}
public function getDurationForHumansAttribute()
{
return Carbon::now()->subSeconds($this->duration)->diffForHumans(now(), true);
}
public function getDurationForHumansShortAttribute(): string
{
return Carbon::now()->subSeconds($this->duration)->diffForHumans(now(), true, true);
}
public function hasRecordingPreference(): bool
{
re...
|
86378
|
NULL
|
NULL
|
NULL
|
|
86378
|
2962
|
2
|
2026-05-28T14:06:14.831749+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-28/1779 /Users/lukas/.screenpipe/data/data/2026-05-28/1779977174831_m1.jpg...
|
PhpStorm
|
faVsco.js – Activity.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
8043719072324535154
|
-8628527368849355612
|
visual_change
|
hybrid
|
NULL
|
Project: faVsco.js, menu
JAlActivity MonitorFileEd Project: faVsco.js, menu
JAlActivity MonitorFileEditViewWindowHelpActivity MonitorAll ProcessesProcess Name% CPUkernel_taskVirtual Machine Service for DockerPhpStormreplaydWindowServermds_storesscreenpipeFirefoxCP Isolated Web ContentlaunchservicesdiTerm2coreaudiodlaunchdSlack HelperFirefoxCP Isolated Web ContentActivity MonitorFirefoxCP Isolated Web ContentClaudelanguage_server_macos_armSlack Helper (Renderer)mobileassetdWispr Flow Helper (Renderer)Slackcef__server Helper (Renderer)FirefoxCP Isolated Web Contentbackupdio.kandji.KandjiAgent.ESF-ExtensionFirefoxCP Isolated Web ContentWispr Flow154,0137,599,053,135,818,514,97,85,35,24,74,64,64,44,23,93,63,63,62,92,82,62,32,22,12,01,91,8CPU Time22:45:44,951:51:07,432:31:03,015:41:35,087:49:56,231:03:33,003:34:31,1733:33,681:00:01,571:03:25,4656:58,1218:34,5214:21,7723:19,816:43,6940:31,4228:19,717:22,8457:20,8637,313:43,2513:13,358:11,5315:11,5853,8531:19,5829:45,402:34,77System:User:Idle:Threads lahl100% <78• Thu 28 May 17:06:14CPUMemoryEnergyDiskNetworkIdle Wake-UpsKind % GPUAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleApple0,00,09,40,040,20,00,00,00,00,40,00,03,80,00,00,00,00,00,00,00,00,00,00,00,00,00,00,046,22%47,34%6,44%CPU LOADThreads:Processes:SearchGPU Time0,000,007:45,740,003:19:55,470,005:03,900,000,0019,250,000,006:05,410,000,000,000,000,000,000,000,000,050,000,000,000,000,000,00PID Userrootlukaslukaslukas_windowserverrootlukaslukasrootlukas_coreaudiodrootlukaslukaslukaslukaslukaslukaslukasrootlukaslukaslukaslukasrootrootlukaslukas...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
86377
|
2962
|
1
|
2026-05-28T14:06:11.719343+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-28/1779 /Users/lukas/.screenpipe/data/data/2026-05-28/1779977171719_m1.jpg...
|
PhpStorm
|
faVsco.js – Activity.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
#12121 on JY-20963-fix-im Project: faVsco.js, menu
#12121 on JY-20963-fix-import-on-deleted-entity, menu
Start Listening for PHP Debug Connections
ServiceTest
Run 'ServiceTest'
Debug 'ServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
4
348
9
103
4
Previous Highlighted Error
Next Highlighted Error
<?php
namespace Jiminny\Models;
use Carbon\Carbon;
use Database\Factories\ActivityFactory;
use DateTimeInterface;
use Exception;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Database\Eloquent;
use Illuminate\Database\Eloquent\Attributes\Scope;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Auth;
use InvalidArgumentException;
use Jiminny\Component\ElasticSearch;
use Jiminny\Component\MeetingBot;
use Jiminny\Component\Model\BitwiseFlagTrait;
use Jiminny\Component\PlaybackPage\Comments\Services\ActivityCommentService;
use Jiminny\Component\Sidekick\SidekickService;
use Jiminny\Component\Uuid\UuidAwareInterface;
use Jiminny\Component\Workflow;
use Jiminny\Contracts;
use Jiminny\Contracts\Crm\ProspectInterface;
use Jiminny\DTO\ImportCall\Call;
use Jiminny\Events\Activities\ActivityTypeUpdated;
use Jiminny\Events\Activities\ActivityUpdated;
use Jiminny\Events\Activities\ProspectUpdated;
use Jiminny\Events\Activities\StageUpdated;
use Jiminny\Events\Activities\StatusUpdated;
use Jiminny\Events\Activities\TitleUpdated;
use Jiminny\Exceptions\InvalidArgumentException as InvalidArgumentJiminnyException;
use Jiminny\Exceptions\LogicException;
use Jiminny\Exceptions\RuntimeException;
use Jiminny\Models;
use Jiminny\Models\Activity\ActivitySummaryLog;
use Jiminny\Models\Activity\ActivityUploadSetting;
use Jiminny\Models\Activity\AvailabilityNotification;
use Jiminny\Models\Activity\CoachRequest;
use Jiminny\Models\Activity\Comment;
use Jiminny\Models\Activity\Log;
use Jiminny\Models\Activity\Message;
use Jiminny\Models\Activity\Moment;
use Jiminny\Models\Activity\Note;
use Jiminny\Models\Activity\ParticipantSpeech;
use Jiminny\Models\Activity\Play;
use Jiminny\Models\Activity\Question;
use Jiminny\Models\Activity\Share;
use Jiminny\Models\Activity\Snapshot;
use Jiminny\Models\Activity\Stats;
use Jiminny\Models\Activity\SubscriptionSet;
use Jiminny\Models\Activity\TopicTrigger;
use Jiminny\Models\Activity\Transcription;
use Jiminny\Models\Calendar\CalendarEvent;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Models\Crm\FieldData;
use Jiminny\Models\ElasticSearch\ActivityElasticSearchTrait;
use Jiminny\Models\Feature\FeatureEnum;
use Jiminny\Models\Participant\Connection;
use Jiminny\Models\Playlist\Activity as PlaylistActivity;
use Jiminny\Services\Activity\ActivityProviderRegistry;
use Jiminny\Services\Activity\Import\DataResolvers\UpdateCrmDataByStrategy;
use Jiminny\Services\Activity\Import\DataResolvers\UpdateCrmDataResolverFactory;
use Jiminny\Services\Activity\Import\DataResolvers\UpdateCrmDataResolverInterface;
use Jiminny\Traits\Enums;
use Jiminny\Traits\RequiresUUID;
use Jiminny\Utils\CurrencyFormatter;
use NumberFormatter;
use function in_array;
/**
* Jiminny\Models\Activity
*
* @property null|int $auto_score filled from ES hydrator, not in DB!
* @property-read Account|null $account
* @property-read CalendarEvent|null $calendarEvent
* @property-read Contact|null $contact
* @property-read Lead|null $lead
* @property-read Opportunity|null $opportunity
* @property-read Stage|null $stage
* @property int $id
* @property mixed|null $uuid
* @property string|null $source
* @property string|null $external_id
* @property string $provider
* @property string|null $location
* @property string|null $telephony_provider_id
* @property int|null $from_participant_id
* @property int|null $to_participant_id
* @property int|null $device_id
* @property string|null $type
* @property int|null $playbook_category_id
* @property int $user_id
* @property int|null $lead_id
* @property int|null $account_id
* @property int|null $contact_id
* @property int|null $opportunity_id
* @property int|null $stage_id
* @property string|null $value
* @property int|null $crm_configuration_id
* @property string|null $crm_provider_id
* @property string|null $language
* @property int|null $transcription_id
* @property int $duration
* @property string $status
* @property int|null $on_air
* @property int|null $calendar_event_id
* @property string $recording_state
* @property bool|null $recording_preference
* @property int $recording_reason_code
* @property int $summary_reminder_sent
* @property \Illuminate\Support\Carbon|null $log_reminder_sent_at
* @property \Illuminate\Support\Carbon|null $organizer_notified_at
* @property bool|null $has_recording_prompt
* @property bool $is_internal
* @property int $is_locked
* @property int $is_recording
* @property bool|null $is_processed
* @property bool $is_private
* @property bool $is_instant_invite
* @property string|null $poster_path
* @property string|null $summary
* @property string|null $title
* @property string|null $description
* @property \Illuminate\Support\Carbon|null $scheduled_start_time
* @property \Illuminate\Support\Carbon|null $scheduled_end_time
* @property \Illuminate\Support\Carbon|null $actual_start_time
* @property \Illuminate\Support\Carbon|null $actual_end_time
* @property int|null $uploaded_by
* @property \Illuminate\Support\Carbon|null $deleted_at
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property string|null $average_score
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Participant> $activeParticipants
* @property-read int|null $active_participants_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Scorecard\ActivityScorecardRuleTrigger> $activityScorecardRuleTriggers
* @property-read int|null $activity_scorecard_rule_triggers_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Scorecard\ActivityScorecardRule> $activityScorecardRules
* @property-read int|null $activity_scorecard_rules_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, AvailabilityNotification> $availabilityNotifications
* @property-read int|null $availability_notifications_count
* @property-read \Jiminny\Models\PlaybookCategory|null $category
* @property-read \Illuminate\Database\Eloquent\Collection<int, CoachRequest> $coachRequests
* @property-read int|null $coach_requests_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\CoachingFeedback> $coachingFeedbacks
* @property-read int|null $coaching_feedbacks_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, Message> $coachingMessages
* @property-read int|null $coaching_messages_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, Comment> $comments
* @property-read int|null $comments_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, Connection> $connections
* @property-read int|null $connections_count
* @property-read Configuration|null $crm
* @property-read \Illuminate\Database\Eloquent\Collection<int, FieldData> $data
* @property-read int|null $data_count
* @property-read \Jiminny\Models\Device|null $device
* @property-read \Kalnoy\Nestedset\Collection<int, \Jiminny\Models\Playlist> $favoritePlaylists
* @property-read int|null $favorite_playlists_count
* @property-read \Jiminny\Models\Participant|null $from
* @property-read string|null $activity_title
* @property-read mixed $comment_count
* @property-read mixed $duration_for_humans
* @property-read string $duration_for_humans_short
* @property-read int $favorite_count
* @property-read mixed $favorites_count
* @property-read mixed $formatted_value
* @property-read string $id_string
* @property-read \Jiminny\Models\Participant|null $organizer
* @property-read mixed $play_count
* @property-read int|null $plays_count
* @property-read ?ProspectInterface $prospect
* @property-read string|null $prospect_name
* @property-read mixed $prospect_type
* @property-read mixed $share_count
* @property-read int|null $shares_count
* @property-read int|null $tracks_with_telephony_count
* @property-read int|null $visible_comments_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\CoachingFeedback> $latestCoachingFeedbacks
* @property-read int|null $latest_coaching_feedbacks_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, Log> $logs
* @property-read int|null $logs_count
* @property-read \Jiminny\Models\Track|null $masterTrack
* @property-read \Illuminate\Database\Eloquent\Collection<int, Message> $messages
* @property-read int|null $messages_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, Moment> $moments
* @property-read int|null $moments_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, Note> $notes
* @property-read int|null $notes_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Participant\Share> $participantShares
* @property-read int|null $participant_shares_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, ParticipantSpeech> $participantSpeeches
* @property-read int|null $participant_speeches_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Participant\ParticipantStats> $participantStats
* @property-read int|null $participant_stats_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Participant> $participants
* @property-read int|null $participants_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, PlaylistActivity> $playlistActivities
* @property-read int|null $playlist_activities_count
* @property-read \Kalnoy\Nestedset\Collection<int, \Jiminny\Models\Playlist> $playlists
* @property-read int|null $playlists_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, Play> $plays
* @property-read \Illuminate\Database\Eloquent\Collection<int, Question> $questions
* @property-read int|null $questions_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Session> $sessions
* @property-read int|null $sessions_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, Share> $shares
* @property-read \Illuminate\Database\Eloquent\Collection<int, Snapshot> $snapshots
* @property-read int|null $snapshots_count
* @property-read Stats|null $stats
* @property-read \Jiminny\Models\Participant|null $to
* @property-read \Illuminate\Database\Eloquent\Collection<int, TopicTrigger> $topicTriggers
* @property-read int|null $topic_triggers_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Track> $tracks
* @property-read int|null $tracks_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Track> $tracksWithTelephony
* @property-read Transcription|null $transcription
* @property-read \Jiminny\Models\User $user
* @property-read \Illuminate\Database\Eloquent\Collection<int, Comment> $visibleComments
*
* @method static \Illuminate\Database\Eloquent\Collection<int, static> all($columns = ['*'])
* @method static \Jiminny\Component\Eloquent\Builder|Activity chunkByIdDesc($count, callable $callback, $column = null, $alias = null)
* @method static \Database\Factories\ActivityFactory factory(...$parameters)
* @method static \Illuminate\Database\Eloquent\Collection<int, static> get($columns = ['*'])
* @method static \Jiminny\Component\Eloquent\Builder|Activity heldBetween(\Carbon\Carbon $start, \Carbon\Carbon $end)
* @method static \Jiminny\Component\Eloquent\Builder|Activity idOrUuId($idOrUuid, bool $first = true)
* @method static \Jiminny\Component\Eloquent\Builder|Activity newModelQuery()
* @method static \Jiminny\Component\Eloquent\Builder|Activity newQuery()
* @method static Builder|Activity onlyTrashed()
* @method static \Jiminny\Component\Eloquent\Builder|Activity query()
* @method static \Jiminny\Component\Eloquent\Builder|Activity scheduledBetween(\Carbon\Carbon $start, \Carbon\Carbon $end)
* @method static \Jiminny\Component\Eloquent\Builder|Activity inOpenDeals()
* @method static \Jiminny\Component\Eloquent\Builder|Activity notInOpenDeals()
* @method static \Jiminny\Component\Eloquent\Builder|Activity forTeam(int $teamId)
* @method static \Jiminny\Component\Eloquent\Builder|Activity search(callable $searchQuery, $key = null, $sortByResults = true)
* @method static \Jiminny\Component\Eloquent\Builder|Activity uuid(string $uuid, bool $first = true)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereAccountId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereActualEndTime($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereActualStartTime($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereAverageScore($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereCalendarEventId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereContactId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereCreatedAt($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereCrmConfigurationId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereCrmProviderId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereDeletedAt($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereDescription($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereDeviceId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereDuration($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereFromParticipantId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereHasRecordingPrompt($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereIsInstantInvite($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereIsInternal($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereIsLocked($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereIsPrivate($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereIsProcessed($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereIsRecording($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereLanguage($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereLeadId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereLocation($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereLogReminderSentAt($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereOnAir($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereOpportunityId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereOrganizerNotifiedAt($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity wherePlaybookCategoryId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity wherePosterPath($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereProvider($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereRecordingPreference($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereRecordingReasonCode($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereRecordingState($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereScheduledEndTime($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereScheduledStartTime($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereSource($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereExternalId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereStageId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereStatus($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereSummary($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereSummaryReminderSent($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereTelephonyProviderId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereTitle($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereToParticipantId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereTranscriptionId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereType($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereUpdatedAt($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereUploadedBy($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereUserId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereUuid($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereValue($value)
* @method static Builder|Activity withTrashed()
* @method static Builder|Activity withoutTrashed()
*
* @mixin \Eloquent
*/
class Activity extends Model implements
ElasticSearch\Contract\Searchable,
Workflow\Workflow\WorkflowAwareInterface,
Models\Contracts\ActivityContract,
Contracts\Model\ActivityInterface,
UuidAwareInterface
{
use HasFactory;
use Enums;
use SoftDeletes;
use RequiresUUID;
use BitwiseFlagTrait;
use ElasticSearch\Model\Searchable;
use ActivityElasticSearchTrait;
use Workflow\Workflow\WorkflowAware {
transitionTo as traitTransitionTo;
}
public const int FLAG_RECORDING_REASON_DEFAULT = 0;
// Recording Prompted but never started
public const int FLAG_RECORDING_REASON_COMPLIANCE_PROMPT = 1;
public const int FLAG_RECORDING_REASON_COMPLIANCE_RESUMED = 2;
public const int FLAG_RECORDING_REASON_NO_AUDIO = 3;
// Recording Disabled by Organization
public const int FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT = 4;
// Recording was restricted to one-side recordings only
public const int FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT_ONE_SIDE = 8;
// Recording was not started because it was internal and team setting disabled that.
public const int FLAG_RECORDING_REASON_TEAM_INTERNAL_DISABLED = 16;
// Recording was not started because it was internal and user setting disabled that.
public const int FLAG_RECORDING_REASON_USER_INTERNAL_DISABLED = 32;
// Recording was not started because user setting disabled automatic recording.
public const int FLAG_RECORDING_REASON_USER_AUTOMATIC_DISABLED = 64;
// Recording was not started because team setting disabled automatic recording.
public const int FLAG_RECORDING_REASON_TEAM_AUTOMATIC_DISABLED = 128;
// Recording was not started because user has overriden default.
public const int FLAG_RECORDING_REASON_PREFERENCE_OVERRIDE = 256;
// Recording was not started because they don't want internal, and this meeting was not scheduled/imported in time.
public const int FLAG_RECORDING_REASON_USER_INTERNAL_DISABLED_UNSCHEDULED = 512;
// Recording was not started because their team setting does excludes the meeting type.
public const int FLAG_RECORDING_REASON_UNSUPPORTED_TYPE = 1024;
// Recording was not started because the external provider disabled it (or recording is missing etc).
public const int FLAG_RECORDING_REASON_EXTERNALLY_DISABLED = 2048;
// Recording was stopped externally ("exit-meeting" Pusher event)
public const int FLAG_RECORDING_REASON_STOPPED_EXTERNALLY = 384;
// Recording couldn't be started due to Zoom hosting conflict error
public const int FLAG_RECORDING_REASON_HOSTING_CONFLICT = 448;
// meeting.failed event with reason code BOT_DENIED_FROM_LOBBY
public const int FLAG_RECORDING_REASON_MEETING_BOT_DENIED_FROM_LOBBY = 4096;
// meeting.failed event with reason code LOBBY_TIMEOUT
public const int FLAG_RECORDING_REASON_MEETING_BOT_LOBBY_TIMEOUT = 8192;
// meeting.failed event with reason code BOT_KICKED
public const int FLAG_RECORDING_REASON_MEETING_BOT_KICKED = 16384;
// meeting.failed event with reason code UNKNOWN
public const int FLAG_RECORDING_REASON_MEETING_BOT_UNKNOWN = 32768;
public const int FLAG_RECORDING_REASON_CONSENT_DENIED = 65536;
// Invalid meeting (e.g. URL is invalid, or the meeting is not found)
public const int FLAG_RECORDING_REASON_MEETING_BOT_INVALID = 131072;
// The host stopped the recording.
public const int FLAG_RECORDING_REASON_USER_STOPPED = 262144;
// Recording was not started because an alternative vendor disabled it (or overrode it).
public const int FLAG_RECORDING_REASON_VENDOR_OVERRIDE = 1048576;
// Login required meeting.failed code
public const int FLAG_RECORDING_REASON_LOGIN_REQUIRED = 524288;
// Password for meeting was not provided - meeting.failed code
public const int FLAG_RECORDING_REASON_MEETING_PASSWORD_NOT_PROVIDED = 2097152;
// meeting.failed - when the meeting is locked
public const int FLAG_RECORDING_REASON_MEETING_IS_LOCKED = 4194304;
// max recording duration reached
public const int FLAG_RECORDING_REASON_MAX_DURATION_REACHED = 8388608;
// recording size is too small
public const int FLAG_RECORDING_REASON_EMPTY_RECORDING = 16777216;
// meeting.failed - when bot is redirected to sign in page multiple times
public const int FLAG_RECORDING_REASON_MAX_RESTART_COUNT_IS_REACHED = 33554432;
// meeting.failed event with reason code CONNECTION_LOST
public const int FLAG_RECORDING_REASON_MEETING_BOT_CONNECTION_LOST = 67108864;
// recording is corrupted.
public const int FLAG_RECORDING_REASON_MEDIA_FILE_UNSUPPORTED_MIME_TYPE = 134217728;
// meeting ended in lobby
public const int FLAG_RECORDING_REASON_MEETING_ENDED_IN_LOBBY = 268435456;
// meeting not started
public const int FLAG_RECORDING_REASON_REASON_MEETING_NOT_STARTED = 536870912;
// unfinished zoom custom disclaimer
public const int FLAG_RECORDING_REASON_FEATURE_RULE_NOT_FOUND_ERROR = 1073741824;
// recording download failed - server error
public const int FLAG_RECORDING_REASON_SERVER_ERROR = 2147483648;
// recording download failed - client code 404
public const int FLAG_RECORDING_REASON_NOT_FOUND = 2147483649;
// recording download failed - client code 401, 403
public const int FLAG_RECORDING_REASON_ACCESS_DENIED = 2147483650;
// recording download failed - client code 429
public const int FLAG_RECORDING_REASON_TOO_MANY_REQUESTS = 2147483651;
// recording download failed - unknown client error
public const int FLAG_RECORDING_REASON_CLIENT_ERROR = 2147483652;
// recording download failed - unknown error
public const int FLAG_RECORDING_REASON_UNKNOWN_ERROR = 2147483653;
// It has been setup ahead of time through calendar
public const string STATUS_SCHEDULED = 'scheduled';
// It is awaiting audio.
public const string STATUS_PENDING = 'pending';
// Participant(s) dialed in, awaiting organizer.
public const string STATUS_RINGING = 'ringing';
// Call is in progress.
public const string STATUS_IN_PROGRESS = 'in-progress';
// It has ended.
public const string STATUS_COMPLETED = 'completed';
// Cancelled prior to starting.
public const string STATUS_CANCELLED = 'canceled';
public const string STATUS_DUPLICATED = 'duplicated'; // duplicated conference
public const string STATUS_STARTING_SOON = 'starting-soon';
public const string STATUS_BOT_CREATE_SENT = 'bot-create-sent';
public const string STATUS_BOT_INSTANCE_WORKER_ASSIGNED = 'worker-assigned';
public const string STATUS_BOT_INSTANCE_STARTED = 'bot-started';
// When bot instance is waiting in lobby
public const string STATUS_BOT_INSTANCE_WAITING_LOBBY = 'bot-waiting';
public const string STATUS_BUSY = 'busy';
public const string STATUS_NO_ANSWER = 'no-answer';
public const string STATUS_FAILED = 'failed'; // Used by SMS too
// SMS related
public const string STATUS_ACCEPTED = 'accepted';
public const string STATUS_QUEUED = 'queued';
public const string STATUS_SENDING = 'sending';
public const string STATUS_SENT = 'sent';
public const string STATUS_DELIVERED = 'delivered';
public const string STATUS_UNDELIVERED = 'undelivered';
public const string STATUS_RECEIVING = 'receiving';
public const string STATUS_RECEIVED = 'received';
public const string STATUS_RESENT = 'resent';
public const array SMS_STATUSES = [
Activity::STATUS_RECEIVED,
Activity::STATUS_SENT,
Activity::STATUS_DELIVERED,
];
public const array SOFT_PHONE_CONFERENCE_STATUSES = [
Activity::STATUS_IN_PROGRESS,
Activity::STATUS_COMPLETED,
];
// @todo refactor prefix from `TYPE_` to `CHANNEL_`
public const string TYPE_SOFTPHONE = 'softphone';
public const string TYPE_SOFTPHONE_INBOUND = 'softphone-inbound';
public const string TYPE_CONFERENCE = 'conference';
public const string TYPE_SMS_INBOUND = 'sms-inbound';
public const string TYPE_SMS_OUTBOUND = 'sms-outbound';
public const string TYPE_EMAIL_INBOUND = 'email-inbound';
public const string TYPE_EMAIL_OUTBOUND = 'email-outbound';
public const array CHANNELS = [
self::TYPE_SOFTPHONE,
self::TYPE_SOFTPHONE_INBOUND,
self::TYPE_CONFERENCE,
self::TYPE_SMS_INBOUND,
self::TYPE_SMS_OUTBOUND,
self::TYPE_EMAIL_INBOUND,
self::TYPE_EMAIL_OUTBOUND,
];
public const array PLAYABLE_CHANNELS = [
self::TYPE_SOFTPHONE,
self::TYPE_SOFTPHONE_INBOUND,
self::TYPE_CONFERENCE,
];
// Recording States
public const string RECORDING_OFF = 'off'; // Default state
public const string RECORDING_IN_PROGRESS = 'in-progress';
public const string RECORDING_PAUSED = 'paused';
public const string RECORDING_STOPPED = 'stopped'; // To never be resumed.
public const string RECORDING_RECORDED = 'recorded'; // At least some portion of it was recorded.
public const string RECORDING_FAILED = 'failed'; // Recording was attempted but failed for some reason.
// Live Stream States
public const int ON_AIR_DEFAULT = 0;
public const int ON_AIR_READY = 1;
public const int ON_AIR_PREPARING = 2;
public const int ON_AIR_STREAMING = 3;
public const int ON_AIR_FINISHED = 4;
public const int ON_AIR_NOT_STREAMED = 5;
public const int ON_AIR_ERROR = -1;
public const string SOURCE_GONG = 'gong';
public const string SOURCE_CHORUS = 'chorus';
public const string SOURCE_OUTLOOK = 'outlook';
public const string SOURCE_GOOGLE = 'google';
// Activity Providers
public const string PROVIDER_TWILIO = 'twilio'; // XXX: This is run via the Jiminny Provider.
public const string PROVIDER_OUTREACH = 'outreach';
public const string PROVIDER_ZOOM_BOT = 'zoom-bot';
public const string PROVIDER_SALESLOFT = 'salesloft';
public const string PROVIDER_GOOGLE = 'google';
public const string PROVIDER_AIRCALL = 'aircall';
public const string PROVIDER_JUSTCALL = 'justcall';
public const string PROVIDER_GOOGLE_MEET = 'google-meet';
public const string PROVIDER_GONG = 'gong';
public const string PROVIDER_HUBSPOT = 'hubspot';
public const string PROVIDER_CLOSE = 'close';
public const string PROVIDER_TEAMS = 'ms-teams';
public const string PROVIDER_SALESFORCE = 'salesforce';
public const string PROVIDER_GROOVE = 'groove';
public const string PROVIDER_XANT = 'xant';
public const string PROVIDER_OFFICE = 'office';
public const string PROVIDER_NATTERBOX = 'natterbox';
public const string PROVIDER_RINGCENTRAL = 'ringcentral';
public const string PROVIDER_RINGCENTRAL_VIDEO = 'ringcentral-video';
public const string PROVIDER_GOTOMEETING = 'go-to-meeting';
public const string PROVIDER_DEMODESK = 'demo-desk';
public const string PROVIDER_DIALPAD = 'dialpad';
public const string PROVIDER_ZOOM_PHONE = 'zoom-phone';
public const string PROVIDER_CLOUDCALL = 'cloudcall';
public const string PROVIDER_CLOUDCALL_US = 'cloudcall-us';
public const string PROVIDER_EIGHT_BY_EIGHT = 'eight-by-eight'; // "8x8" UK
public const string PROVIDER_EIGHT_BY_EIGHT_CA = 'eight-by-eight-ca'; // "8x8" Canada
public const string PROVIDER_EIGHT_BY_EIGHT_AP = 'eight-by-eight-ap'; // "8x8" Australia
public const string PROVIDER_EIGHT_BY_EIGHT_US_EAST = 'eight-by-eight-use'; // "8x8" US East
public const string PROVIDER_EIGHT_BY_EIGHT_US_WEST = 'eight-by-eight-usw'; // "8x8" US West
public const string PROVIDER_CONNECT_AND_SELL = 'connect-and-sell';
public const string PROVIDER_CLOUD_TALK = 'cloud-talk';
public const string PROVIDER_AMAZON_CONNECT = 'amazon-connect';
public const string PROVIDER_VONAGE = 'vonage';
public const string PROVIDER_MIGRATOR = 'migrator';
public const string PROVIDER_UPLOADER = 'uploader';
public const string PROVIDER_TALKDESK = 'talkdesk';
public const string PROVIDER_TWILIO_FLEX = 'twilio-flex';
public const string PROVIDER_TWILIO_FLEX_DIRECT = 'twilio-flex-direct';
public const string PROVIDER_TWILIO_VIDEO = 'twilio-video';
public const string PROVIDER_AVAYA = 'avaya';
public const string PROVIDER_TELUS = 'telus';
public const string PROVIDER_FIVE_NINE = 'five-nine';
public const string PROVIDER_APOLLO = 'apollo';
public const string PROVIDER_ORUM = 'orum';
public const string PROVIDER_BLOOBIRDS = 'bloobirds';
/**
* @const API_PROVIDERS
* A list of integrations that import calls via API instead of webhooks
*/
public const array API_PROVIDERS = [
self::PROVIDER_OUTREACH,
self::PROVIDER_SALESLOFT,
self::PROVIDER_HUBSPOT,
self::PROVIDER_GROOVE,
self::PROVIDER_XANT,
self::PROVIDER_NATTERBOX,
self::PROVIDER_CLOUDCALL,
self::PROVIDER_CLOUDCALL_US,
self::PROVIDER_EIGHT_BY_EIGHT,
self::PROVIDER_EIGHT_BY_EIGHT_CA,
self::PROVIDER_EIGHT_BY_EIGHT_AP,
self::PROVIDER_EIGHT_BY_EIGHT_US_EAST,
self::PROVIDER_EIGHT_BY_EIGHT_US_WEST,
self::PROVIDER_CONNECT_AND_SELL,
self::PROVIDER_CLOUD_TALK,
self::PROVIDER_AMAZON_CONNECT,
self::PROVIDER_VONAGE,
self::PROVIDER_TALKDESK,
self::PROVIDER_TWILIO_VIDEO,
self::PROVIDER_TWILIO_FLEX,
self::PROVIDER_TWILIO_FLEX_DIRECT,
self::PROVIDER_FIVE_NINE,
self::PROVIDER_APOLLO,
self::PROVIDER_ORUM,
self::PROVIDER_BLOOBIRDS,
self::PROVIDER_RINGCENTRAL,
self::PROVIDER_AVAYA,
self::PROVIDER_TELUS,
];
public const array FINITE_STATES = [
self::TYPE_SOFTPHONE => [
self::STATUS_COMPLETED,
self::STATUS_FAILED,
self::STATUS_NO_ANSWER,
self::STATUS_BUSY,
],
self::TYPE_SOFTPHONE_INBOUND => [
self::STATUS_COMPLETED,
self::STATUS_FAILED,
self::STATUS_NO_ANSWER,
self::STATUS_BUSY,
],
self::TYPE_CONFERENCE => self::FINITE_STATES_CONFERENCE,
];
public const array FINITE_STATES_CONFERENCE = [
self::STATUS_COMPLETED,
self::STATUS_FAILED,
self::STATUS_CANCELLED,
];
public const array MEETING_BOT_JOIN_ATTEMPTED = [
self::STATUS_BOT_INSTANCE_WAITING_LOBBY,
self::STATUS_BOT_INSTANCE_STARTED,
];
public static array $enumStatuses = [
self::STATUS_SCHEDULED,
self::STATUS_PENDING,
self::STATUS_RINGING,
self::STATUS_IN_PROGRESS,
self::STATUS_COMPLETED,
self::STATUS_CANCELLED,
self::STATUS_BUSY,
self::STATUS_NO_ANSWER,
self::STATUS_FAILED,
self::STATUS_ACCEPTED,
self::STATUS_QUEUED,
self::STATUS_SENDING,
self::STATUS_SENT,
self::STATUS_RESENT,
self::STATUS_DELIVERED,
self::STATUS_UNDELIVERED,
self::STATUS_RECEIVING,
self::STATUS_RECEIVED,
self::STATUS_BOT_INSTANCE_WAITING_LOBBY,
self::STATUS_STARTING_SOON,
self::STATUS_BOT_INSTANCE_WORKER_ASSIGNED,
self::STATUS_BOT_INSTANCE_STARTED,
self::STATUS_DUPLICATED,
];
public static array $enumProviders = [
self::PROVIDER_TWILIO,
self::PROVIDER_OUTREACH,
self::PROVIDER_ZOOM_BOT,
self::PROVIDER_SALESLOFT,
self::PROVIDER_AIRCALL,
self::PROVIDER_JUSTCALL,
self::PROVIDER_GOOGLE_MEET,
self::PROVIDER_GONG,
self::PROVIDER_HUBSPOT,
self::PROVIDER_CLOSE,
self::PROVIDER_TEAMS,
self::PROVIDER_SALESFORCE,
self::PROVIDER_GROOVE,
self::PROVIDER_XANT,
self::PROVIDER_GOOGLE,
self::PROVIDER_OFFICE,
self::PROVIDER_NATTERBOX,
self::PROVIDER_RINGCENTRAL,
self::PROVIDER_RINGCENTRAL_VIDEO,
self::PROVIDER_GOTOMEETING,
self::PROVIDER_DEMODESK,
self::PROVIDER_DIALPAD,
self::PROVIDER_ZOOM_PHONE,
self::PROVIDER_CLOUDCALL,
self::PROVIDER_CLOUDCALL_US,
self::PROVIDER_EIGHT_BY_EIGHT,
self::PROVIDER_EIGHT_BY_EIGHT_CA,
self::PROVIDER_EIGHT_BY_EIGHT_AP,
self::PROVIDER_EIGHT_BY_EIGHT_US_EAST,
self::PROVIDER_EIGHT_BY_EIGHT_US_WEST,
self::PROVIDER_CONNECT_AND_SELL,
self::PROVIDER_CLOUD_TALK,
self::PROVIDER_AMAZON_CONNECT,
self::PROVIDER_VONAGE,
self::PROVIDER_TALKDESK,
self::PROVIDER_TWILIO_FLEX,
self::PROVIDER_TWILIO_FLEX_DIRECT,
self::PROVIDER_TWILIO_VIDEO,
self::PROVIDER_AVAYA,
self::PROVIDER_TELUS,
self::PROVIDER_FIVE_NINE,
self::PROVIDER_APOLLO,
self::PROVIDER_ORUM,
self::PROVIDER_BLOOBIRDS,
];
public static $enumRecordingStates = [
self::RECORDING_OFF, // Default state
self::RECORDING_IN_PROGRESS,
self::RECORDING_PAUSED,
self::RECORDING_STOPPED,
self::RECORDING_RECORDED,
self::RECORDING_FAILED,
];
// @Important:
// This collection is not used anywhere, and is fully duplicated by the Channels const.
// Validate if it is referred somehow via the enum trait, and if not, remove it entirely.
// An even better strategy will be to move all those constants to a dedicated class
protected array $enumTypes = [
self::TYPE_SOFTPHONE,
self::TYPE_SOFTPHONE_INBOUND,
self::TYPE_CONFERENCE,
self::TYPE_SMS_INBOUND,
self::TYPE_SMS_OUTBOUND,
self::TYPE_EMAIL_INBOUND,
self::TYPE_EMAIL_OUTBOUND,
];
protected static $enumFailedStatuses = [
self::STATUS_NO_ANSWER,
self::STATUS_FAILED,
self::STATUS_BUSY,
self::STATUS_CANCELLED,
];
protected $table = 'activities';
protected $fillable = [
// Type of activity.
'type', // @todo refactor to `channel`
// The activity type.
'playbook_category_id',
// User who hosts the activity.
'user_id',
// Related Lead record (if applicable)
'lead_id',
// Related Account record (if applicable)
'account_id',
// Related Contact record (if applicable)
'contact_id',
// Related Opportunity record (if applicable)
'opportunity_id',
// Stage of activity.
'stage_id',
// Value of opportunity.
'value',
// If the activity relates to a CRM task.
'crm_provider_id',
// If the activity was created through an external device.
'device_id',
// the activity's language code
'language',
// transcription id
'transcription_id',
// Duration of the call, with microseconds precision.
'duration',
// One of enumStatuses above.
'status',
// Have we reminded them to log the call?
'log_reminder_sent_at',
// If activity is private or inter-org, flagged here.
'is_internal',
// Managers and above can mark a call as private, to exclude it from other team members
'is_private',
'is_processed',
// Boolean for this activity being instant invite handled.
'is_instant_invite',
// If activity is in recording state, flagged here.
'recording_state',
// If activity recording is overidden from default.
'recording_preference',
// if recording did (not) happen, why that is
'recording_reason_code',
// Average score, updated during
'average_score',
// Summary that the organizer has taken after the call.
'summary',
// Subject of the activity, usually taken from calendar event.
'title',
// Description of the activity, usually taken from calendar event.
'description',
// Start time, usually taken from calendar event.
'scheduled_start_time',
// End time, usually taken from calendar event.
'scheduled_end_time',
// When the call actually started.
'actual_start_time',
// When the call actually ended.
'actual_end_time',
// SMS: Message reference
'telephony_provider_id',
// SMS: Participant who sent message
'from_participant_id',
// SMS: Participant who should receive the message
'to_participant_id',
// When an external guest joins an organizers meeting room and the organizer is not present,
// send them an SMS notification that someone has joined.
'organizer_notified_at',
// where was the activity imported from
'source',
// The id in the source system (e.g. the bot id in Recall.ai)
'external_id',
// The provider, by default it is twilio.
'provider',
// Meeting location url
'location',
// The snapshot for displaying a poster image.
'poster_path',
'crm_configuration_id',
// If there is an automated message that the conversation is being recorded
'has_recording_prompt',
// If the activity is being live-streamed
'on_air',
'calendar_event_id',
];
protected $appends = [
'id_string',
'organizer',
];
protected $hidden = [
'uuid',
];
protected $visible = [
'id_string',
'type',
'duration',
'average_score',
'status',
'log_reminder_sent_at',
'title',
'description',
'is_internal',
'scheduled_start_time',
'scheduled_end_time',
'actual_start_time',
'actual_end_time',
'user',
'category',
'account',
'contact',
'opportunity',
'lead',
'stage',
'stats',
'participants',
'playlists',
'tracks',
'comments',
'plays',
'coachingFeedbacks',
'shares',
'favorites',
'language',
'transcription',
'is_private',
'is_instant_invite',
'on_air',
'calendar_event_id',
];
protected function casts(): array
{
return [
'scheduled_start_time' => 'datetime',
'scheduled_end_time' => 'datetime',
'actual_start_time' => 'datetime',
'actual_end_time' => 'datetime',
'organizer_notified_at' => 'datetime',
'log_reminder_sent_at' => 'datetime',
'is_internal' => 'boolean',
'duration' => 'integer',
'average_score' => 'decimal:2',
'is_private' => 'boolean',
'is_processed' => 'boolean',
'is_instant_invite' => 'boolean',
'value' => 'decimal:2',
'recording_preference' => 'boolean',
'recording_reason_code' => 'integer',
'has_recording_prompt' => 'boolean',
'on_air' => 'integer',
];
}
protected static function boot()
{
parent::boot();
static::updated(static function (Activity $activity) {
// If activity is about to start (pending, ringing, in-progress) or event is scheduled in less than 1 week
if (in_array($activity->status, [Activity::STATUS_PENDING, Activity::STATUS_RINGING, Activity::STATUS_IN_PROGRESS], true) ||
($activity->scheduled_start_time && (int) $activity->scheduled_start_time->diffInWeeks(new Carbon(), true) < 1)) {
if ($activity->isDirty('status')) {
event(new StatusUpdated($activity));
}
if ($activity->isDirty('stage_id')) {
event(new StageUpdated($activity));
}
if ($activity->isDirty(['lead_id', 'account_id', 'contact_id'])) {
event(new ProspectUpdated($activity));
}
if ($activity->isDirty('opportunity_id')) {
event(new ActivityUpdated($activity, 'activity.opportunity-updated', Auth::user()));
}
if ($activity->isDirty('title')) {
event(new TitleUpdated($activity));
}
}
if ($activity->isDirty('playbook_category_id')) {
event(new ActivityTypeUpdated($activity));
}
});
static::deleted(static function (Activity $activity) {
// Hard delete associated playlistActivities
$activity->playlistActivities()->delete();
});
}
public function getOrganizerAttribute(): ?Participant
{
$participant = $this->participants()->where('user_id', $this->user_id)->first();
if (! $participant instanceof Participant && $participant !== null) {
throw new RuntimeException(sprintf('$participant must be an instance of "%s" or null', Participant::class));
}
return $participant;
}
public function getFormattedValueAttribute()
{
$currencyCode = 'USD';
if ($this->opportunity) {
$currencyCode = $this->opportunity->getCurrencyCode();
}
$formatter = new CurrencyFormatter();
$formatter->setTextAttribute(NumberFormatter::CURRENCY_CODE, $currencyCode);
$formatter->setAttribute(NumberFormatter::MAX_FRACTION_DIGITS, 0);
return $formatter->format($this->value, $currencyCode);
}
public function getProspectNameAttribute(): ?string
{
$prospectName = null;
if ($this->lead_id) {
$prospectName = $this->lead->name;
} elseif ($this->contact_id) {
$prospectName = $this->contact->name;
} elseif ($this->account_id) {
$prospectName = $this->account->name;
}
return $prospectName;
}
public function getProspectName(): ?string
{
/** @var string|null */
return $this->getAttribute('prospect_name');
}
/**
* Get activity title depending on prospect or title
*/
public function getActivityTitleAttribute(): ?string
{
$activityTitle = null;
if ($this->prospect && $this->prospect->getName()) {
if ($this->account_id) {
$activityTitle = $this->account->name;
} elseif ($this->lead_id) {
$activityTitle = $this->lead->company;
} elseif ($this->contact_id) {
$activityTitle = $this->contact->account ? $this->contact->account->name : $this->contact->name;
}
} elseif ($this->title) {
$activityTitle = $this->title;
}
return $activityTitle;
}
public function wasRecentlyCreated(): bool
{
return $this->wasRecentlyCreated;
}
public function getProspectTypeAttribute()
{
$prospectType = null;
if ($this->lead_id) {
$prospectType = 'Lead';
} elseif ($this->contact_id) {
$prospectType = 'Contact';
} elseif ($this->account_id) {
$prospectType = 'Account';
}
return $prospectType;
}
/**
* Return the best match for prospect. Results are in the following order of priority:
* 1. Lead
* 2. Contact
* 3. Account
* 4. NULL
*/
public function getProspectAttribute(): ?ProspectInterface
{
if ($this->hasLead()) {
return $this->getLead();
}
if ($this->hasContact()) {
return $this->getContact();
}
if ($this->hasAccount()) {
return $this->getAccount();
}
return null;
}
public function getTitleAttribute($value): ?string
{
return \getActivityTitleAttribute(
$this->user->name,
$this->getType(),
$value,
$this->prospect->name ?? null,
$this->from->national_phone_number ?? null
);
}
public function getTitle(): ?string
{
return $this->getAttribute('title');
}
public function getSummary(): ?string
{
return $this->getAttribute('summary');
}
public function isInternal(): bool
{
return $this->getAttribute('is_internal');
}
public function getIsPrivate(): bool
{
return $this->getAttribute('is_private');
}
public function getDescription(): ?string
{
return $this->getAttribute('description');
}
public function hasTitle(): bool
{
return $this->getOriginal('title') !== null;
}
public function getPlayCountAttribute()
{
return $this->getPlaysCountAttribute();
}
public function getPlaysCountAttribute()
{
if (! isset($this->attributes['plays_count'])) {
$this->loadCount('plays');
}
return $this->attributes['plays_count'];
}
public function getCommentCountAttribute()
{
return $this->getCommentsCountAttribute();
}
public function getCommentsCountAttribute()
{
if (! isset($this->attributes['comments_count'])) {
$this->loadCount('comments');
}
return $this->attributes['comments_count'];
}
public function getVisibleCommentsCountAttribute()
{
if (! isset($this->attributes['visible_comments_count'])) {
$activityCommentsService = app(ActivityCommentService::class);
$user = Auth::user() instanceof User ? Auth::user() : null;
$this->attributes['visible_comments_count'] = $activityCommentsService
->getVisibleCommentsCount($this, $user);
}
return $this->attributes['visible_comments_count'];
}
public function getShareCountAttribute()
{
return $this->getSharesCountAttribute();
}
public function getSharesCountAttribute()
{
if (! isset($this->attributes['shares_count'])) {
$this->loadCount('shares');
}
return $this->attributes['shares_count'];
}
/**
* Get the count of favorites playlists this activity appears in
*/
public function getFavoriteCountAttribute(): int
{
return $this->getFavoritesCountAttribute();
}
public function getFavoritesCountAttribute()
{
if (! isset($this->attributes['favorites_count'])) {
$this->loadCount('favorites');
}
return $this->attributes['favorites_count'];
}
public function getActiveParticipantsCountAttribute()
{
if (! isset($this->attributes['active_participants_count'])) {
$this->loadCount('activeParticipants');
}
return $this->attributes['active_participants_count'];
}
public function getTracksWithTelephonyCountAttribute()
{
if (! isset($this->attributes['tracks_with_telephony_count'])) {
$this->loadCount('tracksWithTelephony');
}
return $this->attributes['tracks_with_telephony_count'];
}
/**
* @TEMP
* $this->loadCount('tracksWithTelephony') throws null pointer exception
*/
public function countTracksWithTelephony(): int
{
return $this->tracks()->whereNotNull('telephony_provider_id')->count();
}
public function getDuration(): float
{
return $this->getAttribute('duration');
}
public function getDurationForHumansAttribute()
{
return Carbon::now()->subSeconds($this->duration)->diffForHumans(now(), true);
}
public function getDurationForHumansShortAttribute(): string
{
return Carbon::now()->subSeconds($this->duration)->diffForHumans(now(), true, true);
}
public function hasRecordingPreference(): bool
{
re...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"#12121 on JY-20963-fix-import-on-deleted-entity, menu","depth":5,"on_screen":true,"help_text":"Pull request #12121 exists for current branch JY-20963-fix-import-on-deleted-entity, but pull request details loading failed","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"ServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'ServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'ServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"4","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"348","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"9","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"103","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"4","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\nnamespace Jiminny\\Models;\n\nuse Carbon\\Carbon;\nuse Database\\Factories\\ActivityFactory;\nuse DateTimeInterface;\nuse Exception;\nuse Illuminate\\Contracts\\Auth\\Authenticatable;\nuse Illuminate\\Database\\Eloquent;\nuse Illuminate\\Database\\Eloquent\\Attributes\\Scope;\nuse Illuminate\\Database\\Eloquent\\Builder;\nuse Illuminate\\Database\\Eloquent\\Factories\\Factory;\nuse Illuminate\\Database\\Eloquent\\Factories\\HasFactory;\nuse Illuminate\\Database\\Eloquent\\Relations\\BelongsTo;\nuse Illuminate\\Database\\Eloquent\\Relations\\BelongsToMany;\nuse Illuminate\\Database\\Eloquent\\Relations\\HasMany;\nuse Illuminate\\Database\\Eloquent\\Relations\\HasManyThrough;\nuse Illuminate\\Database\\Eloquent\\Relations\\HasOne;\nuse Illuminate\\Database\\Eloquent\\SoftDeletes;\nuse Illuminate\\Support\\Collection;\nuse Illuminate\\Support\\Facades\\Auth;\nuse InvalidArgumentException;\nuse Jiminny\\Component\\ElasticSearch;\nuse Jiminny\\Component\\MeetingBot;\nuse Jiminny\\Component\\Model\\BitwiseFlagTrait;\nuse Jiminny\\Component\\PlaybackPage\\Comments\\Services\\ActivityCommentService;\nuse Jiminny\\Component\\Sidekick\\SidekickService;\nuse Jiminny\\Component\\Uuid\\UuidAwareInterface;\nuse Jiminny\\Component\\Workflow;\nuse Jiminny\\Contracts;\nuse Jiminny\\Contracts\\Crm\\ProspectInterface;\nuse Jiminny\\DTO\\ImportCall\\Call;\nuse Jiminny\\Events\\Activities\\ActivityTypeUpdated;\nuse Jiminny\\Events\\Activities\\ActivityUpdated;\nuse Jiminny\\Events\\Activities\\ProspectUpdated;\nuse Jiminny\\Events\\Activities\\StageUpdated;\nuse Jiminny\\Events\\Activities\\StatusUpdated;\nuse Jiminny\\Events\\Activities\\TitleUpdated;\nuse Jiminny\\Exceptions\\InvalidArgumentException as InvalidArgumentJiminnyException;\nuse Jiminny\\Exceptions\\LogicException;\nuse Jiminny\\Exceptions\\RuntimeException;\nuse Jiminny\\Models;\nuse Jiminny\\Models\\Activity\\ActivitySummaryLog;\nuse Jiminny\\Models\\Activity\\ActivityUploadSetting;\nuse Jiminny\\Models\\Activity\\AvailabilityNotification;\nuse Jiminny\\Models\\Activity\\CoachRequest;\nuse Jiminny\\Models\\Activity\\Comment;\nuse Jiminny\\Models\\Activity\\Log;\nuse Jiminny\\Models\\Activity\\Message;\nuse Jiminny\\Models\\Activity\\Moment;\nuse Jiminny\\Models\\Activity\\Note;\nuse Jiminny\\Models\\Activity\\ParticipantSpeech;\nuse Jiminny\\Models\\Activity\\Play;\nuse Jiminny\\Models\\Activity\\Question;\nuse Jiminny\\Models\\Activity\\Share;\nuse Jiminny\\Models\\Activity\\Snapshot;\nuse Jiminny\\Models\\Activity\\Stats;\nuse Jiminny\\Models\\Activity\\SubscriptionSet;\nuse Jiminny\\Models\\Activity\\TopicTrigger;\nuse Jiminny\\Models\\Activity\\Transcription;\nuse Jiminny\\Models\\Calendar\\CalendarEvent;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Models\\Crm\\FieldData;\nuse Jiminny\\Models\\ElasticSearch\\ActivityElasticSearchTrait;\nuse Jiminny\\Models\\Feature\\FeatureEnum;\nuse Jiminny\\Models\\Participant\\Connection;\nuse Jiminny\\Models\\Playlist\\Activity as PlaylistActivity;\nuse Jiminny\\Services\\Activity\\ActivityProviderRegistry;\nuse Jiminny\\Services\\Activity\\Import\\DataResolvers\\UpdateCrmDataByStrategy;\nuse Jiminny\\Services\\Activity\\Import\\DataResolvers\\UpdateCrmDataResolverFactory;\nuse Jiminny\\Services\\Activity\\Import\\DataResolvers\\UpdateCrmDataResolverInterface;\nuse Jiminny\\Traits\\Enums;\nuse Jiminny\\Traits\\RequiresUUID;\nuse Jiminny\\Utils\\CurrencyFormatter;\nuse NumberFormatter;\n\nuse function in_array;\n\n/**\n * Jiminny\\Models\\Activity\n *\n * @property null|int $auto_score filled from ES hydrator, not in DB!\n * @property-read Account|null $account\n * @property-read CalendarEvent|null $calendarEvent\n * @property-read Contact|null $contact\n * @property-read Lead|null $lead\n * @property-read Opportunity|null $opportunity\n * @property-read Stage|null $stage\n * @property int $id\n * @property mixed|null $uuid\n * @property string|null $source\n * @property string|null $external_id\n * @property string $provider\n * @property string|null $location\n * @property string|null $telephony_provider_id\n * @property int|null $from_participant_id\n * @property int|null $to_participant_id\n * @property int|null $device_id\n * @property string|null $type\n * @property int|null $playbook_category_id\n * @property int $user_id\n * @property int|null $lead_id\n * @property int|null $account_id\n * @property int|null $contact_id\n * @property int|null $opportunity_id\n * @property int|null $stage_id\n * @property string|null $value\n * @property int|null $crm_configuration_id\n * @property string|null $crm_provider_id\n * @property string|null $language\n * @property int|null $transcription_id\n * @property int $duration\n * @property string $status\n * @property int|null $on_air\n * @property int|null $calendar_event_id\n * @property string $recording_state\n * @property bool|null $recording_preference\n * @property int $recording_reason_code\n * @property int $summary_reminder_sent\n * @property \\Illuminate\\Support\\Carbon|null $log_reminder_sent_at\n * @property \\Illuminate\\Support\\Carbon|null $organizer_notified_at\n * @property bool|null $has_recording_prompt\n * @property bool $is_internal\n * @property int $is_locked\n * @property int $is_recording\n * @property bool|null $is_processed\n * @property bool $is_private\n * @property bool $is_instant_invite\n * @property string|null $poster_path\n * @property string|null $summary\n * @property string|null $title\n * @property string|null $description\n * @property \\Illuminate\\Support\\Carbon|null $scheduled_start_time\n * @property \\Illuminate\\Support\\Carbon|null $scheduled_end_time\n * @property \\Illuminate\\Support\\Carbon|null $actual_start_time\n * @property \\Illuminate\\Support\\Carbon|null $actual_end_time\n * @property int|null $uploaded_by\n * @property \\Illuminate\\Support\\Carbon|null $deleted_at\n * @property \\Illuminate\\Support\\Carbon|null $created_at\n * @property \\Illuminate\\Support\\Carbon|null $updated_at\n * @property string|null $average_score\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Participant> $activeParticipants\n * @property-read int|null $active_participants_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Scorecard\\ActivityScorecardRuleTrigger> $activityScorecardRuleTriggers\n * @property-read int|null $activity_scorecard_rule_triggers_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Scorecard\\ActivityScorecardRule> $activityScorecardRules\n * @property-read int|null $activity_scorecard_rules_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, AvailabilityNotification> $availabilityNotifications\n * @property-read int|null $availability_notifications_count\n * @property-read \\Jiminny\\Models\\PlaybookCategory|null $category\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, CoachRequest> $coachRequests\n * @property-read int|null $coach_requests_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\CoachingFeedback> $coachingFeedbacks\n * @property-read int|null $coaching_feedbacks_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Message> $coachingMessages\n * @property-read int|null $coaching_messages_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Comment> $comments\n * @property-read int|null $comments_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Connection> $connections\n * @property-read int|null $connections_count\n * @property-read Configuration|null $crm\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, FieldData> $data\n * @property-read int|null $data_count\n * @property-read \\Jiminny\\Models\\Device|null $device\n * @property-read \\Kalnoy\\Nestedset\\Collection<int, \\Jiminny\\Models\\Playlist> $favoritePlaylists\n * @property-read int|null $favorite_playlists_count\n * @property-read \\Jiminny\\Models\\Participant|null $from\n * @property-read string|null $activity_title\n * @property-read mixed $comment_count\n * @property-read mixed $duration_for_humans\n * @property-read string $duration_for_humans_short\n * @property-read int $favorite_count\n * @property-read mixed $favorites_count\n * @property-read mixed $formatted_value\n * @property-read string $id_string\n * @property-read \\Jiminny\\Models\\Participant|null $organizer\n * @property-read mixed $play_count\n * @property-read int|null $plays_count\n * @property-read ?ProspectInterface $prospect\n * @property-read string|null $prospect_name\n * @property-read mixed $prospect_type\n * @property-read mixed $share_count\n * @property-read int|null $shares_count\n * @property-read int|null $tracks_with_telephony_count\n * @property-read int|null $visible_comments_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\CoachingFeedback> $latestCoachingFeedbacks\n * @property-read int|null $latest_coaching_feedbacks_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Log> $logs\n * @property-read int|null $logs_count\n * @property-read \\Jiminny\\Models\\Track|null $masterTrack\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Message> $messages\n * @property-read int|null $messages_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Moment> $moments\n * @property-read int|null $moments_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Note> $notes\n * @property-read int|null $notes_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Participant\\Share> $participantShares\n * @property-read int|null $participant_shares_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, ParticipantSpeech> $participantSpeeches\n * @property-read int|null $participant_speeches_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Participant\\ParticipantStats> $participantStats\n * @property-read int|null $participant_stats_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Participant> $participants\n * @property-read int|null $participants_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, PlaylistActivity> $playlistActivities\n * @property-read int|null $playlist_activities_count\n * @property-read \\Kalnoy\\Nestedset\\Collection<int, \\Jiminny\\Models\\Playlist> $playlists\n * @property-read int|null $playlists_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Play> $plays\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Question> $questions\n * @property-read int|null $questions_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Session> $sessions\n * @property-read int|null $sessions_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Share> $shares\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Snapshot> $snapshots\n * @property-read int|null $snapshots_count\n * @property-read Stats|null $stats\n * @property-read \\Jiminny\\Models\\Participant|null $to\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, TopicTrigger> $topicTriggers\n * @property-read int|null $topic_triggers_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Track> $tracks\n * @property-read int|null $tracks_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Track> $tracksWithTelephony\n * @property-read Transcription|null $transcription\n * @property-read \\Jiminny\\Models\\User $user\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Comment> $visibleComments\n *\n * @method static \\Illuminate\\Database\\Eloquent\\Collection<int, static> all($columns = ['*'])\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity chunkByIdDesc($count, callable $callback, $column = null, $alias = null)\n * @method static \\Database\\Factories\\ActivityFactory factory(...$parameters)\n * @method static \\Illuminate\\Database\\Eloquent\\Collection<int, static> get($columns = ['*'])\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity heldBetween(\\Carbon\\Carbon $start, \\Carbon\\Carbon $end)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity idOrUuId($idOrUuid, bool $first = true)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity newModelQuery()\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity newQuery()\n * @method static Builder|Activity onlyTrashed()\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity query()\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity scheduledBetween(\\Carbon\\Carbon $start, \\Carbon\\Carbon $end)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity inOpenDeals()\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity notInOpenDeals()\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity forTeam(int $teamId)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity search(callable $searchQuery, $key = null, $sortByResults = true)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity uuid(string $uuid, bool $first = true)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereAccountId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereActualEndTime($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereActualStartTime($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereAverageScore($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereCalendarEventId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereContactId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereCreatedAt($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereCrmConfigurationId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereCrmProviderId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereDeletedAt($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereDescription($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereDeviceId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereDuration($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereFromParticipantId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereHasRecordingPrompt($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereIsInstantInvite($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereIsInternal($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereIsLocked($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereIsPrivate($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereIsProcessed($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereIsRecording($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereLanguage($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereLeadId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereLocation($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereLogReminderSentAt($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereOnAir($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereOpportunityId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereOrganizerNotifiedAt($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity wherePlaybookCategoryId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity wherePosterPath($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereProvider($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereRecordingPreference($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereRecordingReasonCode($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereRecordingState($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereScheduledEndTime($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereScheduledStartTime($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereSource($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereExternalId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereStageId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereStatus($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereSummary($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereSummaryReminderSent($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereTelephonyProviderId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereTitle($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereToParticipantId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereTranscriptionId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereType($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereUpdatedAt($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereUploadedBy($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereUserId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereUuid($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereValue($value)\n * @method static Builder|Activity withTrashed()\n * @method static Builder|Activity withoutTrashed()\n *\n * @mixin \\Eloquent\n */\nclass Activity extends Model implements\n ElasticSearch\\Contract\\Searchable,\n Workflow\\Workflow\\WorkflowAwareInterface,\n Models\\Contracts\\ActivityContract,\n Contracts\\Model\\ActivityInterface,\n UuidAwareInterface\n{\n use HasFactory;\n\n use Enums;\n use SoftDeletes;\n use RequiresUUID;\n use BitwiseFlagTrait;\n use ElasticSearch\\Model\\Searchable;\n use ActivityElasticSearchTrait;\n\n use Workflow\\Workflow\\WorkflowAware {\n transitionTo as traitTransitionTo;\n }\n\n public const int FLAG_RECORDING_REASON_DEFAULT = 0;\n\n // Recording Prompted but never started\n public const int FLAG_RECORDING_REASON_COMPLIANCE_PROMPT = 1;\n public const int FLAG_RECORDING_REASON_COMPLIANCE_RESUMED = 2;\n public const int FLAG_RECORDING_REASON_NO_AUDIO = 3;\n\n // Recording Disabled by Organization\n public const int FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT = 4;\n\n // Recording was restricted to one-side recordings only\n public const int FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT_ONE_SIDE = 8;\n\n // Recording was not started because it was internal and team setting disabled that.\n public const int FLAG_RECORDING_REASON_TEAM_INTERNAL_DISABLED = 16;\n\n // Recording was not started because it was internal and user setting disabled that.\n public const int FLAG_RECORDING_REASON_USER_INTERNAL_DISABLED = 32;\n\n // Recording was not started because user setting disabled automatic recording.\n public const int FLAG_RECORDING_REASON_USER_AUTOMATIC_DISABLED = 64;\n\n // Recording was not started because team setting disabled automatic recording.\n public const int FLAG_RECORDING_REASON_TEAM_AUTOMATIC_DISABLED = 128;\n\n // Recording was not started because user has overriden default.\n public const int FLAG_RECORDING_REASON_PREFERENCE_OVERRIDE = 256;\n\n // Recording was not started because they don't want internal, and this meeting was not scheduled/imported in time.\n public const int FLAG_RECORDING_REASON_USER_INTERNAL_DISABLED_UNSCHEDULED = 512;\n\n // Recording was not started because their team setting does excludes the meeting type.\n public const int FLAG_RECORDING_REASON_UNSUPPORTED_TYPE = 1024;\n\n // Recording was not started because the external provider disabled it (or recording is missing etc).\n public const int FLAG_RECORDING_REASON_EXTERNALLY_DISABLED = 2048;\n\n // Recording was stopped externally (\"exit-meeting\" Pusher event)\n public const int FLAG_RECORDING_REASON_STOPPED_EXTERNALLY = 384;\n\n // Recording couldn't be started due to Zoom hosting conflict error\n public const int FLAG_RECORDING_REASON_HOSTING_CONFLICT = 448;\n\n // meeting.failed event with reason code BOT_DENIED_FROM_LOBBY\n public const int FLAG_RECORDING_REASON_MEETING_BOT_DENIED_FROM_LOBBY = 4096;\n\n // meeting.failed event with reason code LOBBY_TIMEOUT\n public const int FLAG_RECORDING_REASON_MEETING_BOT_LOBBY_TIMEOUT = 8192;\n\n // meeting.failed event with reason code BOT_KICKED\n public const int FLAG_RECORDING_REASON_MEETING_BOT_KICKED = 16384;\n\n // meeting.failed event with reason code UNKNOWN\n public const int FLAG_RECORDING_REASON_MEETING_BOT_UNKNOWN = 32768;\n\n public const int FLAG_RECORDING_REASON_CONSENT_DENIED = 65536;\n\n // Invalid meeting (e.g. URL is invalid, or the meeting is not found)\n public const int FLAG_RECORDING_REASON_MEETING_BOT_INVALID = 131072;\n\n // The host stopped the recording.\n public const int FLAG_RECORDING_REASON_USER_STOPPED = 262144;\n\n // Recording was not started because an alternative vendor disabled it (or overrode it).\n public const int FLAG_RECORDING_REASON_VENDOR_OVERRIDE = 1048576;\n\n // Login required meeting.failed code\n public const int FLAG_RECORDING_REASON_LOGIN_REQUIRED = 524288;\n\n // Password for meeting was not provided - meeting.failed code\n public const int FLAG_RECORDING_REASON_MEETING_PASSWORD_NOT_PROVIDED = 2097152;\n\n // meeting.failed - when the meeting is locked\n public const int FLAG_RECORDING_REASON_MEETING_IS_LOCKED = 4194304;\n\n // max recording duration reached\n public const int FLAG_RECORDING_REASON_MAX_DURATION_REACHED = 8388608;\n\n // recording size is too small\n public const int FLAG_RECORDING_REASON_EMPTY_RECORDING = 16777216;\n\n // meeting.failed - when bot is redirected to sign in page multiple times\n public const int FLAG_RECORDING_REASON_MAX_RESTART_COUNT_IS_REACHED = 33554432;\n\n // meeting.failed event with reason code CONNECTION_LOST\n public const int FLAG_RECORDING_REASON_MEETING_BOT_CONNECTION_LOST = 67108864;\n\n // recording is corrupted.\n public const int FLAG_RECORDING_REASON_MEDIA_FILE_UNSUPPORTED_MIME_TYPE = 134217728;\n\n // meeting ended in lobby\n public const int FLAG_RECORDING_REASON_MEETING_ENDED_IN_LOBBY = 268435456;\n\n // meeting not started\n public const int FLAG_RECORDING_REASON_REASON_MEETING_NOT_STARTED = 536870912;\n\n // unfinished zoom custom disclaimer\n public const int FLAG_RECORDING_REASON_FEATURE_RULE_NOT_FOUND_ERROR = 1073741824;\n\n // recording download failed - server error\n public const int FLAG_RECORDING_REASON_SERVER_ERROR = 2147483648;\n\n // recording download failed - client code 404\n public const int FLAG_RECORDING_REASON_NOT_FOUND = 2147483649;\n\n // recording download failed - client code 401, 403\n public const int FLAG_RECORDING_REASON_ACCESS_DENIED = 2147483650;\n\n // recording download failed - client code 429\n public const int FLAG_RECORDING_REASON_TOO_MANY_REQUESTS = 2147483651;\n\n // recording download failed - unknown client error\n public const int FLAG_RECORDING_REASON_CLIENT_ERROR = 2147483652;\n\n // recording download failed - unknown error\n public const int FLAG_RECORDING_REASON_UNKNOWN_ERROR = 2147483653;\n\n // It has been setup ahead of time through calendar\n public const string STATUS_SCHEDULED = 'scheduled';\n\n // It is awaiting audio.\n public const string STATUS_PENDING = 'pending';\n\n // Participant(s) dialed in, awaiting organizer.\n public const string STATUS_RINGING = 'ringing';\n\n // Call is in progress.\n public const string STATUS_IN_PROGRESS = 'in-progress';\n\n // It has ended.\n public const string STATUS_COMPLETED = 'completed';\n\n // Cancelled prior to starting.\n public const string STATUS_CANCELLED = 'canceled';\n\n public const string STATUS_DUPLICATED = 'duplicated'; // duplicated conference\n\n public const string STATUS_STARTING_SOON = 'starting-soon';\n\n public const string STATUS_BOT_CREATE_SENT = 'bot-create-sent';\n\n public const string STATUS_BOT_INSTANCE_WORKER_ASSIGNED = 'worker-assigned';\n\n public const string STATUS_BOT_INSTANCE_STARTED = 'bot-started';\n\n // When bot instance is waiting in lobby\n public const string STATUS_BOT_INSTANCE_WAITING_LOBBY = 'bot-waiting';\n\n public const string STATUS_BUSY = 'busy';\n public const string STATUS_NO_ANSWER = 'no-answer';\n public const string STATUS_FAILED = 'failed'; // Used by SMS too\n\n // SMS related\n public const string STATUS_ACCEPTED = 'accepted';\n public const string STATUS_QUEUED = 'queued';\n public const string STATUS_SENDING = 'sending';\n public const string STATUS_SENT = 'sent';\n public const string STATUS_DELIVERED = 'delivered';\n public const string STATUS_UNDELIVERED = 'undelivered';\n public const string STATUS_RECEIVING = 'receiving';\n public const string STATUS_RECEIVED = 'received';\n public const string STATUS_RESENT = 'resent';\n\n public const array SMS_STATUSES = [\n Activity::STATUS_RECEIVED,\n Activity::STATUS_SENT,\n Activity::STATUS_DELIVERED,\n ];\n\n public const array SOFT_PHONE_CONFERENCE_STATUSES = [\n Activity::STATUS_IN_PROGRESS,\n Activity::STATUS_COMPLETED,\n ];\n\n // @todo refactor prefix from `TYPE_` to `CHANNEL_`\n public const string TYPE_SOFTPHONE = 'softphone';\n public const string TYPE_SOFTPHONE_INBOUND = 'softphone-inbound';\n public const string TYPE_CONFERENCE = 'conference';\n public const string TYPE_SMS_INBOUND = 'sms-inbound';\n public const string TYPE_SMS_OUTBOUND = 'sms-outbound';\n public const string TYPE_EMAIL_INBOUND = 'email-inbound';\n public const string TYPE_EMAIL_OUTBOUND = 'email-outbound';\n\n public const array CHANNELS = [\n self::TYPE_SOFTPHONE,\n self::TYPE_SOFTPHONE_INBOUND,\n self::TYPE_CONFERENCE,\n self::TYPE_SMS_INBOUND,\n self::TYPE_SMS_OUTBOUND,\n self::TYPE_EMAIL_INBOUND,\n self::TYPE_EMAIL_OUTBOUND,\n ];\n\n public const array PLAYABLE_CHANNELS = [\n self::TYPE_SOFTPHONE,\n self::TYPE_SOFTPHONE_INBOUND,\n self::TYPE_CONFERENCE,\n ];\n\n // Recording States\n public const string RECORDING_OFF = 'off'; // Default state\n public const string RECORDING_IN_PROGRESS = 'in-progress';\n public const string RECORDING_PAUSED = 'paused';\n public const string RECORDING_STOPPED = 'stopped'; // To never be resumed.\n public const string RECORDING_RECORDED = 'recorded'; // At least some portion of it was recorded.\n public const string RECORDING_FAILED = 'failed'; // Recording was attempted but failed for some reason.\n\n // Live Stream States\n public const int ON_AIR_DEFAULT = 0;\n public const int ON_AIR_READY = 1;\n public const int ON_AIR_PREPARING = 2;\n public const int ON_AIR_STREAMING = 3;\n public const int ON_AIR_FINISHED = 4;\n public const int ON_AIR_NOT_STREAMED = 5;\n public const int ON_AIR_ERROR = -1;\n\n public const string SOURCE_GONG = 'gong';\n public const string SOURCE_CHORUS = 'chorus';\n public const string SOURCE_OUTLOOK = 'outlook';\n public const string SOURCE_GOOGLE = 'google';\n\n // Activity Providers\n public const string PROVIDER_TWILIO = 'twilio'; // XXX: This is run via the Jiminny Provider.\n public const string PROVIDER_OUTREACH = 'outreach';\n public const string PROVIDER_ZOOM_BOT = 'zoom-bot';\n public const string PROVIDER_SALESLOFT = 'salesloft';\n public const string PROVIDER_GOOGLE = 'google';\n public const string PROVIDER_AIRCALL = 'aircall';\n public const string PROVIDER_JUSTCALL = 'justcall';\n public const string PROVIDER_GOOGLE_MEET = 'google-meet';\n public const string PROVIDER_GONG = 'gong';\n public const string PROVIDER_HUBSPOT = 'hubspot';\n public const string PROVIDER_CLOSE = 'close';\n public const string PROVIDER_TEAMS = 'ms-teams';\n public const string PROVIDER_SALESFORCE = 'salesforce';\n public const string PROVIDER_GROOVE = 'groove';\n public const string PROVIDER_XANT = 'xant';\n public const string PROVIDER_OFFICE = 'office';\n public const string PROVIDER_NATTERBOX = 'natterbox';\n public const string PROVIDER_RINGCENTRAL = 'ringcentral';\n public const string PROVIDER_RINGCENTRAL_VIDEO = 'ringcentral-video';\n public const string PROVIDER_GOTOMEETING = 'go-to-meeting';\n public const string PROVIDER_DEMODESK = 'demo-desk';\n public const string PROVIDER_DIALPAD = 'dialpad';\n public const string PROVIDER_ZOOM_PHONE = 'zoom-phone';\n public const string PROVIDER_CLOUDCALL = 'cloudcall';\n public const string PROVIDER_CLOUDCALL_US = 'cloudcall-us';\n public const string PROVIDER_EIGHT_BY_EIGHT = 'eight-by-eight'; // \"8x8\" UK\n public const string PROVIDER_EIGHT_BY_EIGHT_CA = 'eight-by-eight-ca'; // \"8x8\" Canada\n public const string PROVIDER_EIGHT_BY_EIGHT_AP = 'eight-by-eight-ap'; // \"8x8\" Australia\n public const string PROVIDER_EIGHT_BY_EIGHT_US_EAST = 'eight-by-eight-use'; // \"8x8\" US East\n public const string PROVIDER_EIGHT_BY_EIGHT_US_WEST = 'eight-by-eight-usw'; // \"8x8\" US West\n public const string PROVIDER_CONNECT_AND_SELL = 'connect-and-sell';\n public const string PROVIDER_CLOUD_TALK = 'cloud-talk';\n public const string PROVIDER_AMAZON_CONNECT = 'amazon-connect';\n public const string PROVIDER_VONAGE = 'vonage';\n public const string PROVIDER_MIGRATOR = 'migrator';\n public const string PROVIDER_UPLOADER = 'uploader';\n public const string PROVIDER_TALKDESK = 'talkdesk';\n public const string PROVIDER_TWILIO_FLEX = 'twilio-flex';\n public const string PROVIDER_TWILIO_FLEX_DIRECT = 'twilio-flex-direct';\n public const string PROVIDER_TWILIO_VIDEO = 'twilio-video';\n public const string PROVIDER_AVAYA = 'avaya';\n public const string PROVIDER_TELUS = 'telus';\n public const string PROVIDER_FIVE_NINE = 'five-nine';\n public const string PROVIDER_APOLLO = 'apollo';\n public const string PROVIDER_ORUM = 'orum';\n public const string PROVIDER_BLOOBIRDS = 'bloobirds';\n\n /**\n * @const API_PROVIDERS\n * A list of integrations that import calls via API instead of webhooks\n */\n public const array API_PROVIDERS = [\n self::PROVIDER_OUTREACH,\n self::PROVIDER_SALESLOFT,\n self::PROVIDER_HUBSPOT,\n self::PROVIDER_GROOVE,\n self::PROVIDER_XANT,\n self::PROVIDER_NATTERBOX,\n self::PROVIDER_CLOUDCALL,\n self::PROVIDER_CLOUDCALL_US,\n self::PROVIDER_EIGHT_BY_EIGHT,\n self::PROVIDER_EIGHT_BY_EIGHT_CA,\n self::PROVIDER_EIGHT_BY_EIGHT_AP,\n self::PROVIDER_EIGHT_BY_EIGHT_US_EAST,\n self::PROVIDER_EIGHT_BY_EIGHT_US_WEST,\n self::PROVIDER_CONNECT_AND_SELL,\n self::PROVIDER_CLOUD_TALK,\n self::PROVIDER_AMAZON_CONNECT,\n self::PROVIDER_VONAGE,\n self::PROVIDER_TALKDESK,\n self::PROVIDER_TWILIO_VIDEO,\n self::PROVIDER_TWILIO_FLEX,\n self::PROVIDER_TWILIO_FLEX_DIRECT,\n self::PROVIDER_FIVE_NINE,\n self::PROVIDER_APOLLO,\n self::PROVIDER_ORUM,\n self::PROVIDER_BLOOBIRDS,\n self::PROVIDER_RINGCENTRAL,\n self::PROVIDER_AVAYA,\n self::PROVIDER_TELUS,\n ];\n\n public const array FINITE_STATES = [\n self::TYPE_SOFTPHONE => [\n self::STATUS_COMPLETED,\n self::STATUS_FAILED,\n self::STATUS_NO_ANSWER,\n self::STATUS_BUSY,\n ],\n self::TYPE_SOFTPHONE_INBOUND => [\n self::STATUS_COMPLETED,\n self::STATUS_FAILED,\n self::STATUS_NO_ANSWER,\n self::STATUS_BUSY,\n ],\n self::TYPE_CONFERENCE => self::FINITE_STATES_CONFERENCE,\n ];\n\n public const array FINITE_STATES_CONFERENCE = [\n self::STATUS_COMPLETED,\n self::STATUS_FAILED,\n self::STATUS_CANCELLED,\n ];\n\n public const array MEETING_BOT_JOIN_ATTEMPTED = [\n self::STATUS_BOT_INSTANCE_WAITING_LOBBY,\n self::STATUS_BOT_INSTANCE_STARTED,\n ];\n\n public static array $enumStatuses = [\n self::STATUS_SCHEDULED,\n self::STATUS_PENDING,\n self::STATUS_RINGING,\n self::STATUS_IN_PROGRESS,\n self::STATUS_COMPLETED,\n self::STATUS_CANCELLED,\n self::STATUS_BUSY,\n self::STATUS_NO_ANSWER,\n self::STATUS_FAILED,\n self::STATUS_ACCEPTED,\n self::STATUS_QUEUED,\n self::STATUS_SENDING,\n self::STATUS_SENT,\n self::STATUS_RESENT,\n self::STATUS_DELIVERED,\n self::STATUS_UNDELIVERED,\n self::STATUS_RECEIVING,\n self::STATUS_RECEIVED,\n self::STATUS_BOT_INSTANCE_WAITING_LOBBY,\n self::STATUS_STARTING_SOON,\n self::STATUS_BOT_INSTANCE_WORKER_ASSIGNED,\n self::STATUS_BOT_INSTANCE_STARTED,\n self::STATUS_DUPLICATED,\n ];\n\n public static array $enumProviders = [\n self::PROVIDER_TWILIO,\n self::PROVIDER_OUTREACH,\n self::PROVIDER_ZOOM_BOT,\n self::PROVIDER_SALESLOFT,\n self::PROVIDER_AIRCALL,\n self::PROVIDER_JUSTCALL,\n self::PROVIDER_GOOGLE_MEET,\n self::PROVIDER_GONG,\n self::PROVIDER_HUBSPOT,\n self::PROVIDER_CLOSE,\n self::PROVIDER_TEAMS,\n self::PROVIDER_SALESFORCE,\n self::PROVIDER_GROOVE,\n self::PROVIDER_XANT,\n self::PROVIDER_GOOGLE,\n self::PROVIDER_OFFICE,\n self::PROVIDER_NATTERBOX,\n self::PROVIDER_RINGCENTRAL,\n self::PROVIDER_RINGCENTRAL_VIDEO,\n self::PROVIDER_GOTOMEETING,\n self::PROVIDER_DEMODESK,\n self::PROVIDER_DIALPAD,\n self::PROVIDER_ZOOM_PHONE,\n self::PROVIDER_CLOUDCALL,\n self::PROVIDER_CLOUDCALL_US,\n self::PROVIDER_EIGHT_BY_EIGHT,\n self::PROVIDER_EIGHT_BY_EIGHT_CA,\n self::PROVIDER_EIGHT_BY_EIGHT_AP,\n self::PROVIDER_EIGHT_BY_EIGHT_US_EAST,\n self::PROVIDER_EIGHT_BY_EIGHT_US_WEST,\n self::PROVIDER_CONNECT_AND_SELL,\n self::PROVIDER_CLOUD_TALK,\n self::PROVIDER_AMAZON_CONNECT,\n self::PROVIDER_VONAGE,\n self::PROVIDER_TALKDESK,\n self::PROVIDER_TWILIO_FLEX,\n self::PROVIDER_TWILIO_FLEX_DIRECT,\n self::PROVIDER_TWILIO_VIDEO,\n self::PROVIDER_AVAYA,\n self::PROVIDER_TELUS,\n self::PROVIDER_FIVE_NINE,\n self::PROVIDER_APOLLO,\n self::PROVIDER_ORUM,\n self::PROVIDER_BLOOBIRDS,\n ];\n\n public static $enumRecordingStates = [\n self::RECORDING_OFF, // Default state\n self::RECORDING_IN_PROGRESS,\n self::RECORDING_PAUSED,\n self::RECORDING_STOPPED,\n self::RECORDING_RECORDED,\n self::RECORDING_FAILED,\n ];\n\n // @Important:\n // This collection is not used anywhere, and is fully duplicated by the Channels const.\n // Validate if it is referred somehow via the enum trait, and if not, remove it entirely.\n // An even better strategy will be to move all those constants to a dedicated class\n protected array $enumTypes = [\n self::TYPE_SOFTPHONE,\n self::TYPE_SOFTPHONE_INBOUND,\n self::TYPE_CONFERENCE,\n self::TYPE_SMS_INBOUND,\n self::TYPE_SMS_OUTBOUND,\n self::TYPE_EMAIL_INBOUND,\n self::TYPE_EMAIL_OUTBOUND,\n ];\n\n protected static $enumFailedStatuses = [\n self::STATUS_NO_ANSWER,\n self::STATUS_FAILED,\n self::STATUS_BUSY,\n self::STATUS_CANCELLED,\n ];\n\n protected $table = 'activities';\n\n protected $fillable = [\n // Type of activity.\n 'type', // @todo refactor to `channel`\n // The activity type.\n 'playbook_category_id',\n // User who hosts the activity.\n 'user_id',\n // Related Lead record (if applicable)\n 'lead_id',\n // Related Account record (if applicable)\n 'account_id',\n // Related Contact record (if applicable)\n 'contact_id',\n // Related Opportunity record (if applicable)\n 'opportunity_id',\n // Stage of activity.\n 'stage_id',\n // Value of opportunity.\n 'value',\n // If the activity relates to a CRM task.\n 'crm_provider_id',\n // If the activity was created through an external device.\n 'device_id',\n // the activity's language code\n 'language',\n // transcription id\n 'transcription_id',\n // Duration of the call, with microseconds precision.\n 'duration',\n // One of enumStatuses above.\n 'status',\n // Have we reminded them to log the call?\n 'log_reminder_sent_at',\n // If activity is private or inter-org, flagged here.\n 'is_internal',\n // Managers and above can mark a call as private, to exclude it from other team members\n 'is_private',\n 'is_processed',\n // Boolean for this activity being instant invite handled.\n 'is_instant_invite',\n // If activity is in recording state, flagged here.\n 'recording_state',\n // If activity recording is overidden from default.\n 'recording_preference',\n // if recording did (not) happen, why that is\n 'recording_reason_code',\n // Average score, updated during\n 'average_score',\n // Summary that the organizer has taken after the call.\n 'summary',\n // Subject of the activity, usually taken from calendar event.\n 'title',\n // Description of the activity, usually taken from calendar event.\n 'description',\n // Start time, usually taken from calendar event.\n 'scheduled_start_time',\n // End time, usually taken from calendar event.\n 'scheduled_end_time',\n // When the call actually started.\n 'actual_start_time',\n // When the call actually ended.\n 'actual_end_time',\n // SMS: Message reference\n 'telephony_provider_id',\n // SMS: Participant who sent message\n 'from_participant_id',\n // SMS: Participant who should receive the message\n 'to_participant_id',\n // When an external guest joins an organizers meeting room and the organizer is not present,\n // send them an SMS notification that someone has joined.\n 'organizer_notified_at',\n // where was the activity imported from\n 'source',\n // The id in the source system (e.g. the bot id in Recall.ai)\n 'external_id',\n // The provider, by default it is twilio.\n 'provider',\n // Meeting location url\n 'location',\n // The snapshot for displaying a poster image.\n 'poster_path',\n 'crm_configuration_id',\n // If there is an automated message that the conversation is being recorded\n 'has_recording_prompt',\n // If the activity is being live-streamed\n 'on_air',\n 'calendar_event_id',\n ];\n\n protected $appends = [\n 'id_string',\n 'organizer',\n ];\n\n protected $hidden = [\n 'uuid',\n ];\n\n protected $visible = [\n 'id_string',\n 'type',\n 'duration',\n 'average_score',\n 'status',\n 'log_reminder_sent_at',\n 'title',\n 'description',\n 'is_internal',\n 'scheduled_start_time',\n 'scheduled_end_time',\n 'actual_start_time',\n 'actual_end_time',\n 'user',\n 'category',\n 'account',\n 'contact',\n 'opportunity',\n 'lead',\n 'stage',\n 'stats',\n 'participants',\n 'playlists',\n 'tracks',\n 'comments',\n 'plays',\n 'coachingFeedbacks',\n 'shares',\n 'favorites',\n 'language',\n 'transcription',\n 'is_private',\n 'is_instant_invite',\n 'on_air',\n 'calendar_event_id',\n ];\n\n protected function casts(): array\n {\n return [\n 'scheduled_start_time' => 'datetime',\n 'scheduled_end_time' => 'datetime',\n 'actual_start_time' => 'datetime',\n 'actual_end_time' => 'datetime',\n 'organizer_notified_at' => 'datetime',\n 'log_reminder_sent_at' => 'datetime',\n 'is_internal' => 'boolean',\n 'duration' => 'integer',\n 'average_score' => 'decimal:2',\n 'is_private' => 'boolean',\n 'is_processed' => 'boolean',\n 'is_instant_invite' => 'boolean',\n 'value' => 'decimal:2',\n 'recording_preference' => 'boolean',\n 'recording_reason_code' => 'integer',\n 'has_recording_prompt' => 'boolean',\n 'on_air' => 'integer',\n ];\n }\n\n protected static function boot()\n {\n parent::boot();\n\n static::updated(static function (Activity $activity) {\n // If activity is about to start (pending, ringing, in-progress) or event is scheduled in less than 1 week\n if (in_array($activity->status, [Activity::STATUS_PENDING, Activity::STATUS_RINGING, Activity::STATUS_IN_PROGRESS], true) ||\n ($activity->scheduled_start_time && (int) $activity->scheduled_start_time->diffInWeeks(new Carbon(), true) < 1)) {\n if ($activity->isDirty('status')) {\n event(new StatusUpdated($activity));\n }\n\n if ($activity->isDirty('stage_id')) {\n event(new StageUpdated($activity));\n }\n\n if ($activity->isDirty(['lead_id', 'account_id', 'contact_id'])) {\n event(new ProspectUpdated($activity));\n }\n\n if ($activity->isDirty('opportunity_id')) {\n event(new ActivityUpdated($activity, 'activity.opportunity-updated', Auth::user()));\n }\n\n if ($activity->isDirty('title')) {\n event(new TitleUpdated($activity));\n }\n }\n\n if ($activity->isDirty('playbook_category_id')) {\n event(new ActivityTypeUpdated($activity));\n }\n });\n\n static::deleted(static function (Activity $activity) {\n // Hard delete associated playlistActivities\n $activity->playlistActivities()->delete();\n });\n }\n\n public function getOrganizerAttribute(): ?Participant\n {\n $participant = $this->participants()->where('user_id', $this->user_id)->first();\n\n if (! $participant instanceof Participant && $participant !== null) {\n throw new RuntimeException(sprintf('$participant must be an instance of \"%s\" or null', Participant::class));\n }\n\n return $participant;\n }\n\n public function getFormattedValueAttribute()\n {\n $currencyCode = 'USD';\n if ($this->opportunity) {\n $currencyCode = $this->opportunity->getCurrencyCode();\n }\n\n $formatter = new CurrencyFormatter();\n $formatter->setTextAttribute(NumberFormatter::CURRENCY_CODE, $currencyCode);\n $formatter->setAttribute(NumberFormatter::MAX_FRACTION_DIGITS, 0);\n\n return $formatter->format($this->value, $currencyCode);\n }\n\n public function getProspectNameAttribute(): ?string\n {\n $prospectName = null;\n\n if ($this->lead_id) {\n $prospectName = $this->lead->name;\n } elseif ($this->contact_id) {\n $prospectName = $this->contact->name;\n } elseif ($this->account_id) {\n $prospectName = $this->account->name;\n }\n\n return $prospectName;\n }\n\n public function getProspectName(): ?string\n {\n /** @var string|null */\n return $this->getAttribute('prospect_name');\n }\n\n /**\n * Get activity title depending on prospect or title\n */\n public function getActivityTitleAttribute(): ?string\n {\n $activityTitle = null;\n if ($this->prospect && $this->prospect->getName()) {\n if ($this->account_id) {\n $activityTitle = $this->account->name;\n } elseif ($this->lead_id) {\n $activityTitle = $this->lead->company;\n } elseif ($this->contact_id) {\n $activityTitle = $this->contact->account ? $this->contact->account->name : $this->contact->name;\n }\n } elseif ($this->title) {\n $activityTitle = $this->title;\n }\n\n return $activityTitle;\n }\n\n public function wasRecentlyCreated(): bool\n {\n return $this->wasRecentlyCreated;\n }\n\n public function getProspectTypeAttribute()\n {\n $prospectType = null;\n\n if ($this->lead_id) {\n $prospectType = 'Lead';\n } elseif ($this->contact_id) {\n $prospectType = 'Contact';\n } elseif ($this->account_id) {\n $prospectType = 'Account';\n }\n\n return $prospectType;\n }\n\n /**\n * Return the best match for prospect. Results are in the following order of priority:\n * 1. Lead\n * 2. Contact\n * 3. Account\n * 4. NULL\n */\n public function getProspectAttribute(): ?ProspectInterface\n {\n if ($this->hasLead()) {\n return $this->getLead();\n }\n\n if ($this->hasContact()) {\n return $this->getContact();\n }\n\n if ($this->hasAccount()) {\n return $this->getAccount();\n }\n\n return null;\n }\n\n public function getTitleAttribute($value): ?string\n {\n return \\getActivityTitleAttribute(\n $this->user->name,\n $this->getType(),\n $value,\n $this->prospect->name ?? null,\n $this->from->national_phone_number ?? null\n );\n }\n\n public function getTitle(): ?string\n {\n return $this->getAttribute('title');\n }\n\n public function getSummary(): ?string\n {\n return $this->getAttribute('summary');\n }\n\n public function isInternal(): bool\n {\n return $this->getAttribute('is_internal');\n }\n\n public function getIsPrivate(): bool\n {\n return $this->getAttribute('is_private');\n }\n\n public function getDescription(): ?string\n {\n return $this->getAttribute('description');\n }\n\n public function hasTitle(): bool\n {\n return $this->getOriginal('title') !== null;\n }\n\n public function getPlayCountAttribute()\n {\n return $this->getPlaysCountAttribute();\n }\n\n public function getPlaysCountAttribute()\n {\n if (! isset($this->attributes['plays_count'])) {\n $this->loadCount('plays');\n }\n\n return $this->attributes['plays_count'];\n }\n\n public function getCommentCountAttribute()\n {\n return $this->getCommentsCountAttribute();\n }\n\n public function getCommentsCountAttribute()\n {\n if (! isset($this->attributes['comments_count'])) {\n $this->loadCount('comments');\n }\n\n return $this->attributes['comments_count'];\n }\n\n public function getVisibleCommentsCountAttribute()\n {\n if (! isset($this->attributes['visible_comments_count'])) {\n $activityCommentsService = app(ActivityCommentService::class);\n $user = Auth::user() instanceof User ? Auth::user() : null;\n $this->attributes['visible_comments_count'] = $activityCommentsService\n ->getVisibleCommentsCount($this, $user);\n }\n\n return $this->attributes['visible_comments_count'];\n }\n\n public function getShareCountAttribute()\n {\n return $this->getSharesCountAttribute();\n }\n\n public function getSharesCountAttribute()\n {\n if (! isset($this->attributes['shares_count'])) {\n $this->loadCount('shares');\n }\n\n return $this->attributes['shares_count'];\n }\n\n\n /**\n * Get the count of favorites playlists this activity appears in\n */\n public function getFavoriteCountAttribute(): int\n {\n return $this->getFavoritesCountAttribute();\n }\n\n public function getFavoritesCountAttribute()\n {\n if (! isset($this->attributes['favorites_count'])) {\n $this->loadCount('favorites');\n }\n\n return $this->attributes['favorites_count'];\n }\n\n public function getActiveParticipantsCountAttribute()\n {\n if (! isset($this->attributes['active_participants_count'])) {\n $this->loadCount('activeParticipants');\n }\n\n return $this->attributes['active_participants_count'];\n }\n\n public function getTracksWithTelephonyCountAttribute()\n {\n if (! isset($this->attributes['tracks_with_telephony_count'])) {\n $this->loadCount('tracksWithTelephony');\n }\n\n return $this->attributes['tracks_with_telephony_count'];\n }\n\n /**\n * @TEMP\n * $this->loadCount('tracksWithTelephony') throws null pointer exception\n */\n public function countTracksWithTelephony(): int\n {\n return $this->tracks()->whereNotNull('telephony_provider_id')->count();\n }\n\n public function getDuration(): float\n {\n return $this->getAttribute('duration');\n }\n\n public function getDurationForHumansAttribute()\n {\n return Carbon::now()->subSeconds($this->duration)->diffForHumans(now(), true);\n }\n\n public function getDurationForHumansShortAttribute(): string\n {\n return Carbon::now()->subSeconds($this->duration)->diffForHumans(now(), true, true);\n }\n\n public function hasRecordingPreference(): bool\n {\n return $this->getAttribute('recording_preference') !== null;\n }\n\n public function getRecordingPreference()\n {\n return $this->getAttribute('recording_preference');\n }\n\n /** @return BelongsTo<User, self> */\n public function user(): BelongsTo\n {\n return $this->belongsTo(User::class)->with('group');\n }\n\n public function device()\n {\n return $this->belongsTo(Device::class);\n }\n\n public function category()\n {\n return $this->belongsTo(PlaybookCategory::class, 'playbook_category_id');\n }\n\n public function getCategory(): ?PlaybookCategory\n {\n return $this->getAttribute('category');\n }\n\n public function getPlaybookCategoryId(): ?int\n {\n return $this->getAttribute('playbook_category_id');\n }\n\n public function hasStats(): bool\n {\n return $this->getAttribute('stats') !== null;\n }\n\n public function getStats(): ?Stats\n {\n return $this->getAttribute('stats');\n }\n\n public function stats(): HasOne\n {\n return $this->hasOne(Stats::class);\n }\n\n public function participantStats(): Eloquent\\Relations\\HasManyThrough\n {\n return $this->hasManyThrough(\n Models\\Participant\\ParticipantStats::class,\n Participant::class,\n 'activity_id',\n 'participant_id'\n );\n }\n\n public function getParticipantStats(): Eloquent\\Collection\n {\n return $this->getAttribute('participantStats');\n }\n\n public function account()\n {\n return $this->belongsTo(Account::class);\n }\n\n public function contact()\n {\n return $this->belongsTo(Contact::class)->with(['account']);\n }\n\n public function lead()\n {\n return $this->belongsTo(Lead::class)->with(['stage', 'recordType']);\n }\n\n /**\n * @return BelongsTo<Opportunity, self>\n */\n public function opportunity(): BelongsTo\n {\n /** @var BelongsTo<Opportunity, self> */\n return $this->belongsTo(Opportunity::class);\n }\n\n public function stage()\n {\n return $this->belongsTo(Stage::class);\n }\n\n /**\n * @return HasMany<Session>\n */\n public function sessions(): HasMany\n {\n return $this->hasMany(Session::class);\n }\n\n /**\n * @return HasMany|ParticipantSpeech[]|Eloquent\\Collection\n */\n public function participantSpeeches()\n {\n return $this->hasMany(ParticipantSpeech::class);\n }\n\n public function getParticipantSpeeches(): Eloquent\\Collection\n {\n return $this->getAttribute('participantSpeeches');\n }\n\n /**\n * @return HasMany|Log[]|Eloquent\\Collection\n */\n public function logs()\n {\n return $this->hasMany(Log::class);\n }\n\n /**\n * @return HasMany|Moment[]|Eloquent\\Collection\n */\n public function moments()\n {\n return $this->hasMany(Moment::class);\n }\n\n /**\n * @return HasMany|Note[]|Eloquent\\Collection\n */\n public function notes()\n {\n return $this->hasMany(Note::class);\n }\n\n /**\n * @return Eloquent\\Collection|Note[]\n */\n public function getNotes(): Eloquent\\Collection\n {\n return $this->getAttribute('notes');\n }\n\n /**\n * @return HasMany|Message[]|Eloquent\\Collection\n */\n public function messages()\n {\n return $this->hasMany(Message::class);\n }\n\n public function coachingMessages(): HasMany\n {\n return $this->hasMany(Message::class)\n ->where('is_private', 1);\n }\n\n public function getCoachingMessages(): Eloquent\\Collection\n {\n return $this->getAttribute('coachingMessages');\n }\n\n public function participants(): HasMany\n {\n return $this->hasMany(Participant::class);\n }\n\n public function getSnapshots(): Eloquent\\Collection\n {\n return $this->getAttribute('snapshots');\n }\n\n /** @return HasMany<Track> */\n public function tracks(): HasMany\n {\n return $this->hasMany(Track::class);\n }\n\n public function tracksWithTelephony(): HasMany\n {\n return $this->hasMany(Track::class)->whereNotNull('telephony_provider_id');\n }\n\n public function getTracksWithTelephony(): Eloquent\\Collection\n {\n return $this->getAttribute('tracksWithTelephony');\n }\n\n /** @return Collection|Track[] */\n public function getTracks(): Eloquent\\Collection\n {\n return $this->getAttribute('tracks');\n }\n\n public function masterTrack(): HasOne\n {\n return $this->hasOne(Track::class)->where('is_master', 1)\n ->whereIn('format', [Track::FORMAT_WAV, Track::FORMAT_M3U8])\n ->latest();\n }\n\n public function getMasterTrack(): ?Track\n {\n /** @var Track|null */\n return $this->getAttribute('masterTrack');\n }\n\n public function transcription(): Eloquent\\Relations\\BelongsTo\n {\n return $this->belongsTo(Transcription::class, 'transcription_id');\n }\n\n public function findTranscriptionPromptSummaries(): Collection\n {\n $transcriptionId = $this->getTranscriptionId();\n if (is_null($transcriptionId)) {\n return new Collection();\n }\n\n return Models\\AiPrompt::query()\n ->where('transcription_id', $transcriptionId)\n ->get();\n }\n\n public function getTranscription(): Transcription\n {\n return $this->getAttribute('transcription');\n }\n\n public function hasTranscription(): bool\n {\n return $this->getAttribute('transcription') !== null;\n }\n\n public function setTranscriptionId(int $transcriptionId): Activity\n {\n $this->setAttribute('transcription_id', $transcriptionId);\n\n return $this;\n }\n\n public function unsetTranscriptionId(): self\n {\n $this->setAttribute('transcription_id', null);\n\n return $this;\n }\n\n public function getTranscriptionId(): ?int\n {\n return $this->getAttribute('transcription_id');\n }\n\n /** @deprecated */\n public function hasTranscriptionId(): bool\n {\n return $this->getAttribute('transcription_id') !== null;\n }\n\n public function coachRequests()\n {\n return $this->hasMany(CoachRequest::class);\n }\n\n public function availabilityNotifications()\n {\n return $this->hasMany(AvailabilityNotification::class);\n }\n\n public function processingStates()\n {\n return $this->hasMany(Models\\Activity\\ActivityProcessingState::class);\n }\n\n public function uploadSettings()\n {\n return $this->hasMany(ActivityUploadSetting::class);\n }\n\n public function comments()\n {\n return $this->hasMany(Comment::class);\n }\n\n public function getComments(): Eloquent\\Collection\n {\n return $this->getAttribute('comments');\n }\n\n public function visibleComments()\n {\n $rel = $this->hasMany(Comment::class);\n // Doesn't have auth()->user() in some tests, breaks the build\n if ($user = auth()->user()) {\n return $rel->visibleThreads($user->id);\n }\n\n return $rel;\n }\n\n public function snapshots(): HasMany\n {\n return $this->hasMany(Snapshot::class);\n }\n\n public function calendarEvent()\n {\n return $this->belongsTo(CalendarEvent::class);\n }\n\n public function getCalendarEvent(): ?CalendarEvent\n {\n return $this->getAttribute('calendarEvent');\n }\n\n public function latestCoachingFeedbacks(): HasMany\n {\n return $this->hasMany(CoachingFeedback::class)->latest();\n }\n\n public function playlists(): BelongsToMany\n {\n return $this->belongsToMany(Playlist::class, 'playlist_activities')\n ->withPivot('id', 'uuid', 'user_id', 'start_time', 'end_time')\n ->using(PlaylistActivity::class)\n ->withTimestamps();\n }\n\n public function coachingFeedbacks(): HasMany\n {\n return $this->hasMany(CoachingFeedback::class);\n }\n\n /**\n * @return Eloquent\\Collection|CoachingFeedback[]\n */\n public function getCoachingFeedback(?int $visibility = null): Eloquent\\Collection\n {\n $feedbacks = $this->coachingFeedbacks();\n if ($visibility !== null) {\n $feedbacks = $feedbacks->where('visibility', $visibility);\n }\n\n return $feedbacks->get();\n }\n\n /** @return Eloquent\\Collection<int, PlaylistActivity> */\n public function favoritedBy(User $user): Eloquent\\Collection\n {\n return $this->favorites()->where('user_id', $user->getId())->get();\n }\n\n /**\n * Checks whether consumer has added this activity to their favorites playlist\n * In addition a default playlist gets created if not already present\n */\n public function wasFavoritedBy(User $user): bool\n {\n $playlist = $user->favoritePlaylist();\n\n return $playlist\n ->activities()\n ->where('activity_id', '=', $this->getId())\n ->exists();\n }\n\n /**\n * @return HasMany<PlaylistActivity>\n */\n public function playlistActivities(): HasMany\n {\n return $this->hasMany(PlaylistActivity::class);\n }\n\n /**\n * @return HasManyThrough<Playlist>\n */\n public function favoritePlaylists(): HasManyThrough\n {\n return $this->hasManyThrough(\n Playlist::class,\n PlaylistActivity::class,\n 'activity_id',\n 'id',\n 'id',\n 'playlist_id'\n )->where('is_default', 1);\n }\n\n /**\n * @return Eloquent\\Collection<int, Playlist>\n */\n public function getFavoritePlaylists(): Eloquent\\Collection\n {\n return $this->getAttribute('favoritePlaylists');\n }\n\n /**\n * Get activities from the default/favorite playlist\n *\n * @return Eloquent\\Builder|static\n */\n public function favorites()\n {\n return $this->playlistActivities()->whereHas('playlist', function ($query) {\n $query->where('is_default', 1);\n });\n }\n\n /**\n * @return Model|SubscriptionSet|null|object\n */\n public function subscribedBy(User $user)\n {\n if ($this->prospect === null) {\n return null;\n }\n\n return SubscriptionSet::select('activity_subscription_sets.*')\n ->where('user_id', $user->id)\n ->join('activity_subscriptions', function ($join) {\n $join\n ->on('subscription_set_id', '=', 'activity_subscription_sets.id');\n\n if ($this->account_id) {\n if ($this->opportunity_id) {\n $join\n ->where('followable_type', 'opportunity')\n ->where('followable_id', $this->opportunity_id);\n } else {\n $join\n ->where('followable_type', 'account')\n ->where('followable_id', $this->account_id);\n }\n } elseif ($this->contact_id) {\n $join\n ->where('followable_type', 'contact')\n ->where('followable_id', $this->contact_id);\n } elseif ($this->lead_id) {\n $join\n ->where('followable_type', 'lead')\n ->where('followable_id', $this->lead_id);\n }\n })\n ->first();\n }\n\n /**\n * @return array|Eloquent\\Builder[]|Eloquent\\Collection|SubscriptionSet[]\n */\n public function subscribers()\n {\n if ($this->prospect === null) {\n return [];\n }\n\n return SubscriptionSet::with(['subscriptions', 'user'])\n ->whereHas('subscriptions', function ($query) {\n if ($this->account_id) {\n if ($this->opportunity_id) {\n $query\n ->where('followable_type', 'opportunity')\n ->where('followable_id', $this->opportunity_id);\n } else {\n $query\n ->where('followable_type', 'account')\n ->where('followable_id', $this->account_id);\n }\n } elseif ($this->contact_id) {\n $query\n ->where('followable_type', 'contact')\n ->where('followable_id', $this->contact_id);\n } elseif ($this->lead_id) {\n $query\n ->where('followable_type', 'lead')\n ->where('followable_id', $this->lead_id);\n } else {\n // Nothing to join on?\n // refactor - use Jiminny specific exception\n throw new InvalidArgumentException('Cannot join on a specific customer filter.');\n }\n })\n ->whereHas('user', function ($query) {\n $query\n ->where('team_id', $this->user->team_id)\n ->where('status', User::STATUS_ACTIVE);\n })\n ->get();\n }\n\n /**\n * @return HasMany|Builder|Eloquent\\Collection|Play[]\n */\n public function plays()\n {\n return $this->hasMany(Play::class);\n }\n\n public function getPlays(): Eloquent\\Collection\n {\n return $this->getAttribute('plays');\n }\n\n public function playsBy(User $user)\n {\n /** @var Builder $builder */\n $builder = $this->plays()->where('user_id', $user->id);\n\n return $builder->get();\n }\n\n /**\n * Check if activity was played by a user\n */\n public function wasPlayedBy(User $user): bool\n {\n return $this->plays()->where('user_id', $user->id)->exists();\n }\n\n public function shares()\n {\n return $this->hasMany(Share::class);\n }\n\n /** @return BelongsTo<Participant, self> */\n public function from(): BelongsTo\n {\n return $this->belongsTo(Participant::class, 'from_participant_id');\n }\n\n /** @return BelongsTo<Participant, self> */\n public function to(): BelongsTo\n {\n return $this->belongsTo(Participant::class, 'to_participant_id');\n }\n\n /**\n * Get all of the connections through the participants.\n */\n public function connections()\n {\n return $this->hasManyThrough(\n Connection::class,\n Participant::class,\n 'activity_id',\n 'participant_id'\n );\n }\n\n public function getConnections(): Eloquent\\Collection\n {\n return $this->getAttribute('connections');\n }\n\n /**\n * Get all of the shares through the participants.\n */\n public function participantShares()\n {\n return $this->hasManyThrough(\n Participant\\Share::class,\n Participant::class,\n 'activity_id',\n 'participant_id'\n );\n }\n\n public function getParticipantShares(): Eloquent\\Collection\n {\n return $this->getAttribute('participantShares');\n }\n\n public function topicTriggers(): HasMany\n {\n return $this->hasMany(TopicTrigger::class);\n }\n\n public function activityScorecardRuleTriggers(): HasMany\n {\n return $this->hasMany(Models\\Scorecard\\ActivityScorecardRuleTrigger::class);\n }\n\n public function activityScorecardRules(): HasMany\n {\n return $this->hasMany(Models\\Scorecard\\ActivityScorecardRule::class);\n }\n\n public function questions(): HasMany\n {\n return $this->hasMany(Question::class);\n }\n\n /**\n * Get all the custom data attached to it.\n */\n public function data(): HasMany\n {\n return $this->hasMany(FieldData::class);\n }\n\n public function getData(): Eloquent\\Collection\n {\n /** @var Eloquent\\Collection */\n return $this->getAttribute('data');\n }\n\n #[Scope]\n protected function heldBetween($query, Carbon $start, Carbon $end)\n {\n // Sanity check.\n $from = min($start, $end);\n $until = max($start, $end);\n\n return $query\n ->where('actual_start_date', '>=', $from)\n ->where('actual_end_date', '<=', $until);\n }\n\n #[Scope]\n protected function scheduledBetween($query, Carbon $start, Carbon $end)\n {\n // Sanity check.\n $from = min($start, $end);\n $until = max($start, $end);\n\n return $query\n ->where('scheduled_start_date', '>=', $from)\n ->where('scheduled_end_date', '<=', $until);\n }\n\n /**\n * @param Builder<self> $query\n *\n * @return Builder<self>\n */\n #[Scope]\n protected function forTeam(Builder $query, int $teamId): Builder\n {\n /** @var Builder<self> */\n return $query->whereHas('user', static function (Builder $query) use ($teamId): void {\n $query->where('team_id', $teamId);\n });\n }\n\n /**\n * @param Builder<self> $query\n *\n * @return Builder<self>\n */\n #[Scope]\n protected function inOpenDeals(Builder $query): Builder\n {\n /** @var Builder<self> */\n return $query->whereHas(\n 'opportunity',\n static fn (Builder $query): Builder => $query\n ->where('is_closed', false)\n ->where('deleted_at', '=', null),\n );\n }\n\n /**\n * @param Builder<self> $query\n *\n * @return Builder<self>\n */\n #[Scope]\n protected function notInOpenDeals(Builder $query): Builder\n {\n /** @var Builder<self> */\n return $query->where(\n static fn (Builder $query): Builder => $query->whereNull('opportunity_id')\n ->orWhereHas(\n 'opportunity',\n static fn (Builder $query): Builder => $query->where('is_closed', true),\n )\n ->orWhereHas(\n 'opportunity',\n static fn (Builder $query): Builder => $query->withTrashed()->where('deleted_at', '!=', null),\n ),\n );\n }\n\n /**\n * Finds a participant and updates it with data. If participant doesn't exist creates a new participant from data.\n *\n * @param array $data participant data used to identify the participant and update it\n * @param bool $enterRoom true if participant is entering the room. false if we just want to update some participant data\n * @param Carbon|null $enterTime if $enterNow is true then this is the join time when the actual enter has occurred\n */\n public function updateOrCreateParticipant(\n array $data,\n bool $enterRoom = true,\n ?Carbon $enterTime = null,\n bool $nameMatching = false,\n ): Participant {\n $search = [];\n $participant = null;\n\n if (isset($data['user_id'])) {\n // Check if they already exist based on their ID.\n $search['user_id'] = $data['user_id'];\n } elseif (isset($data['provider_id'])) {\n $search['provider_id'] = $data['provider_id'];\n } elseif ($nameMatching && isset($data['name'])) {\n $search['name'] = $data['name'];\n }\n\n if (! empty($data['email'])) {\n $search['email'] = $data['email'];\n\n // If we have their email, this should be unique enough to lookup (e.g. calendar event based participant).\n unset($search['provider_id']);\n }\n\n // Search by phone number only in case nothing else is available to search by.\n if (array_key_exists('phone_number', $data) && empty($search)) {\n $search['phone_number'] = $data['phone_number'];\n }\n\n if (! empty($search)) {\n // Do a lookup now to see if we have a match on the provided data.\n $lookup = array_map(static function ($key, $value): array {\n return [$key, $value];\n }, array_keys($search), $search);\n\n $participant = $this->participants()->withTrashed()->where($lookup)->first();\n }\n\n // Do a partial match on the name and search in the team members.\n if (! $participant instanceof Participant && $nameMatching && ! empty($data['name'])) {\n $participantMatcher = app(MeetingBot\\Service\\ParticipantMatcher::class);\n\n if (! $participantMatcher instanceof MeetingBot\\Service\\ParticipantMatcher) {\n throw new LogicException('Expecting ParticipantMatcher service instance');\n }\n\n $participant = $participantMatcher->match($this, $data['name']);\n\n // If we've found good participant, avoid data overwrite in `$participant->fill($data)` below.\n if ($participant instanceof Models\\Participant && $participant->hasName()) {\n unset($data['name']); // Thoughts: should we unset also $data['user_id'] and $data['email'] ?\n }\n }\n\n if (! $participant instanceof Participant) {\n // If no match, create a new participant.\n if (empty($search)) {\n $participant = $this->participants()->create();\n } else {\n // If no match, create a new participant but avoid creating duplicates\n $participant = $this->participants()->withTrashed()->firstOrNew($search);\n }\n }\n\n // If we have just recycled a deleted participant\n if ($participant->trashed()) {\n $participant->deleted_at = null;\n }\n\n // Deal with the case when calendar syncs the event while it's in progress.\n // We should prevent change of the participant name, because speeches mapping will fail.\n if ($enterRoom === false\n && $this->isInProgress()\n && $participant->hasName()\n && isset($data['name'])\n && $data['name'] !== $participant->getName()\n ) {\n unset($data['name']);\n }\n\n // Upsert with new data.\n $participant->fill($data);\n\n if ($enterRoom) {\n if ($enterTime === null) {\n $enterTime = now();\n }\n\n // Participant enters room for the first time\n if ($participant->enter_time === null) {\n $participant->enter_time = $enterTime;\n }\n\n // If there is an exit time and it's prior to new enter_time\n if ($participant->exit_time && $participant->exit_time->lt($enterTime)) {\n // Participant has re-joined\n $participant->exit_time = null;\n }\n }\n\n $participant->save();\n\n return $participant;\n }\n\n /**\n * Updates participant CRM data\n *\n * @param array{\n * Lead|null,\n * Account|null,\n * Opportunity|null,\n * Contact|null,\n * Stage|null,\n * string|null\n *} $records\n * @param Participant $participant participant the CRM data is associated with\n */\n public function updateParticipantCrmData(array $records, Participant $participant): void\n {\n // Extract the records.\n [$lead, , , $contact] = $records;\n\n $resolver = $this->getUpdateCrmDataResolver();\n $strategy = $resolver->resolveForParticipant($lead, $contact);\n\n if ($strategy == UpdateCrmDataByStrategy::Lead) {\n if (! $participant->hasName()) {\n $participant->name = $lead->name;\n }\n\n if (! $participant->hasEmailAddress()) {\n $participant->email = $lead->email;\n }\n\n if (! $participant->hasPhoneNumber()) {\n $participant->phone_number = $lead->phone;\n }\n\n $participant->lead_id = $lead->id;\n $participant->save();\n } elseif ($strategy == UpdateCrmDataByStrategy::Contact) {\n if (! $participant->hasName()) {\n $participant->name = $contact->name;\n }\n\n if (! $participant->hasEmailAddress()) {\n $participant->email = $contact->email;\n }\n\n if (! $participant->hasPhoneNumber()) {\n $participant->phone_number = $contact->phone;\n }\n\n $participant->contact_id = $contact->id;\n $participant->save();\n }\n }\n\n /**\n * Updates activity CRM data\n *\n * @param array{\n * Lead|null,\n * Account|null,\n * Opportunity|null,\n * Contact|null,\n * Stage|null,\n * string|null\n *} $records\n */\n public function updateActivityCrmData(array $records): void\n {\n // Extract the records.\n [$lead, $account, $opportunity, $contact, $stage] = $records;\n\n $resolver = $this->getUpdateCrmDataResolver();\n $strategy = $resolver->resolveForActivity($lead, $contact, $account);\n\n if ($strategy == UpdateCrmDataByStrategy::Lead) {\n // Also update the parent activity if required, checking we don't create a mixed lead/account record.\n if ($this->account_id === null && $this->contact_id === null && $this->lead_id === null) {\n $this->lead_id = $lead->id;\n\n if ($this->stage_id === null && $stage) {\n $this->stage_id = $stage->id;\n }\n\n $this->save();\n }\n } elseif ($strategy == UpdateCrmDataByStrategy::Contact) {\n // Also update the parent activity if required, checking we don't create a mixed lead/account record.\n $this->lead_id = null;\n if ($this->stage && $this->stage->getType() === Stage::TYPE_LEAD) {\n $this->stage_id = null;\n }\n\n // Don't trust previous matched account_id as it might have been changed in the CRM\n if ($account && $account->id !== $this->account_id) {\n $this->account_id = $account->id;\n }\n\n if ($opportunity && $this->opportunity_id !== $opportunity->id) {\n $this->opportunity_id = $opportunity->id;\n // Reset stage_id when opportunity changes to avoid stale stage from previous opportunity match\n $this->stage_id = $stage?->id;\n }\n\n if ($this->stage_id === null && $stage) {\n $this->stage_id = $stage->id;\n }\n\n if ($opportunity && $this->value !== $opportunity->value) {\n $this->value = $opportunity->value;\n }\n\n // Always set contact_id when available, regardless of account_id status\n if ($this->contact_id === null && $contact) {\n $this->contact_id = $contact->id;\n }\n\n $this->save();\n } elseif ($strategy == UpdateCrmDataByStrategy::Account && $this->account_id === null) {\n // Also update the parent activity if required, checking we don't create a mixed lead/account record.\n $this->lead_id = null;\n if ($this->stage && $this->stage->getType() === Stage::TYPE_LEAD) {\n $this->stage_id = null;\n }\n\n // Update the account and opportunity on the activity record if possible.\n $this->account_id = $account->id;\n\n if ($this->stage_id === null && $stage) {\n $this->stage_id = $stage->id;\n }\n\n if ($this->opportunity_id === null && $opportunity) {\n $this->opportunity_id = $opportunity->id;\n $this->value = $opportunity->value;\n }\n\n $this->save();\n }\n }\n\n public function getActivityProspectData(): array\n {\n return [\n 'lead' => $this->lead_id,\n 'contact' => $this->contact_id,\n 'account' => $this->account_id,\n 'opportunity' => $this->opportunity_id,\n 'stage' => $this->stage_id,\n ];\n }\n\n public function isOrganizer(User $user): bool\n {\n return $this->user_id && $this->user_id === $user->id;\n }\n\n public function isJoinable(): bool\n {\n return \\in_array($this->status, [\n self::STATUS_SCHEDULED,\n self::STATUS_PENDING,\n self::STATUS_RINGING,\n self::STATUS_IN_PROGRESS,\n ], true);\n }\n\n public function isAttemptedForBotJoin(): bool\n {\n return in_array($this->getAttribute('status'), self::MEETING_BOT_JOIN_ATTEMPTED, true);\n }\n\n /**\n * Check if the activity can be saved to CRM (manual or autolog)\n */\n public function isLoggable(): bool\n {\n if ($this->getUser()->getTeam()->hasFeature(FeatureEnum::SIDEKICK_SETTINGS)) {\n $sidekickService = app(SidekickService::class);\n\n if (! $sidekickService->isSidekickEnabledForUser($this->getUser())) {\n return false;\n }\n }\n\n // If we don't know the activity type, don't try to log.\n if ($this->playbook_category_id === null) {\n return false;\n }\n\n if ($this->user->crm_required === false) {\n return false;\n }\n\n // Don't prompt for internal meetings.\n if ($this->is_internal) {\n return false;\n }\n\n // If we don't know who we are trying to log to, don't try to log.\n if ($this->prospect === null) {\n return false;\n }\n\n $validStatus = false;\n switch ($this->type) {\n case self::TYPE_SOFTPHONE:\n case self::TYPE_SOFTPHONE_INBOUND:\n $validStatus = true;\n\n break;\n case self::TYPE_CONFERENCE:\n $validStatus = in_array($this->status, [\n self::STATUS_BUSY,\n self::STATUS_NO_ANSWER,\n self::STATUS_COMPLETED,\n self::STATUS_CANCELLED,\n ], true);\n\n break;\n case self::TYPE_SMS_INBOUND:\n case self::TYPE_SMS_OUTBOUND:\n $validStatus = in_array($this->status, [\n self::STATUS_QUEUED,\n self::STATUS_SENT,\n self::STATUS_UNDELIVERED,\n self::STATUS_DELIVERED,\n self::STATUS_RECEIVED,\n ], true);\n\n break;\n }\n\n // Depending on the activity channel, we should not try to log.\n return $validStatus;\n }\n\n public function isScheduled(): bool\n {\n return $this->status === self::STATUS_SCHEDULED;\n }\n\n public function scheduledDuration(): int\n {\n if ($this->scheduled_start_time && $this->scheduled_end_time) {\n return $this->scheduled_end_time->timestamp - $this->scheduled_start_time->timestamp;\n }\n\n return 0;\n }\n\n public function isPending(): bool\n {\n return $this->status === self::STATUS_PENDING;\n }\n\n public function isCompleted(): bool\n {\n return $this->status === self::STATUS_COMPLETED;\n }\n\n public function isRinging(): bool\n {\n return $this->status === self::STATUS_RINGING;\n }\n\n public function isInProgress(): bool\n {\n return $this->status === self::STATUS_IN_PROGRESS;\n }\n\n public function isBusy(): bool\n {\n return $this->status === self::STATUS_BUSY;\n }\n\n public function isNoAnswer(): bool\n {\n return $this->status === self::STATUS_NO_ANSWER;\n }\n\n public function isFailed(): bool\n {\n return $this->status === self::STATUS_FAILED;\n }\n\n public function isCancelled(): bool\n {\n return $this->status === self::STATUS_CANCELLED;\n }\n\n public function hasEnded(int $gracePeriodMinutes = 15): bool\n {\n if ($this->isCompleted()) {\n return true;\n }\n\n if (($this->isFailed() || $this->isCancelled()) && $this->hasScheduledEndTime()) {\n return $this->getScheduledEndTime()->addMinutes($gracePeriodMinutes)->isPast();\n }\n\n return false;\n }\n\n public function hasStarted(): bool\n {\n return $this->hasActualStartTime();\n }\n\n public function isOngoing(): bool\n {\n return $this->hasActualStartTime() && ! $this->hasActualEndTime();\n }\n\n public function isTypeSmsInbound(): bool\n {\n return $this->getType() === self::TYPE_SMS_INBOUND;\n }\n\n public function isTypeSmsOutbound(): bool\n {\n return $this->getType() === self::TYPE_SMS_OUTBOUND;\n }\n\n public function isTypeSoftPhone(): bool\n {\n return $this->getType() === self::TYPE_SOFTPHONE;\n }\n\n public function isTypeSoftphoneInbound(): bool\n {\n return $this->getType() === self::TYPE_SOFTPHONE_INBOUND;\n }\n\n public function isTypeConference(): bool\n {\n return $this->getType() === self::TYPE_CONFERENCE;\n }\n\n /**\n * Get a conference elapsed time in seconds.\n *\n * @return int seconds count\n */\n public function secondsTimeElapsed(): int\n {\n if (empty($this->actual_start_time)) {\n return 0;\n }\n\n // Get number of seconds since conference actual start time\n return (int) abs(Carbon::now()->diffInRealSeconds($this->actual_start_time));\n }\n\n /**\n * Get a conference elapsed time formatted as \"1:30:20\" if more than 1 hour or \"30:20\" otherwise.\n */\n public function formattedTimeElapsed(): string\n {\n // Get number of seconds since conference actual start time.\n $elapsedSeconds = $this->secondsTimeElapsed();\n $elapsedTime = Carbon::createFromTimestampUTC($elapsedSeconds);\n\n // Format conference start time.\n return $elapsedTime->format($elapsedSeconds < 3600 ? 'i:s' : 'G:i:s');\n }\n\n public function wasScheduled(): bool\n {\n return $this->calendarEvent !== null || in_array($this->getSource(), [self::SOURCE_OUTLOOK, self::SOURCE_GOOGLE]);\n }\n\n public function isInstant(): bool\n {\n return ! $this->wasScheduled();\n }\n\n /**\n * GETTERS AND SETTERS FOLLOW BELOW\n */\n\n public function getUuid(): string\n {\n return $this->getAttribute('id_string');\n }\n\n public function getId(): int\n {\n return $this->getAttribute('id');\n }\n\n public function getFromParticipantId(): ?int\n {\n return $this->getAttribute('from_participant_id');\n }\n\n public function getFromParticipant(): ?Participant\n {\n return $this->getAttribute('from');\n }\n\n public function getToParticipantId(): ?int\n {\n return $this->getAttribute('to_participant_id');\n }\n\n public function getToParticipant(): ?Participant\n {\n return $this->getAttribute('to');\n }\n\n public function hasScheduledStartTime(): bool\n {\n return $this->getAttribute('scheduled_start_time') !== null;\n }\n\n public function getScheduledStartTime(): ?Carbon\n {\n return $this->getAttribute('scheduled_start_time');\n }\n\n public function setScheduledStartTime(DateTimeInterface $dateTime): self\n {\n $this->setAttribute('scheduled_start_time', $dateTime);\n\n return $this;\n }\n\n public function getScheduledEndTime(): ?DateTimeInterface\n {\n return $this->getAttribute('scheduled_end_time');\n }\n\n public function hasScheduledEndTime(): bool\n {\n return $this->getAttribute('scheduled_end_time') !== null;\n }\n\n public function setScheduledEndTime(DateTimeInterface $dateTime): self\n {\n $this->setAttribute('scheduled_end_time', $dateTime);\n\n return $this;\n }\n\n public function getActualStartTime(): ?Carbon\n {\n return $this->getAttribute('actual_start_time');\n }\n\n public function hasActualStartTime(): bool\n {\n return $this->getAttribute('actual_start_time') !== null;\n }\n\n public function getActualEndTime(): ?Carbon\n {\n return $this->getAttribute('actual_end_time');\n }\n\n public function hasActualEndTime(): bool\n {\n return $this->getAttribute('actual_end_time') !== null;\n }\n\n public function getType(): ?string\n {\n return $this->getAttribute('type');\n }\n\n public function getStatus(): string\n {\n return $this->getAttribute('status');\n }\n\n public function setStatus(string $status): self\n {\n $this->setAttribute('status', $status);\n\n return $this;\n }\n\n public function setActualStartTime(DateTimeInterface $dateTime): self\n {\n $this->setAttribute('actual_start_time', $dateTime);\n\n return $this;\n }\n\n public function setActualEndTime(DateTimeInterface $dateTime, bool $shouldUpdateDuration = true): self\n {\n $this->setAttribute('actual_end_time', $dateTime);\n\n if (! $shouldUpdateDuration) {\n return $this;\n }\n\n return $this->updateDuration();\n }\n\n public function updateDuration(): self\n {\n if (! $this->hasActualStartTime() || ! $this->hasActualEndTime()) {\n return $this;\n }\n\n return $this->setDuration(\n (int) abs($this->getActualStartTime()->diffInRealSeconds($this->getActualEndTime()))\n );\n }\n\n public function setDuration(int $duration): self\n {\n $this->setAttribute('duration', $duration);\n\n return $this;\n }\n\n public function getRecordingState(): string\n {\n return $this->getAttribute('recording_state');\n }\n\n public function isRecordingState(string $recordingState): bool\n {\n return $this->getRecordingState() === $recordingState;\n }\n\n public function setRecordingState(string $recordingState): self\n {\n $this->setAttribute('recording_state', $recordingState);\n\n return $this;\n }\n\n public function hasActivityType(): bool\n {\n return $this->getAttribute('category') !== null;\n }\n\n public function getActivityType(): ?PlaybookCategory\n {\n return $this->getAttribute('category');\n }\n\n public function setActivityType(int $playbookCategoryId): self\n {\n $this->setAttribute('playbook_category_id', $playbookCategoryId);\n\n return $this;\n }\n\n public function hasStage(): bool\n {\n return $this->getAttribute('stage') !== null;\n }\n\n public function getStage(): ?Stage\n {\n return $this->getAttribute('stage');\n }\n\n public function getStageId(): ?int\n {\n return $this->getAttribute('stage_id');\n }\n\n public function setStageId(?int $stageId): void\n {\n $this->setAttribute('stage_id', $stageId);\n }\n\n public function hasOpportunity(): bool\n {\n return $this->getAttribute('opportunity') !== null;\n }\n\n public function getOpportunity(): ?Opportunity\n {\n return $this->getAttribute('opportunity');\n }\n\n public function getOpportunityId(): ?int\n {\n return $this->getAttribute('opportunity_id');\n }\n\n public function setOpportunityId(?int $opportunityId): void\n {\n $this->setAttribute('opportunity_id', $opportunityId);\n }\n\n public function hasContact(): bool\n {\n return $this->getAttribute('contact') !== null;\n }\n\n public function getContact(): ?Contact\n {\n return $this->getAttribute('contact');\n }\n\n public function getContactId(): ?int\n {\n return $this->getAttribute('contact_id');\n }\n\n public function setContactId(?int $contactId): void\n {\n $this->setAttribute('contact_id', $contactId);\n }\n\n public function hasLead(): bool\n {\n return $this->getAttribute('lead') !== null;\n }\n\n public function getLead(): ?Lead\n {\n return $this->getAttribute('lead');\n }\n\n public function getLeadId(): ?int\n {\n return $this->getAttribute('lead_id');\n }\n\n public function setLeadId(?int $leadId): void\n {\n $this->setAttribute('lead_id', $leadId);\n }\n\n public function hasAccount(): bool\n {\n return $this->getAttribute('account') !== null;\n }\n\n public function getAccount(): ?Account\n {\n return $this->getAttribute('account');\n }\n\n public function getAccountId(): ?int\n {\n return $this->getAttribute('account_id');\n }\n\n public function setAccountId(?int $accountId): void\n {\n $this->setAttribute('account_id', $accountId);\n }\n\n /**\n * This method exists to avoid confusion using ->participants() or ->participants. Use the getter instead.\n *\n * @return Collection<int, Participant>|Participant[]\n */\n public function getParticipants(): Collection\n {\n return $this->participants;\n }\n\n /**\n * @deprecated use ParticipantRepository::findParticipantRoomOwner() instead\n */\n public function findParticipantRoomOwner(): ?Participant\n {\n $roomOwnerId = $this->getUserId();\n\n return $this->getParticipants()\n ->filter(static fn (Participant $participant): bool => $participant->isSameUserId($roomOwnerId))\n ->first();\n }\n\n public function hasCrmProviderId(): bool\n {\n return $this->getAttribute('crm_provider_id') !== null;\n }\n\n public function getCrmProviderId(): ?string\n {\n return $this->getAttribute('crm_provider_id');\n }\n\n public function setCrmProviderId(?string $crmProviderId): void\n {\n $this->setAttribute('crm_provider_id', $crmProviderId);\n }\n\n public function getUserId(): ?int\n {\n return $this->getAttribute('user_id');\n }\n\n public function hasUser(): bool\n {\n return $this->user()->exists();\n }\n\n public function getUser(): User\n {\n return $this->getAttribute('user');\n }\n\n public function getCreatedAt(): Carbon\n {\n return $this->getAttribute('created_at');\n }\n\n public function isInFiniteState(): bool\n {\n return $this->isFiniteState($this->getStatus());\n }\n\n public function isFiniteState(string $status): bool\n {\n $finiteStates = self::FINITE_STATES[$this->getType()] ?? [];\n\n return in_array($status, $finiteStates, true);\n }\n\n public function getParticipant(Authenticatable $user): Participant\n {\n return $this->findParticipant($user);\n }\n\n public function findParticipant(Authenticatable $user): ?Participant\n {\n if ($user instanceof User) {\n /** @var User $user */\n return $this->participants()->where('user_id', '=', $user->getId())->first();\n }\n\n throw new LogicException(sprintf('Unsupported Authenticatable implementation %s', get_class($user)));\n }\n\n public function hasLanguageCode(): bool\n {\n return $this->getAttribute('language') !== null;\n }\n\n public function getLanguageCode(): ?string\n {\n /** @var string|null */\n return $this->getAttribute('language');\n }\n\n public function getLanguageCodeHyphenated(): string\n {\n return str_replace('_', '-', $this->getLanguageCode() ?? '');\n }\n\n public function getLanguageCodeLocale(): string\n {\n [ $language ] = explode('_', $this->getLanguageCode() ?? '');\n\n return $language;\n }\n\n public function setLanguageCode(string $value): self\n {\n return $this->setAttribute('language', $value);\n }\n\n public function hasSource(): bool\n {\n return $this->getAttribute('source') !== null;\n }\n\n public function setSource(?string $source): self\n {\n return $this->setAttribute('source', $source);\n }\n\n public function isSource(string $source): bool\n {\n return $this->getAttribute('source') === $source;\n }\n\n public function getSource(): ?string\n {\n return $this->getAttribute('source');\n }\n\n public function isSourceGong(): bool\n {\n return $this->isSource(self::SOURCE_GONG);\n }\n\n public function getExternalId(): ?string\n {\n return $this->getAttribute('external_id');\n }\n\n public function setExternalId(?string $externalId): self\n {\n return $this->setAttribute('external_id', $externalId);\n }\n\n public function hasExternalId(): bool\n {\n return $this->getAttribute('external_id') !== null;\n }\n\n public function getProvider(): string\n {\n return $this->getAttribute('provider');\n }\n\n public function hasTelephonyProviderId(): bool\n {\n return $this->getAttribute('telephony_provider_id') !== null;\n }\n\n public function getTelephonyProviderId(): ?string\n {\n return $this->getAttribute('telephony_provider_id');\n }\n\n public function setTelephonyProviderId(?string $telephonyProviderId): self\n {\n return $this->setAttribute('telephony_provider_id', $telephonyProviderId);\n }\n\n public function getLocation(): ?string\n {\n return $this->getAttribute('location');\n }\n\n public function setLocation(?string $location): self\n {\n return $this->setAttribute('location', $location);\n }\n\n public function isDeleted(): bool\n {\n return $this->getAttribute('deleted_at') !== null;\n }\n\n /**\n * Check if activity recording is on and activity status is not one of the failed statuses.\n */\n public function canReviewActivity(): bool\n {\n $failedStatuses = self::$enumFailedStatuses;\n\n return (! in_array($this->recording_state, [self::RECORDING_OFF, self::RECORDING_STOPPED], true) &&\n ! in_array($this->status, $failedStatuses, true));\n }\n\n public function hasReasonCodeBotKicked(): bool\n {\n return $this->getFlag('recording_reason_code', self::FLAG_RECORDING_REASON_MEETING_BOT_KICKED);\n }\n\n public function hasReasonCodeNotCompliant(): bool\n {\n return $this->getFlag('recording_reason_code', self::FLAG_RECORDING_REASON_CONSENT_DENIED);\n }\n\n public function hasTopicTriggers(): bool\n {\n return $this->topicTriggers()->count() !== 0;\n }\n\n public function getTopicTriggers(): Collection\n {\n return $this->topicTriggers;\n }\n\n public function getTopicTriggersSorted(): Collection\n {\n $this->loadMissing([\n 'topicTriggers.participant',\n 'topicTriggers.playbackThemeTopicTrigger',\n 'topicTriggers.playbackThemeTopicTrigger',\n 'topicTriggers.playbackThemeTopicTrigger.playbackThemeTopic',\n 'topicTriggers.playbackThemeTopicTrigger.playbackThemeTopic.playbackTheme',\n ]);\n\n return $this->topicTriggers\n ->sortBy([\n 'playbackThemeTopicTrigger.playbackThemeTopic.playbackTheme.sort',\n 'playbackThemeTopicTrigger.playbackThemeTopic.sort',\n 'playbackThemeTopicTrigger.sort',\n ]);\n }\n\n public function hasQuestions(): bool\n {\n return $this->questions()->exists();\n }\n\n public function getQuestions(): Collection\n {\n return $this->questions;\n }\n\n public function hasValue(): bool\n {\n return $this->getAttribute('value') !== null;\n }\n\n public function getValue(): ?float\n {\n return $this->getAttribute('value');\n }\n\n public function setValue(?float $value): void\n {\n $this->setAttribute('value', $value);\n }\n\n public function transitionTo(string $newState, callable $callback, ?int $timeout = null): self\n {\n $newState = $this->getWorkflowStateFor(\n $this->getType(),\n $newState\n );\n\n return $this->traitTransitionTo($newState, $callback, $timeout);\n }\n\n public function getWorkflowState(): string\n {\n return $this->getWorkflowStateFor(\n $this->getType(),\n $this->getStatus()\n );\n }\n\n public function getActivityProviderDisplayName(): string\n {\n return \\Cache::remember('activity_provider_display_name-' . $this->getProvider(), 60 * 60 * 24, function () {\n $activityProviderRegistry = app()->make(ActivityProviderRegistry::class);\n\n try {\n return $activityProviderRegistry->get($this->getProvider())->getDisplayName();\n } catch (Exception $exception) {\n return ucfirst($this->getProvider());\n }\n });\n }\n\n private function getWorkflowStateFor(string $activityChannel, string $activityStatus): string\n {\n return sprintf(\n '%s::%s',\n $activityChannel,\n $activityStatus\n );\n }\n\n public function getWorkflow(): array\n {\n $map = [\n self::TYPE_SOFTPHONE => [\n self::STATUS_SCHEDULED => [\n self::STATUS_PENDING,\n self::STATUS_IN_PROGRESS,\n self::STATUS_FAILED,\n self::STATUS_BUSY,\n ],\n self::STATUS_PENDING => [\n self::STATUS_IN_PROGRESS,\n self::STATUS_FAILED,\n self::STATUS_BUSY,\n ],\n self::STATUS_RINGING => [\n self::STATUS_CANCELLED,\n self::STATUS_FAILED,\n self::STATUS_IN_PROGRESS,\n self::STATUS_BUSY,\n ],\n self::STATUS_IN_PROGRESS => [\n self::STATUS_COMPLETED,\n ],\n ],\n self::TYPE_SOFTPHONE_INBOUND => [\n self::STATUS_RINGING => [\n self::STATUS_IN_PROGRESS,\n self::STATUS_NO_ANSWER,\n self::STATUS_CANCELLED,\n self::STATUS_FAILED,\n self::STATUS_BUSY,\n ],\n self::STATUS_IN_PROGRESS => [\n self::STATUS_COMPLETED,\n ],\n ],\n ];\n\n return collect($map)\n ->mapWithKeys(function (array $currentStates, string $activityChannel): array {\n return [\n $activityChannel => collect($currentStates)\n ->mapWithKeys(function (array $possibleStates, $currentState) use ($activityChannel): array {\n $transitionName = $this->getWorkflowStateFor($activityChannel, $currentState);\n\n return [\n $transitionName => array_map(function (string $newState) use ($activityChannel) {\n return $this->getWorkflowStateFor($activityChannel, $newState);\n }, $possibleStates),\n ];\n }),\n ];\n })\n ->reduce(static function (array $carry, Collection $item): array {\n return array_merge($carry, $item->all());\n }, []);\n }\n\n public function hasPosterPath(): bool\n {\n return $this->getAttribute('poster_path') !== null;\n }\n\n public function getPosterPath(): ?string\n {\n return $this->getAttribute('poster_path');\n }\n\n /**\n * Take into account all recording settings and determine if we need to record this activity or not.\n */\n public function shouldRecord(): bool\n {\n return $this->determineRecordingReasonCode() === null;\n }\n\n public function determineRecordingReasonCode(): ?int\n {\n // Conference specific decisions.\n if ($this->isTypeConference()) {\n // If they have manually overridden the recording setting to not record.\n if ($this->hasRecordingPreference() && $this->getRecordingPreference() === false) {\n return self::FLAG_RECORDING_REASON_PREFERENCE_OVERRIDE;\n }\n\n // If they have manually overridden the recording setting to record.\n if ($this->hasRecordingPreference() && $this->getRecordingPreference() === true) {\n return null;\n }\n\n // If their team has disabled recording meetings, don't record.\n if ($this->user->team->isConferenceRecordPreferenceDisabled()) {\n return self::FLAG_RECORDING_REASON_TEAM_AUTOMATIC_DISABLED;\n }\n\n // If the host has disabled recording meetings, don't record.\n if ($this->user->checkConferenceRecordPreference() === false) {\n return self::FLAG_RECORDING_REASON_USER_AUTOMATIC_DISABLED;\n }\n\n // If it was marked internal...\n if ($this->is_internal) {\n // and their team has disabled recording internal meetings, don't record.\n if (\n $this->user->team->isConferenceRecordPreferenceEnabled()\n && ! $this->user->team->isConferenceRecordInternalPreferenceEnabled()\n ) {\n return self::FLAG_RECORDING_REASON_TEAM_INTERNAL_DISABLED;\n }\n\n // and the host has disabled recording internal meetings, don't record.\n if ($this->user->checkConferenceRecordInternalPreference() === false) {\n return self::FLAG_RECORDING_REASON_USER_INTERNAL_DISABLED;\n }\n }\n\n // If it was not scheduled and they disabled internal meetings, we cannot determine if it was internal.\n if ($this->wasScheduled() === false && $this->user->checkConferenceRecordInternalPreference() === false) {\n return self::FLAG_RECORDING_REASON_USER_INTERNAL_DISABLED_UNSCHEDULED;\n }\n }\n\n return null;\n }\n\n public function getRecordingReasonCode(): int\n {\n return $this->getAttribute('recording_reason_code');\n }\n\n public function setRecordingReasonCode(int $recordingReasonCode): self\n {\n $this->setAttribute('recording_reason_code', $recordingReasonCode);\n\n return $this;\n }\n\n // Not used today.\n public function getRecordingReasonString(): ?string\n {\n if ($this->hasRecordingReasonCompliancePrompted()) {\n return Team::COMPLIANCE_MODE_RECORDING_PROMPT;\n }\n\n if ($this->hasRecordingReasonComplianceRestricted()) {\n return Team::COMPLIANCE_MODE_RECORDING_RESTRICT;\n }\n\n if ($this->hasRecordingReasonComplianceRestrictedToOneSideRecording()) {\n return Team::COMPLIANCE_MODE_RECORDING_RESTRICT_ONE_SIDE;\n }\n\n return null;\n }\n\n public function hasRecordingReasonComplianceRestricted(): bool\n {\n return $this->getFlag('recording_reason_code', self::FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT);\n }\n\n public function hasRecordingReasonCompliancePrompted(): bool\n {\n return $this->getFlag('recording_reason_code', self::FLAG_RECORDING_REASON_COMPLIANCE_PROMPT);\n }\n\n public function hasRecordingReasonComplianceRestrictedToOneSideRecording(): bool\n {\n return $this->getFlag('recording_reason_code', self::FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT_ONE_SIDE);\n }\n\n public function getAudioTrack(): ?Track\n {\n /** @var Track|null */\n return $this->tracks()\n ->where('type', '=', Track::TYPE_AUDIO)\n ->first();\n }\n\n public function activeParticipants(): HasMany\n {\n return $this->hasMany(Participant::class)->active();\n }\n\n public function getActiveParticipants(): Eloquent\\Collection\n {\n return $this->getAttribute('activeParticipants');\n }\n\n public function crm(): Eloquent\\Relations\\BelongsTo\n {\n return $this->belongsTo(Configuration::class, 'crm_configuration_id');\n }\n\n public function activitySummaryLogs(): HasMany\n {\n return $this->hasMany(ActivitySummaryLog::class);\n }\n\n public function getCrm(): ?Configuration\n {\n return $this->getAttribute('crm');\n }\n\n public function hasCrmConfiguration(): bool\n {\n return $this->getAttribute('crm') !== null;\n }\n\n public function isProcessed(): ?bool\n {\n return $this->getAttribute('is_processed');\n }\n\n public function hasRecordingPrompt(): bool\n {\n return $this->getAttribute('has_recording_prompt') === true;\n }\n\n public function isOnAir(): bool\n {\n return $this->getAttribute('on_air') === self::ON_AIR_READY || $this->getAttribute('on_air') === self::ON_AIR_STREAMING;\n }\n\n public function setOnAir(int $onAir): self\n {\n $this->setAttribute('on_air', $onAir);\n\n return $this;\n }\n\n public function getOnAir(): ?int\n {\n return $this->getAttribute('on_air');\n }\n\n public function setTitleFromCallData(Call $call): void\n {\n $direction = $call->isOutbound() ? 'to' : 'from';\n\n $party = $this->prospect_name\n ?? $call->getContactName()\n ?? $call->getOtherPartyPhoneNumber()\n ;\n\n $this->update(['title' => sprintf('Call %s %s', $direction, $party)]);\n }\n\n /**\n * @param array{}|array{channels:string|null, format:string|null, type:string|null, status:string|null} $audioParams\n */\n public function createAudioTrack(\n string $telephonyProviderId,\n string $recordingUrl,\n array $audioParams = []\n ): Track {\n return $this->tracks()->updateOrCreate([\n 'telephony_provider_id' => $telephonyProviderId,\n ], [\n 'type' => $audioParams['type'] ?? Track::TYPE_AUDIO,\n 'status' => $audioParams['status'] ?? Track::STATUS_PENDING,\n 'format' => $audioParams['format'] ?? Track::FORMAT_WAV,\n 'provider_content_url' => $recordingUrl,\n 'start_time' => $this->actual_start_time,\n 'end_time' => $this->actual_end_time,\n ]);\n }\n\n public function createTrack(string $telephonyProviderId, array $params): Track\n {\n return $this->tracks()->updateOrCreate(\n [\n 'telephony_provider_id' => $telephonyProviderId,\n ],\n $params\n );\n }\n\n public function createOrganiserParticipant(Call $call): Participant\n {\n $user = $this->getUser();\n\n return $this->updateOrCreateParticipant([\n 'is_ghost' => 0,\n 'name' => $user->name,\n 'email' => $user->email,\n 'phone_number' => phone_e164(null, $call->getUserPhoneNumber()),\n 'enter_time' => $this->actual_start_time,\n 'exit_time' => $this->actual_end_time,\n 'user_id' => $user->id,\n ], false);\n }\n\n public function createProspectParticipant(Call $call): Participant\n {\n // not null 'name' is mandatory here to create a separate participant with 'nameMatching'\n // in case of the same phone_number with the Organiser\n $useNameMatching = $call->getUserPhoneNumber() === $call->getOtherPartyPhoneNumber();\n $defaultName = $useNameMatching ? '' : null;\n\n return $this->updateOrCreateParticipant(data: [\n 'is_ghost' => 0,\n 'name' => $this->prospect->name ?? $defaultName,\n 'email' => $this->prospect->email ?? null,\n 'phone_number' => phone_e164(null, $call->getOtherPartyPhoneNumber()),\n 'enter_time' => $this->actual_start_time,\n 'exit_time' => $this->actual_end_time,\n 'contact_id' => $this->contact_id ?? null,\n 'lead_id' => $this->lead_id ?? null,\n ], enterRoom: false, nameMatching: $useNameMatching);\n }\n\n public function updateParticipants(Participant $organiserParticipant, Participant $prospectParticipant): void\n {\n $this->update([\n 'from_participant_id' => $this->isTypeSoftPhone() ? $organiserParticipant->id : $prospectParticipant->id,\n 'to_participant_id' => $this->isTypeSoftPhone() ? $prospectParticipant->id : $organiserParticipant->id,\n ]);\n }\n\n public function hasProspect(): bool\n {\n return $this->getProspectAttribute() !== null;\n }\n\n public function isPrivate(): bool\n {\n return $this->getAttribute('is_private');\n }\n\n /** Create a new factory instance for the model. */\n protected static function newFactory(): Factory\n {\n return ActivityFactory::new();\n }\n\n public function getUpdatedAt(): Carbon\n {\n return $this->getAttribute('updated_at');\n }\n\n public function getActivitySummaryLogs(): Eloquent\\Collection\n {\n return $this->getAttribute('activitySummaryLogs');\n }\n\n public function hasProspectActivitySummaryLog(): bool\n {\n return $this->getActivitySummaryLogs()->contains(\n 'relation_type',\n ActivitySummaryLog::RELATION_OBJECT_TYPE_PROSPECT\n );\n }\n\n public function getTeam(): Team\n {\n return $this->getUser()->getTeam();\n }\n\n private function getUpdateCrmDataResolver(): UpdateCrmDataResolverInterface\n {\n $factory = app(UpdateCrmDataResolverFactory::class);\n\n return $factory->create($this);\n }\n\n public function getMeetingTrackProviderId(string $type): string\n {\n $label = match ($type) {\n Track::TYPE_VIDEO => 'v',\n Track::TYPE_AUDIO => 'a',\n default => throw new InvalidArgumentJiminnyException('Invalid track type'),\n };\n\n $startTimestamp = $this->getScheduledStartTime()?->getTimestamp();\n $teamId = $this->getTeam()->getId();\n\n return $this->getTelephonyProviderId() . ':' . $label . ':' . $startTimestamp . ':' . $teamId;\n }\n\n /**\n * Get all consent records associated with this activity\n *\n * @return \\Illuminate\\Database\\Eloquent\\Relations\\HasMany\n */\n public function participantConsents(): HasMany\n {\n return $this->hasMany(Participant\\Consent::class);\n }\n\n public function isDiallerCall(): bool\n {\n if ($this->getProvider() === Activity::PROVIDER_UPLOADER) {\n return false;\n }\n\n if (! in_array($this->getType(), [self::TYPE_SOFTPHONE, self::TYPE_SOFTPHONE_INBOUND])) {\n return false;\n }\n\n return $this->getProvider() !== self::PROVIDER_TWILIO;\n }\n\n public function getActivityDateWithFallback(): Carbon\n {\n if ($this->getActualStartTime() !== null) {\n return $this->getActualStartTime();\n }\n\n if ($this->getScheduledStartTime() !== null) {\n return $this->getScheduledStartTime();\n }\n\n return $this->getCreatedAt();\n }\n\n public function getCrmType(): ?string\n {\n // Treat uploader activities as conferences\n if ($this->getProvider() === Activity::PROVIDER_UPLOADER) {\n return Activity::TYPE_CONFERENCE;\n }\n\n return $this->getType();\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\nnamespace Jiminny\\Models;\n\nuse Carbon\\Carbon;\nuse Database\\Factories\\ActivityFactory;\nuse DateTimeInterface;\nuse Exception;\nuse Illuminate\\Contracts\\Auth\\Authenticatable;\nuse Illuminate\\Database\\Eloquent;\nuse Illuminate\\Database\\Eloquent\\Attributes\\Scope;\nuse Illuminate\\Database\\Eloquent\\Builder;\nuse Illuminate\\Database\\Eloquent\\Factories\\Factory;\nuse Illuminate\\Database\\Eloquent\\Factories\\HasFactory;\nuse Illuminate\\Database\\Eloquent\\Relations\\BelongsTo;\nuse Illuminate\\Database\\Eloquent\\Relations\\BelongsToMany;\nuse Illuminate\\Database\\Eloquent\\Relations\\HasMany;\nuse Illuminate\\Database\\Eloquent\\Relations\\HasManyThrough;\nuse Illuminate\\Database\\Eloquent\\Relations\\HasOne;\nuse Illuminate\\Database\\Eloquent\\SoftDeletes;\nuse Illuminate\\Support\\Collection;\nuse Illuminate\\Support\\Facades\\Auth;\nuse InvalidArgumentException;\nuse Jiminny\\Component\\ElasticSearch;\nuse Jiminny\\Component\\MeetingBot;\nuse Jiminny\\Component\\Model\\BitwiseFlagTrait;\nuse Jiminny\\Component\\PlaybackPage\\Comments\\Services\\ActivityCommentService;\nuse Jiminny\\Component\\Sidekick\\SidekickService;\nuse Jiminny\\Component\\Uuid\\UuidAwareInterface;\nuse Jiminny\\Component\\Workflow;\nuse Jiminny\\Contracts;\nuse Jiminny\\Contracts\\Crm\\ProspectInterface;\nuse Jiminny\\DTO\\ImportCall\\Call;\nuse Jiminny\\Events\\Activities\\ActivityTypeUpdated;\nuse Jiminny\\Events\\Activities\\ActivityUpdated;\nuse Jiminny\\Events\\Activities\\ProspectUpdated;\nuse Jiminny\\Events\\Activities\\StageUpdated;\nuse Jiminny\\Events\\Activities\\StatusUpdated;\nuse Jiminny\\Events\\Activities\\TitleUpdated;\nuse Jiminny\\Exceptions\\InvalidArgumentException as InvalidArgumentJiminnyException;\nuse Jiminny\\Exceptions\\LogicException;\nuse Jiminny\\Exceptions\\RuntimeException;\nuse Jiminny\\Models;\nuse Jiminny\\Models\\Activity\\ActivitySummaryLog;\nuse Jiminny\\Models\\Activity\\ActivityUploadSetting;\nuse Jiminny\\Models\\Activity\\AvailabilityNotification;\nuse Jiminny\\Models\\Activity\\CoachRequest;\nuse Jiminny\\Models\\Activity\\Comment;\nuse Jiminny\\Models\\Activity\\Log;\nuse Jiminny\\Models\\Activity\\Message;\nuse Jiminny\\Models\\Activity\\Moment;\nuse Jiminny\\Models\\Activity\\Note;\nuse Jiminny\\Models\\Activity\\ParticipantSpeech;\nuse Jiminny\\Models\\Activity\\Play;\nuse Jiminny\\Models\\Activity\\Question;\nuse Jiminny\\Models\\Activity\\Share;\nuse Jiminny\\Models\\Activity\\Snapshot;\nuse Jiminny\\Models\\Activity\\Stats;\nuse Jiminny\\Models\\Activity\\SubscriptionSet;\nuse Jiminny\\Models\\Activity\\TopicTrigger;\nuse Jiminny\\Models\\Activity\\Transcription;\nuse Jiminny\\Models\\Calendar\\CalendarEvent;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Models\\Crm\\FieldData;\nuse Jiminny\\Models\\ElasticSearch\\ActivityElasticSearchTrait;\nuse Jiminny\\Models\\Feature\\FeatureEnum;\nuse Jiminny\\Models\\Participant\\Connection;\nuse Jiminny\\Models\\Playlist\\Activity as PlaylistActivity;\nuse Jiminny\\Services\\Activity\\ActivityProviderRegistry;\nuse Jiminny\\Services\\Activity\\Import\\DataResolvers\\UpdateCrmDataByStrategy;\nuse Jiminny\\Services\\Activity\\Import\\DataResolvers\\UpdateCrmDataResolverFactory;\nuse Jiminny\\Services\\Activity\\Import\\DataResolvers\\UpdateCrmDataResolverInterface;\nuse Jiminny\\Traits\\Enums;\nuse Jiminny\\Traits\\RequiresUUID;\nuse Jiminny\\Utils\\CurrencyFormatter;\nuse NumberFormatter;\n\nuse function in_array;\n\n/**\n * Jiminny\\Models\\Activity\n *\n * @property null|int $auto_score filled from ES hydrator, not in DB!\n * @property-read Account|null $account\n * @property-read CalendarEvent|null $calendarEvent\n * @property-read Contact|null $contact\n * @property-read Lead|null $lead\n * @property-read Opportunity|null $opportunity\n * @property-read Stage|null $stage\n * @property int $id\n * @property mixed|null $uuid\n * @property string|null $source\n * @property string|null $external_id\n * @property string $provider\n * @property string|null $location\n * @property string|null $telephony_provider_id\n * @property int|null $from_participant_id\n * @property int|null $to_participant_id\n * @property int|null $device_id\n * @property string|null $type\n * @property int|null $playbook_category_id\n * @property int $user_id\n * @property int|null $lead_id\n * @property int|null $account_id\n * @property int|null $contact_id\n * @property int|null $opportunity_id\n * @property int|null $stage_id\n * @property string|null $value\n * @property int|null $crm_configuration_id\n * @property string|null $crm_provider_id\n * @property string|null $language\n * @property int|null $transcription_id\n * @property int $duration\n * @property string $status\n * @property int|null $on_air\n * @property int|null $calendar_event_id\n * @property string $recording_state\n * @property bool|null $recording_preference\n * @property int $recording_reason_code\n * @property int $summary_reminder_sent\n * @property \\Illuminate\\Support\\Carbon|null $log_reminder_sent_at\n * @property \\Illuminate\\Support\\Carbon|null $organizer_notified_at\n * @property bool|null $has_recording_prompt\n * @property bool $is_internal\n * @property int $is_locked\n * @property int $is_recording\n * @property bool|null $is_processed\n * @property bool $is_private\n * @property bool $is_instant_invite\n * @property string|null $poster_path\n * @property string|null $summary\n * @property string|null $title\n * @property string|null $description\n * @property \\Illuminate\\Support\\Carbon|null $scheduled_start_time\n * @property \\Illuminate\\Support\\Carbon|null $scheduled_end_time\n * @property \\Illuminate\\Support\\Carbon|null $actual_start_time\n * @property \\Illuminate\\Support\\Carbon|null $actual_end_time\n * @property int|null $uploaded_by\n * @property \\Illuminate\\Support\\Carbon|null $deleted_at\n * @property \\Illuminate\\Support\\Carbon|null $created_at\n * @property \\Illuminate\\Support\\Carbon|null $updated_at\n * @property string|null $average_score\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Participant> $activeParticipants\n * @property-read int|null $active_participants_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Scorecard\\ActivityScorecardRuleTrigger> $activityScorecardRuleTriggers\n * @property-read int|null $activity_scorecard_rule_triggers_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Scorecard\\ActivityScorecardRule> $activityScorecardRules\n * @property-read int|null $activity_scorecard_rules_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, AvailabilityNotification> $availabilityNotifications\n * @property-read int|null $availability_notifications_count\n * @property-read \\Jiminny\\Models\\PlaybookCategory|null $category\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, CoachRequest> $coachRequests\n * @property-read int|null $coach_requests_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\CoachingFeedback> $coachingFeedbacks\n * @property-read int|null $coaching_feedbacks_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Message> $coachingMessages\n * @property-read int|null $coaching_messages_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Comment> $comments\n * @property-read int|null $comments_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Connection> $connections\n * @property-read int|null $connections_count\n * @property-read Configuration|null $crm\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, FieldData> $data\n * @property-read int|null $data_count\n * @property-read \\Jiminny\\Models\\Device|null $device\n * @property-read \\Kalnoy\\Nestedset\\Collection<int, \\Jiminny\\Models\\Playlist> $favoritePlaylists\n * @property-read int|null $favorite_playlists_count\n * @property-read \\Jiminny\\Models\\Participant|null $from\n * @property-read string|null $activity_title\n * @property-read mixed $comment_count\n * @property-read mixed $duration_for_humans\n * @property-read string $duration_for_humans_short\n * @property-read int $favorite_count\n * @property-read mixed $favorites_count\n * @property-read mixed $formatted_value\n * @property-read string $id_string\n * @property-read \\Jiminny\\Models\\Participant|null $organizer\n * @property-read mixed $play_count\n * @property-read int|null $plays_count\n * @property-read ?ProspectInterface $prospect\n * @property-read string|null $prospect_name\n * @property-read mixed $prospect_type\n * @property-read mixed $share_count\n * @property-read int|null $shares_count\n * @property-read int|null $tracks_with_telephony_count\n * @property-read int|null $visible_comments_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\CoachingFeedback> $latestCoachingFeedbacks\n * @property-read int|null $latest_coaching_feedbacks_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Log> $logs\n * @property-read int|null $logs_count\n * @property-read \\Jiminny\\Models\\Track|null $masterTrack\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Message> $messages\n * @property-read int|null $messages_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Moment> $moments\n * @property-read int|null $moments_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Note> $notes\n * @property-read int|null $notes_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Participant\\Share> $participantShares\n * @property-read int|null $participant_shares_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, ParticipantSpeech> $participantSpeeches\n * @property-read int|null $participant_speeches_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Participant\\ParticipantStats> $participantStats\n * @property-read int|null $participant_stats_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Participant> $participants\n * @property-read int|null $participants_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, PlaylistActivity> $playlistActivities\n * @property-read int|null $playlist_activities_count\n * @property-read \\Kalnoy\\Nestedset\\Collection<int, \\Jiminny\\Models\\Playlist> $playlists\n * @property-read int|null $playlists_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Play> $plays\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Question> $questions\n * @property-read int|null $questions_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Session> $sessions\n * @property-read int|null $sessions_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Share> $shares\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Snapshot> $snapshots\n * @property-read int|null $snapshots_count\n * @property-read Stats|null $stats\n * @property-read \\Jiminny\\Models\\Participant|null $to\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, TopicTrigger> $topicTriggers\n * @property-read int|null $topic_triggers_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Track> $tracks\n * @property-read int|null $tracks_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Track> $tracksWithTelephony\n * @property-read Transcription|null $transcription\n * @property-read \\Jiminny\\Models\\User $user\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Comment> $visibleComments\n *\n * @method static \\Illuminate\\Database\\Eloquent\\Collection<int, static> all($columns = ['*'])\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity chunkByIdDesc($count, callable $callback, $column = null, $alias = null)\n * @method static \\Database\\Factories\\ActivityFactory factory(...$parameters)\n * @method static \\Illuminate\\Database\\Eloquent\\Collection<int, static> get($columns = ['*'])\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity heldBetween(\\Carbon\\Carbon $start, \\Carbon\\Carbon $end)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity idOrUuId($idOrUuid, bool $first = true)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity newModelQuery()\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity newQuery()\n * @method static Builder|Activity onlyTrashed()\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity query()\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity scheduledBetween(\\Carbon\\Carbon $start, \\Carbon\\Carbon $end)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity inOpenDeals()\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity notInOpenDeals()\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity forTeam(int $teamId)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity search(callable $searchQuery, $key = null, $sortByResults = true)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity uuid(string $uuid, bool $first = true)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereAccountId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereActualEndTime($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereActualStartTime($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereAverageScore($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereCalendarEventId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereContactId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereCreatedAt($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereCrmConfigurationId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereCrmProviderId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereDeletedAt($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereDescription($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereDeviceId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereDuration($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereFromParticipantId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereHasRecordingPrompt($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereIsInstantInvite($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereIsInternal($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereIsLocked($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereIsPrivate($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereIsProcessed($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereIsRecording($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereLanguage($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereLeadId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereLocation($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereLogReminderSentAt($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereOnAir($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereOpportunityId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereOrganizerNotifiedAt($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity wherePlaybookCategoryId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity wherePosterPath($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereProvider($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereRecordingPreference($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereRecordingReasonCode($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereRecordingState($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereScheduledEndTime($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereScheduledStartTime($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereSource($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereExternalId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereStageId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereStatus($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereSummary($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereSummaryReminderSent($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereTelephonyProviderId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereTitle($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereToParticipantId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereTranscriptionId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereType($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereUpdatedAt($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereUploadedBy($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereUserId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereUuid($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereValue($value)\n * @method static Builder|Activity withTrashed()\n * @method static Builder|Activity withoutTrashed()\n *\n * @mixin \\Eloquent\n */\nclass Activity extends Model implements\n ElasticSearch\\Contract\\Searchable,\n Workflow\\Workflow\\WorkflowAwareInterface,\n Models\\Contracts\\ActivityContract,\n Contracts\\Model\\ActivityInterface,\n UuidAwareInterface\n{\n use HasFactory;\n\n use Enums;\n use SoftDeletes;\n use RequiresUUID;\n use BitwiseFlagTrait;\n use ElasticSearch\\Model\\Searchable;\n use ActivityElasticSearchTrait;\n\n use Workflow\\Workflow\\WorkflowAware {\n transitionTo as traitTransitionTo;\n }\n\n public const int FLAG_RECORDING_REASON_DEFAULT = 0;\n\n // Recording Prompted but never started\n public const int FLAG_RECORDING_REASON_COMPLIANCE_PROMPT = 1;\n public const int FLAG_RECORDING_REASON_COMPLIANCE_RESUMED = 2;\n public const int FLAG_RECORDING_REASON_NO_AUDIO = 3;\n\n // Recording Disabled by Organization\n public const int FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT = 4;\n\n // Recording was restricted to one-side recordings only\n public const int FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT_ONE_SIDE = 8;\n\n // Recording was not started because it was internal and team setting disabled that.\n public const int FLAG_RECORDING_REASON_TEAM_INTERNAL_DISABLED = 16;\n\n // Recording was not started because it was internal and user setting disabled that.\n public const int FLAG_RECORDING_REASON_USER_INTERNAL_DISABLED = 32;\n\n // Recording was not started because user setting disabled automatic recording.\n public const int FLAG_RECORDING_REASON_USER_AUTOMATIC_DISABLED = 64;\n\n // Recording was not started because team setting disabled automatic recording.\n public const int FLAG_RECORDING_REASON_TEAM_AUTOMATIC_DISABLED = 128;\n\n // Recording was not started because user has overriden default.\n public const int FLAG_RECORDING_REASON_PREFERENCE_OVERRIDE = 256;\n\n // Recording was not started because they don't want internal, and this meeting was not scheduled/imported in time.\n public const int FLAG_RECORDING_REASON_USER_INTERNAL_DISABLED_UNSCHEDULED = 512;\n\n // Recording was not started because their team setting does excludes the meeting type.\n public const int FLAG_RECORDING_REASON_UNSUPPORTED_TYPE = 1024;\n\n // Recording was not started because the external provider disabled it (or recording is missing etc).\n public const int FLAG_RECORDING_REASON_EXTERNALLY_DISABLED = 2048;\n\n // Recording was stopped externally (\"exit-meeting\" Pusher event)\n public const int FLAG_RECORDING_REASON_STOPPED_EXTERNALLY = 384;\n\n // Recording couldn't be started due to Zoom hosting conflict error\n public const int FLAG_RECORDING_REASON_HOSTING_CONFLICT = 448;\n\n // meeting.failed event with reason code BOT_DENIED_FROM_LOBBY\n public const int FLAG_RECORDING_REASON_MEETING_BOT_DENIED_FROM_LOBBY = 4096;\n\n // meeting.failed event with reason code LOBBY_TIMEOUT\n public const int FLAG_RECORDING_REASON_MEETING_BOT_LOBBY_TIMEOUT = 8192;\n\n // meeting.failed event with reason code BOT_KICKED\n public const int FLAG_RECORDING_REASON_MEETING_BOT_KICKED = 16384;\n\n // meeting.failed event with reason code UNKNOWN\n public const int FLAG_RECORDING_REASON_MEETING_BOT_UNKNOWN = 32768;\n\n public const int FLAG_RECORDING_REASON_CONSENT_DENIED = 65536;\n\n // Invalid meeting (e.g. URL is invalid, or the meeting is not found)\n public const int FLAG_RECORDING_REASON_MEETING_BOT_INVALID = 131072;\n\n // The host stopped the recording.\n public const int FLAG_RECORDING_REASON_USER_STOPPED = 262144;\n\n // Recording was not started because an alternative vendor disabled it (or overrode it).\n public const int FLAG_RECORDING_REASON_VENDOR_OVERRIDE = 1048576;\n\n // Login required meeting.failed code\n public const int FLAG_RECORDING_REASON_LOGIN_REQUIRED = 524288;\n\n // Password for meeting was not provided - meeting.failed code\n public const int FLAG_RECORDING_REASON_MEETING_PASSWORD_NOT_PROVIDED = 2097152;\n\n // meeting.failed - when the meeting is locked\n public const int FLAG_RECORDING_REASON_MEETING_IS_LOCKED = 4194304;\n\n // max recording duration reached\n public const int FLAG_RECORDING_REASON_MAX_DURATION_REACHED = 8388608;\n\n // recording size is too small\n public const int FLAG_RECORDING_REASON_EMPTY_RECORDING = 16777216;\n\n // meeting.failed - when bot is redirected to sign in page multiple times\n public const int FLAG_RECORDING_REASON_MAX_RESTART_COUNT_IS_REACHED = 33554432;\n\n // meeting.failed event with reason code CONNECTION_LOST\n public const int FLAG_RECORDING_REASON_MEETING_BOT_CONNECTION_LOST = 67108864;\n\n // recording is corrupted.\n public const int FLAG_RECORDING_REASON_MEDIA_FILE_UNSUPPORTED_MIME_TYPE = 134217728;\n\n // meeting ended in lobby\n public const int FLAG_RECORDING_REASON_MEETING_ENDED_IN_LOBBY = 268435456;\n\n // meeting not started\n public const int FLAG_RECORDING_REASON_REASON_MEETING_NOT_STARTED = 536870912;\n\n // unfinished zoom custom disclaimer\n public const int FLAG_RECORDING_REASON_FEATURE_RULE_NOT_FOUND_ERROR = 1073741824;\n\n // recording download failed - server error\n public const int FLAG_RECORDING_REASON_SERVER_ERROR = 2147483648;\n\n // recording download failed - client code 404\n public const int FLAG_RECORDING_REASON_NOT_FOUND = 2147483649;\n\n // recording download failed - client code 401, 403\n public const int FLAG_RECORDING_REASON_ACCESS_DENIED = 2147483650;\n\n // recording download failed - client code 429\n public const int FLAG_RECORDING_REASON_TOO_MANY_REQUESTS = 2147483651;\n\n // recording download failed - unknown client error\n public const int FLAG_RECORDING_REASON_CLIENT_ERROR = 2147483652;\n\n // recording download failed - unknown error\n public const int FLAG_RECORDING_REASON_UNKNOWN_ERROR = 2147483653;\n\n // It has been setup ahead of time through calendar\n public const string STATUS_SCHEDULED = 'scheduled';\n\n // It is awaiting audio.\n public const string STATUS_PENDING = 'pending';\n\n // Participant(s) dialed in, awaiting organizer.\n public const string STATUS_RINGING = 'ringing';\n\n // Call is in progress.\n public const string STATUS_IN_PROGRESS = 'in-progress';\n\n // It has ended.\n public const string STATUS_COMPLETED = 'completed';\n\n // Cancelled prior to starting.\n public const string STATUS_CANCELLED = 'canceled';\n\n public const string STATUS_DUPLICATED = 'duplicated'; // duplicated conference\n\n public const string STATUS_STARTING_SOON = 'starting-soon';\n\n public const string STATUS_BOT_CREATE_SENT = 'bot-create-sent';\n\n public const string STATUS_BOT_INSTANCE_WORKER_ASSIGNED = 'worker-assigned';\n\n public const string STATUS_BOT_INSTANCE_STARTED = 'bot-started';\n\n // When bot instance is waiting in lobby\n public const string STATUS_BOT_INSTANCE_WAITING_LOBBY = 'bot-waiting';\n\n public const string STATUS_BUSY = 'busy';\n public const string STATUS_NO_ANSWER = 'no-answer';\n public const string STATUS_FAILED = 'failed'; // Used by SMS too\n\n // SMS related\n public const string STATUS_ACCEPTED = 'accepted';\n public const string STATUS_QUEUED = 'queued';\n public const string STATUS_SENDING = 'sending';\n public const string STATUS_SENT = 'sent';\n public const string STATUS_DELIVERED = 'delivered';\n public const string STATUS_UNDELIVERED = 'undelivered';\n public const string STATUS_RECEIVING = 'receiving';\n public const string STATUS_RECEIVED = 'received';\n public const string STATUS_RESENT = 'resent';\n\n public const array SMS_STATUSES = [\n Activity::STATUS_RECEIVED,\n Activity::STATUS_SENT,\n Activity::STATUS_DELIVERED,\n ];\n\n public const array SOFT_PHONE_CONFERENCE_STATUSES = [\n Activity::STATUS_IN_PROGRESS,\n Activity::STATUS_COMPLETED,\n ];\n\n // @todo refactor prefix from `TYPE_` to `CHANNEL_`\n public const string TYPE_SOFTPHONE = 'softphone';\n public const string TYPE_SOFTPHONE_INBOUND = 'softphone-inbound';\n public const string TYPE_CONFERENCE = 'conference';\n public const string TYPE_SMS_INBOUND = 'sms-inbound';\n public const string TYPE_SMS_OUTBOUND = 'sms-outbound';\n public const string TYPE_EMAIL_INBOUND = 'email-inbound';\n public const string TYPE_EMAIL_OUTBOUND = 'email-outbound';\n\n public const array CHANNELS = [\n self::TYPE_SOFTPHONE,\n self::TYPE_SOFTPHONE_INBOUND,\n self::TYPE_CONFERENCE,\n self::TYPE_SMS_INBOUND,\n self::TYPE_SMS_OUTBOUND,\n self::TYPE_EMAIL_INBOUND,\n self::TYPE_EMAIL_OUTBOUND,\n ];\n\n public const array PLAYABLE_CHANNELS = [\n self::TYPE_SOFTPHONE,\n self::TYPE_SOFTPHONE_INBOUND,\n self::TYPE_CONFERENCE,\n ];\n\n // Recording States\n public const string RECORDING_OFF = 'off'; // Default state\n public const string RECORDING_IN_PROGRESS = 'in-progress';\n public const string RECORDING_PAUSED = 'paused';\n public const string RECORDING_STOPPED = 'stopped'; // To never be resumed.\n public const string RECORDING_RECORDED = 'recorded'; // At least some portion of it was recorded.\n public const string RECORDING_FAILED = 'failed'; // Recording was attempted but failed for some reason.\n\n // Live Stream States\n public const int ON_AIR_DEFAULT = 0;\n public const int ON_AIR_READY = 1;\n public const int ON_AIR_PREPARING = 2;\n public const int ON_AIR_STREAMING = 3;\n public const int ON_AIR_FINISHED = 4;\n public const int ON_AIR_NOT_STREAMED = 5;\n public const int ON_AIR_ERROR = -1;\n\n public const string SOURCE_GONG = 'gong';\n public const string SOURCE_CHORUS = 'chorus';\n public const string SOURCE_OUTLOOK = 'outlook';\n public const string SOURCE_GOOGLE = 'google';\n\n // Activity Providers\n public const string PROVIDER_TWILIO = 'twilio'; // XXX: This is run via the Jiminny Provider.\n public const string PROVIDER_OUTREACH = 'outreach';\n public const string PROVIDER_ZOOM_BOT = 'zoom-bot';\n public const string PROVIDER_SALESLOFT = 'salesloft';\n public const string PROVIDER_GOOGLE = 'google';\n public const string PROVIDER_AIRCALL = 'aircall';\n public const string PROVIDER_JUSTCALL = 'justcall';\n public const string PROVIDER_GOOGLE_MEET = 'google-meet';\n public const string PROVIDER_GONG = 'gong';\n public const string PROVIDER_HUBSPOT = 'hubspot';\n public const string PROVIDER_CLOSE = 'close';\n public const string PROVIDER_TEAMS = 'ms-teams';\n public const string PROVIDER_SALESFORCE = 'salesforce';\n public const string PROVIDER_GROOVE = 'groove';\n public const string PROVIDER_XANT = 'xant';\n public const string PROVIDER_OFFICE = 'office';\n public const string PROVIDER_NATTERBOX = 'natterbox';\n public const string PROVIDER_RINGCENTRAL = 'ringcentral';\n public const string PROVIDER_RINGCENTRAL_VIDEO = 'ringcentral-video';\n public const string PROVIDER_GOTOMEETING = 'go-to-meeting';\n public const string PROVIDER_DEMODESK = 'demo-desk';\n public const string PROVIDER_DIALPAD = 'dialpad';\n public const string PROVIDER_ZOOM_PHONE = 'zoom-phone';\n public const string PROVIDER_CLOUDCALL = 'cloudcall';\n public const string PROVIDER_CLOUDCALL_US = 'cloudcall-us';\n public const string PROVIDER_EIGHT_BY_EIGHT = 'eight-by-eight'; // \"8x8\" UK\n public const string PROVIDER_EIGHT_BY_EIGHT_CA = 'eight-by-eight-ca'; // \"8x8\" Canada\n public const string PROVIDER_EIGHT_BY_EIGHT_AP = 'eight-by-eight-ap'; // \"8x8\" Australia\n public const string PROVIDER_EIGHT_BY_EIGHT_US_EAST = 'eight-by-eight-use'; // \"8x8\" US East\n public const string PROVIDER_EIGHT_BY_EIGHT_US_WEST = 'eight-by-eight-usw'; // \"8x8\" US West\n public const string PROVIDER_CONNECT_AND_SELL = 'connect-and-sell';\n public const string PROVIDER_CLOUD_TALK = 'cloud-talk';\n public const string PROVIDER_AMAZON_CONNECT = 'amazon-connect';\n public const string PROVIDER_VONAGE = 'vonage';\n public const string PROVIDER_MIGRATOR = 'migrator';\n public const string PROVIDER_UPLOADER = 'uploader';\n public const string PROVIDER_TALKDESK = 'talkdesk';\n public const string PROVIDER_TWILIO_FLEX = 'twilio-flex';\n public const string PROVIDER_TWILIO_FLEX_DIRECT = 'twilio-flex-direct';\n public const string PROVIDER_TWILIO_VIDEO = 'twilio-video';\n public const string PROVIDER_AVAYA = 'avaya';\n public const string PROVIDER_TELUS = 'telus';\n public const string PROVIDER_FIVE_NINE = 'five-nine';\n public const string PROVIDER_APOLLO = 'apollo';\n public const string PROVIDER_ORUM = 'orum';\n public const string PROVIDER_BLOOBIRDS = 'bloobirds';\n\n /**\n * @const API_PROVIDERS\n * A list of integrations that import calls via API instead of webhooks\n */\n public const array API_PROVIDERS = [\n self::PROVIDER_OUTREACH,\n self::PROVIDER_SALESLOFT,\n self::PROVIDER_HUBSPOT,\n self::PROVIDER_GROOVE,\n self::PROVIDER_XANT,\n self::PROVIDER_NATTERBOX,\n self::PROVIDER_CLOUDCALL,\n self::PROVIDER_CLOUDCALL_US,\n self::PROVIDER_EIGHT_BY_EIGHT,\n self::PROVIDER_EIGHT_BY_EIGHT_CA,\n self::PROVIDER_EIGHT_BY_EIGHT_AP,\n self::PROVIDER_EIGHT_BY_EIGHT_US_EAST,\n self::PROVIDER_EIGHT_BY_EIGHT_US_WEST,\n self::PROVIDER_CONNECT_AND_SELL,\n self::PROVIDER_CLOUD_TALK,\n self::PROVIDER_AMAZON_CONNECT,\n self::PROVIDER_VONAGE,\n self::PROVIDER_TALKDESK,\n self::PROVIDER_TWILIO_VIDEO,\n self::PROVIDER_TWILIO_FLEX,\n self::PROVIDER_TWILIO_FLEX_DIRECT,\n self::PROVIDER_FIVE_NINE,\n self::PROVIDER_APOLLO,\n self::PROVIDER_ORUM,\n self::PROVIDER_BLOOBIRDS,\n self::PROVIDER_RINGCENTRAL,\n self::PROVIDER_AVAYA,\n self::PROVIDER_TELUS,\n ];\n\n public const array FINITE_STATES = [\n self::TYPE_SOFTPHONE => [\n self::STATUS_COMPLETED,\n self::STATUS_FAILED,\n self::STATUS_NO_ANSWER,\n self::STATUS_BUSY,\n ],\n self::TYPE_SOFTPHONE_INBOUND => [\n self::STATUS_COMPLETED,\n self::STATUS_FAILED,\n self::STATUS_NO_ANSWER,\n self::STATUS_BUSY,\n ],\n self::TYPE_CONFERENCE => self::FINITE_STATES_CONFERENCE,\n ];\n\n public const array FINITE_STATES_CONFERENCE = [\n self::STATUS_COMPLETED,\n self::STATUS_FAILED,\n self::STATUS_CANCELLED,\n ];\n\n public const array MEETING_BOT_JOIN_ATTEMPTED = [\n self::STATUS_BOT_INSTANCE_WAITING_LOBBY,\n self::STATUS_BOT_INSTANCE_STARTED,\n ];\n\n public static array $enumStatuses = [\n self::STATUS_SCHEDULED,\n self::STATUS_PENDING,\n self::STATUS_RINGING,\n self::STATUS_IN_PROGRESS,\n self::STATUS_COMPLETED,\n self::STATUS_CANCELLED,\n self::STATUS_BUSY,\n self::STATUS_NO_ANSWER,\n self::STATUS_FAILED,\n self::STATUS_ACCEPTED,\n self::STATUS_QUEUED,\n self::STATUS_SENDING,\n self::STATUS_SENT,\n self::STATUS_RESENT,\n self::STATUS_DELIVERED,\n self::STATUS_UNDELIVERED,\n self::STATUS_RECEIVING,\n self::STATUS_RECEIVED,\n self::STATUS_BOT_INSTANCE_WAITING_LOBBY,\n self::STATUS_STARTING_SOON,\n self::STATUS_BOT_INSTANCE_WORKER_ASSIGNED,\n self::STATUS_BOT_INSTANCE_STARTED,\n self::STATUS_DUPLICATED,\n ];\n\n public static array $enumProviders = [\n self::PROVIDER_TWILIO,\n self::PROVIDER_OUTREACH,\n self::PROVIDER_ZOOM_BOT,\n self::PROVIDER_SALESLOFT,\n self::PROVIDER_AIRCALL,\n self::PROVIDER_JUSTCALL,\n self::PROVIDER_GOOGLE_MEET,\n self::PROVIDER_GONG,\n self::PROVIDER_HUBSPOT,\n self::PROVIDER_CLOSE,\n self::PROVIDER_TEAMS,\n self::PROVIDER_SALESFORCE,\n self::PROVIDER_GROOVE,\n self::PROVIDER_XANT,\n self::PROVIDER_GOOGLE,\n self::PROVIDER_OFFICE,\n self::PROVIDER_NATTERBOX,\n self::PROVIDER_RINGCENTRAL,\n self::PROVIDER_RINGCENTRAL_VIDEO,\n self::PROVIDER_GOTOMEETING,\n self::PROVIDER_DEMODESK,\n self::PROVIDER_DIALPAD,\n self::PROVIDER_ZOOM_PHONE,\n self::PROVIDER_CLOUDCALL,\n self::PROVIDER_CLOUDCALL_US,\n self::PROVIDER_EIGHT_BY_EIGHT,\n self::PROVIDER_EIGHT_BY_EIGHT_CA,\n self::PROVIDER_EIGHT_BY_EIGHT_AP,\n self::PROVIDER_EIGHT_BY_EIGHT_US_EAST,\n self::PROVIDER_EIGHT_BY_EIGHT_US_WEST,\n self::PROVIDER_CONNECT_AND_SELL,\n self::PROVIDER_CLOUD_TALK,\n self::PROVIDER_AMAZON_CONNECT,\n self::PROVIDER_VONAGE,\n self::PROVIDER_TALKDESK,\n self::PROVIDER_TWILIO_FLEX,\n self::PROVIDER_TWILIO_FLEX_DIRECT,\n self::PROVIDER_TWILIO_VIDEO,\n self::PROVIDER_AVAYA,\n self::PROVIDER_TELUS,\n self::PROVIDER_FIVE_NINE,\n self::PROVIDER_APOLLO,\n self::PROVIDER_ORUM,\n self::PROVIDER_BLOOBIRDS,\n ];\n\n public static $enumRecordingStates = [\n self::RECORDING_OFF, // Default state\n self::RECORDING_IN_PROGRESS,\n self::RECORDING_PAUSED,\n self::RECORDING_STOPPED,\n self::RECORDING_RECORDED,\n self::RECORDING_FAILED,\n ];\n\n // @Important:\n // This collection is not used anywhere, and is fully duplicated by the Channels const.\n // Validate if it is referred somehow via the enum trait, and if not, remove it entirely.\n // An even better strategy will be to move all those constants to a dedicated class\n protected array $enumTypes = [\n self::TYPE_SOFTPHONE,\n self::TYPE_SOFTPHONE_INBOUND,\n self::TYPE_CONFERENCE,\n self::TYPE_SMS_INBOUND,\n self::TYPE_SMS_OUTBOUND,\n self::TYPE_EMAIL_INBOUND,\n self::TYPE_EMAIL_OUTBOUND,\n ];\n\n protected static $enumFailedStatuses = [\n self::STATUS_NO_ANSWER,\n self::STATUS_FAILED,\n self::STATUS_BUSY,\n self::STATUS_CANCELLED,\n ];\n\n protected $table = 'activities';\n\n protected $fillable = [\n // Type of activity.\n 'type', // @todo refactor to `channel`\n // The activity type.\n 'playbook_category_id',\n // User who hosts the activity.\n 'user_id',\n // Related Lead record (if applicable)\n 'lead_id',\n // Related Account record (if applicable)\n 'account_id',\n // Related Contact record (if applicable)\n 'contact_id',\n // Related Opportunity record (if applicable)\n 'opportunity_id',\n // Stage of activity.\n 'stage_id',\n // Value of opportunity.\n 'value',\n // If the activity relates to a CRM task.\n 'crm_provider_id',\n // If the activity was created through an external device.\n 'device_id',\n // the activity's language code\n 'language',\n // transcription id\n 'transcription_id',\n // Duration of the call, with microseconds precision.\n 'duration',\n // One of enumStatuses above.\n 'status',\n // Have we reminded them to log the call?\n 'log_reminder_sent_at',\n // If activity is private or inter-org, flagged here.\n 'is_internal',\n // Managers and above can mark a call as private, to exclude it from other team members\n 'is_private',\n 'is_processed',\n // Boolean for this activity being instant invite handled.\n 'is_instant_invite',\n // If activity is in recording state, flagged here.\n 'recording_state',\n // If activity recording is overidden from default.\n 'recording_preference',\n // if recording did (not) happen, why that is\n 'recording_reason_code',\n // Average score, updated during\n 'average_score',\n // Summary that the organizer has taken after the call.\n 'summary',\n // Subject of the activity, usually taken from calendar event.\n 'title',\n // Description of the activity, usually taken from calendar event.\n 'description',\n // Start time, usually taken from calendar event.\n 'scheduled_start_time',\n // End time, usually taken from calendar event.\n 'scheduled_end_time',\n // When the call actually started.\n 'actual_start_time',\n // When the call actually ended.\n 'actual_end_time',\n // SMS: Message reference\n 'telephony_provider_id',\n // SMS: Participant who sent message\n 'from_participant_id',\n // SMS: Participant who should receive the message\n 'to_participant_id',\n // When an external guest joins an organizers meeting room and the organizer is not present,\n // send them an SMS notification that someone has joined.\n 'organizer_notified_at',\n // where was the activity imported from\n 'source',\n // The id in the source system (e.g. the bot id in Recall.ai)\n 'external_id',\n // The provider, by default it is twilio.\n 'provider',\n // Meeting location url\n 'location',\n // The snapshot for displaying a poster image.\n 'poster_path',\n 'crm_configuration_id',\n // If there is an automated message that the conversation is being recorded\n 'has_recording_prompt',\n // If the activity is being live-streamed\n 'on_air',\n 'calendar_event_id',\n ];\n\n protected $appends = [\n 'id_string',\n 'organizer',\n ];\n\n protected $hidden = [\n 'uuid',\n ];\n\n protected $visible = [\n 'id_string',\n 'type',\n 'duration',\n 'average_score',\n 'status',\n 'log_reminder_sent_at',\n 'title',\n 'description',\n 'is_internal',\n 'scheduled_start_time',\n 'scheduled_end_time',\n 'actual_start_time',\n 'actual_end_time',\n 'user',\n 'category',\n 'account',\n 'contact',\n 'opportunity',\n 'lead',\n 'stage',\n 'stats',\n 'participants',\n 'playlists',\n 'tracks',\n 'comments',\n 'plays',\n 'coachingFeedbacks',\n 'shares',\n 'favorites',\n 'language',\n 'transcription',\n 'is_private',\n 'is_instant_invite',\n 'on_air',\n 'calendar_event_id',\n ];\n\n protected function casts(): array\n {\n return [\n 'scheduled_start_time' => 'datetime',\n 'scheduled_end_time' => 'datetime',\n 'actual_start_time' => 'datetime',\n 'actual_end_time' => 'datetime',\n 'organizer_notified_at' => 'datetime',\n 'log_reminder_sent_at' => 'datetime',\n 'is_internal' => 'boolean',\n 'duration' => 'integer',\n 'average_score' => 'decimal:2',\n 'is_private' => 'boolean',\n 'is_processed' => 'boolean',\n 'is_instant_invite' => 'boolean',\n 'value' => 'decimal:2',\n 'recording_preference' => 'boolean',\n 'recording_reason_code' => 'integer',\n 'has_recording_prompt' => 'boolean',\n 'on_air' => 'integer',\n ];\n }\n\n protected static function boot()\n {\n parent::boot();\n\n static::updated(static function (Activity $activity) {\n // If activity is about to start (pending, ringing, in-progress) or event is scheduled in less than 1 week\n if (in_array($activity->status, [Activity::STATUS_PENDING, Activity::STATUS_RINGING, Activity::STATUS_IN_PROGRESS], true) ||\n ($activity->scheduled_start_time && (int) $activity->scheduled_start_time->diffInWeeks(new Carbon(), true) < 1)) {\n if ($activity->isDirty('status')) {\n event(new StatusUpdated($activity));\n }\n\n if ($activity->isDirty('stage_id')) {\n event(new StageUpdated($activity));\n }\n\n if ($activity->isDirty(['lead_id', 'account_id', 'contact_id'])) {\n event(new ProspectUpdated($activity));\n }\n\n if ($activity->isDirty('opportunity_id')) {\n event(new ActivityUpdated($activity, 'activity.opportunity-updated', Auth::user()));\n }\n\n if ($activity->isDirty('title')) {\n event(new TitleUpdated($activity));\n }\n }\n\n if ($activity->isDirty('playbook_category_id')) {\n event(new ActivityTypeUpdated($activity));\n }\n });\n\n static::deleted(static function (Activity $activity) {\n // Hard delete associated playlistActivities\n $activity->playlistActivities()->delete();\n });\n }\n\n public function getOrganizerAttribute(): ?Participant\n {\n $participant = $this->participants()->where('user_id', $this->user_id)->first();\n\n if (! $participant instanceof Participant && $participant !== null) {\n throw new RuntimeException(sprintf('$participant must be an instance of \"%s\" or null', Participant::class));\n }\n\n return $participant;\n }\n\n public function getFormattedValueAttribute()\n {\n $currencyCode = 'USD';\n if ($this->opportunity) {\n $currencyCode = $this->opportunity->getCurrencyCode();\n }\n\n $formatter = new CurrencyFormatter();\n $formatter->setTextAttribute(NumberFormatter::CURRENCY_CODE, $currencyCode);\n $formatter->setAttribute(NumberFormatter::MAX_FRACTION_DIGITS, 0);\n\n return $formatter->format($this->value, $currencyCode);\n }\n\n public function getProspectNameAttribute(): ?string\n {\n $prospectName = null;\n\n if ($this->lead_id) {\n $prospectName = $this->lead->name;\n } elseif ($this->contact_id) {\n $prospectName = $this->contact->name;\n } elseif ($this->account_id) {\n $prospectName = $this->account->name;\n }\n\n return $prospectName;\n }\n\n public function getProspectName(): ?string\n {\n /** @var string|null */\n return $this->getAttribute('prospect_name');\n }\n\n /**\n * Get activity title depending on prospect or title\n */\n public function getActivityTitleAttribute(): ?string\n {\n $activityTitle = null;\n if ($this->prospect && $this->prospect->getName()) {\n if ($this->account_id) {\n $activityTitle = $this->account->name;\n } elseif ($this->lead_id) {\n $activityTitle = $this->lead->company;\n } elseif ($this->contact_id) {\n $activityTitle = $this->contact->account ? $this->contact->account->name : $this->contact->name;\n }\n } elseif ($this->title) {\n $activityTitle = $this->title;\n }\n\n return $activityTitle;\n }\n\n public function wasRecentlyCreated(): bool\n {\n return $this->wasRecentlyCreated;\n }\n\n public function getProspectTypeAttribute()\n {\n $prospectType = null;\n\n if ($this->lead_id) {\n $prospectType = 'Lead';\n } elseif ($this->contact_id) {\n $prospectType = 'Contact';\n } elseif ($this->account_id) {\n $prospectType = 'Account';\n }\n\n return $prospectType;\n }\n\n /**\n * Return the best match for prospect. Results are in the following order of priority:\n * 1. Lead\n * 2. Contact\n * 3. Account\n * 4. NULL\n */\n public function getProspectAttribute(): ?ProspectInterface\n {\n if ($this->hasLead()) {\n return $this->getLead();\n }\n\n if ($this->hasContact()) {\n return $this->getContact();\n }\n\n if ($this->hasAccount()) {\n return $this->getAccount();\n }\n\n return null;\n }\n\n public function getTitleAttribute($value): ?string\n {\n return \\getActivityTitleAttribute(\n $this->user->name,\n $this->getType(),\n $value,\n $this->prospect->name ?? null,\n $this->from->national_phone_number ?? null\n );\n }\n\n public function getTitle(): ?string\n {\n return $this->getAttribute('title');\n }\n\n public function getSummary(): ?string\n {\n return $this->getAttribute('summary');\n }\n\n public function isInternal(): bool\n {\n return $this->getAttribute('is_internal');\n }\n\n public function getIsPrivate(): bool\n {\n return $this->getAttribute('is_private');\n }\n\n public function getDescription(): ?string\n {\n return $this->getAttribute('description');\n }\n\n public function hasTitle(): bool\n {\n return $this->getOriginal('title') !== null;\n }\n\n public function getPlayCountAttribute()\n {\n return $this->getPlaysCountAttribute();\n }\n\n public function getPlaysCountAttribute()\n {\n if (! isset($this->attributes['plays_count'])) {\n $this->loadCount('plays');\n }\n\n return $this->attributes['plays_count'];\n }\n\n public function getCommentCountAttribute()\n {\n return $this->getCommentsCountAttribute();\n }\n\n public function getCommentsCountAttribute()\n {\n if (! isset($this->attributes['comments_count'])) {\n $this->loadCount('comments');\n }\n\n return $this->attributes['comments_count'];\n }\n\n public function getVisibleCommentsCountAttribute()\n {\n if (! isset($this->attributes['visible_comments_count'])) {\n $activityCommentsService = app(ActivityCommentService::class);\n $user = Auth::user() instanceof User ? Auth::user() : null;\n $this->attributes['visible_comments_count'] = $activityCommentsService\n ->getVisibleCommentsCount($this, $user);\n }\n\n return $this->attributes['visible_comments_count'];\n }\n\n public function getShareCountAttribute()\n {\n return $this->getSharesCountAttribute();\n }\n\n public function getSharesCountAttribute()\n {\n if (! isset($this->attributes['shares_count'])) {\n $this->loadCount('shares');\n }\n\n return $this->attributes['shares_count'];\n }\n\n\n /**\n * Get the count of favorites playlists this activity appears in\n */\n public function getFavoriteCountAttribute(): int\n {\n return $this->getFavoritesCountAttribute();\n }\n\n public function getFavoritesCountAttribute()\n {\n if (! isset($this->attributes['favorites_count'])) {\n $this->loadCount('favorites');\n }\n\n return $this->attributes['favorites_count'];\n }\n\n public function getActiveParticipantsCountAttribute()\n {\n if (! isset($this->attributes['active_participants_count'])) {\n $this->loadCount('activeParticipants');\n }\n\n return $this->attributes['active_participants_count'];\n }\n\n public function getTracksWithTelephonyCountAttribute()\n {\n if (! isset($this->attributes['tracks_with_telephony_count'])) {\n $this->loadCount('tracksWithTelephony');\n }\n\n return $this->attributes['tracks_with_telephony_count'];\n }\n\n /**\n * @TEMP\n * $this->loadCount('tracksWithTelephony') throws null pointer exception\n */\n public function countTracksWithTelephony(): int\n {\n return $this->tracks()->whereNotNull('telephony_provider_id')->count();\n }\n\n public function getDuration(): float\n {\n return $this->getAttribute('duration');\n }\n\n public function getDurationForHumansAttribute()\n {\n return Carbon::now()->subSeconds($this->duration)->diffForHumans(now(), true);\n }\n\n public function getDurationForHumansShortAttribute(): string\n {\n return Carbon::now()->subSeconds($this->duration)->diffForHumans(now(), true, true);\n }\n\n public function hasRecordingPreference(): bool\n {\n return $this->getAttribute('recording_preference') !== null;\n }\n\n public function getRecordingPreference()\n {\n return $this->getAttribute('recording_preference');\n }\n\n /** @return BelongsTo<User, self> */\n public function user(): BelongsTo\n {\n return $this->belongsTo(User::class)->with('group');\n }\n\n public function device()\n {\n return $this->belongsTo(Device::class);\n }\n\n public function category()\n {\n return $this->belongsTo(PlaybookCategory::class, 'playbook_category_id');\n }\n\n public function getCategory(): ?PlaybookCategory\n {\n return $this->getAttribute('category');\n }\n\n public function getPlaybookCategoryId(): ?int\n {\n return $this->getAttribute('playbook_category_id');\n }\n\n public function hasStats(): bool\n {\n return $this->getAttribute('stats') !== null;\n }\n\n public function getStats(): ?Stats\n {\n return $this->getAttribute('stats');\n }\n\n public function stats(): HasOne\n {\n return $this->hasOne(Stats::class);\n }\n\n public function participantStats(): Eloquent\\Relations\\HasManyThrough\n {\n return $this->hasManyThrough(\n Models\\Participant\\ParticipantStats::class,\n Participant::class,\n 'activity_id',\n 'participant_id'\n );\n }\n\n public function getParticipantStats(): Eloquent\\Collection\n {\n return $this->getAttribute('participantStats');\n }\n\n public function account()\n {\n return $this->belongsTo(Account::class);\n }\n\n public function contact()\n {\n return $this->belongsTo(Contact::class)->with(['account']);\n }\n\n public function lead()\n {\n return $this->belongsTo(Lead::class)->with(['stage', 'recordType']);\n }\n\n /**\n * @return BelongsTo<Opportunity, self>\n */\n public function opportunity(): BelongsTo\n {\n /** @var BelongsTo<Opportunity, self> */\n return $this->belongsTo(Opportunity::class);\n }\n\n public function stage()\n {\n return $this->belongsTo(Stage::class);\n }\n\n /**\n * @return HasMany<Session>\n */\n public function sessions(): HasMany\n {\n return $this->hasMany(Session::class);\n }\n\n /**\n * @return HasMany|ParticipantSpeech[]|Eloquent\\Collection\n */\n public function participantSpeeches()\n {\n return $this->hasMany(ParticipantSpeech::class);\n }\n\n public function getParticipantSpeeches(): Eloquent\\Collection\n {\n return $this->getAttribute('participantSpeeches');\n }\n\n /**\n * @return HasMany|Log[]|Eloquent\\Collection\n */\n public function logs()\n {\n return $this->hasMany(Log::class);\n }\n\n /**\n * @return HasMany|Moment[]|Eloquent\\Collection\n */\n public function moments()\n {\n return $this->hasMany(Moment::class);\n }\n\n /**\n * @return HasMany|Note[]|Eloquent\\Collection\n */\n public function notes()\n {\n return $this->hasMany(Note::class);\n }\n\n /**\n * @return Eloquent\\Collection|Note[]\n */\n public function getNotes(): Eloquent\\Collection\n {\n return $this->getAttribute('notes');\n }\n\n /**\n * @return HasMany|Message[]|Eloquent\\Collection\n */\n public function messages()\n {\n return $this->hasMany(Message::class);\n }\n\n public function coachingMessages(): HasMany\n {\n return $this->hasMany(Message::class)\n ->where('is_private', 1);\n }\n\n public function getCoachingMessages(): Eloquent\\Collection\n {\n return $this->getAttribute('coachingMessages');\n }\n\n public function participants(): HasMany\n {\n return $this->hasMany(Participant::class);\n }\n\n public function getSnapshots(): Eloquent\\Collection\n {\n return $this->getAttribute('snapshots');\n }\n\n /** @return HasMany<Track> */\n public function tracks(): HasMany\n {\n return $this->hasMany(Track::class);\n }\n\n public function tracksWithTelephony(): HasMany\n {\n return $this->hasMany(Track::class)->whereNotNull('telephony_provider_id');\n }\n\n public function getTracksWithTelephony(): Eloquent\\Collection\n {\n return $this->getAttribute('tracksWithTelephony');\n }\n\n /** @return Collection|Track[] */\n public function getTracks(): Eloquent\\Collection\n {\n return $this->getAttribute('tracks');\n }\n\n public function masterTrack(): HasOne\n {\n return $this->hasOne(Track::class)->where('is_master', 1)\n ->whereIn('format', [Track::FORMAT_WAV, Track::FORMAT_M3U8])\n ->latest();\n }\n\n public function getMasterTrack(): ?Track\n {\n /** @var Track|null */\n return $this->getAttribute('masterTrack');\n }\n\n public function transcription(): Eloquent\\Relations\\BelongsTo\n {\n return $this->belongsTo(Transcription::class, 'transcription_id');\n }\n\n public function findTranscriptionPromptSummaries(): Collection\n {\n $transcriptionId = $this->getTranscriptionId();\n if (is_null($transcriptionId)) {\n return new Collection();\n }\n\n return Models\\AiPrompt::query()\n ->where('transcription_id', $transcriptionId)\n ->get();\n }\n\n public function getTranscription(): Transcription\n {\n return $this->getAttribute('transcription');\n }\n\n public function hasTranscription(): bool\n {\n return $this->getAttribute('transcription') !== null;\n }\n\n public function setTranscriptionId(int $transcriptionId): Activity\n {\n $this->setAttribute('transcription_id', $transcriptionId);\n\n return $this;\n }\n\n public function unsetTranscriptionId(): self\n {\n $this->setAttribute('transcription_id', null);\n\n return $this;\n }\n\n public function getTranscriptionId(): ?int\n {\n return $this->getAttribute('transcription_id');\n }\n\n /** @deprecated */\n public function hasTranscriptionId(): bool\n {\n return $this->getAttribute('transcription_id') !== null;\n }\n\n public function coachRequests()\n {\n return $this->hasMany(CoachRequest::class);\n }\n\n public function availabilityNotifications()\n {\n return $this->hasMany(AvailabilityNotification::class);\n }\n\n public function processingStates()\n {\n return $this->hasMany(Models\\Activity\\ActivityProcessingState::class);\n }\n\n public function uploadSettings()\n {\n return $this->hasMany(ActivityUploadSetting::class);\n }\n\n public function comments()\n {\n return $this->hasMany(Comment::class);\n }\n\n public function getComments(): Eloquent\\Collection\n {\n return $this->getAttribute('comments');\n }\n\n public function visibleComments()\n {\n $rel = $this->hasMany(Comment::class);\n // Doesn't have auth()->user() in some tests, breaks the build\n if ($user = auth()->user()) {\n return $rel->visibleThreads($user->id);\n }\n\n return $rel;\n }\n\n public function snapshots(): HasMany\n {\n return $this->hasMany(Snapshot::class);\n }\n\n public function calendarEvent()\n {\n return $this->belongsTo(CalendarEvent::class);\n }\n\n public function getCalendarEvent(): ?CalendarEvent\n {\n return $this->getAttribute('calendarEvent');\n }\n\n public function latestCoachingFeedbacks(): HasMany\n {\n return $this->hasMany(CoachingFeedback::class)->latest();\n }\n\n public function playlists(): BelongsToMany\n {\n return $this->belongsToMany(Playlist::class, 'playlist_activities')\n ->withPivot('id', 'uuid', 'user_id', 'start_time', 'end_time')\n ->using(PlaylistActivity::class)\n ->withTimestamps();\n }\n\n public function coachingFeedbacks(): HasMany\n {\n return $this->hasMany(CoachingFeedback::class);\n }\n\n /**\n * @return Eloquent\\Collection|CoachingFeedback[]\n */\n public function getCoachingFeedback(?int $visibility = null): Eloquent\\Collection\n {\n $feedbacks = $this->coachingFeedbacks();\n if ($visibility !== null) {\n $feedbacks = $feedbacks->where('visibility', $visibility);\n }\n\n return $feedbacks->get();\n }\n\n /** @return Eloquent\\Collection<int, PlaylistActivity> */\n public function favoritedBy(User $user): Eloquent\\Collection\n {\n return $this->favorites()->where('user_id', $user->getId())->get();\n }\n\n /**\n * Checks whether consumer has added this activity to their favorites playlist\n * In addition a default playlist gets created if not already present\n */\n public function wasFavoritedBy(User $user): bool\n {\n $playlist = $user->favoritePlaylist();\n\n return $playlist\n ->activities()\n ->where('activity_id', '=', $this->getId())\n ->exists();\n }\n\n /**\n * @return HasMany<PlaylistActivity>\n */\n public function playlistActivities(): HasMany\n {\n return $this->hasMany(PlaylistActivity::class);\n }\n\n /**\n * @return HasManyThrough<Playlist>\n */\n public function favoritePlaylists(): HasManyThrough\n {\n return $this->hasManyThrough(\n Playlist::class,\n PlaylistActivity::class,\n 'activity_id',\n 'id',\n 'id',\n 'playlist_id'\n )->where('is_default', 1);\n }\n\n /**\n * @return Eloquent\\Collection<int, Playlist>\n */\n public function getFavoritePlaylists(): Eloquent\\Collection\n {\n return $this->getAttribute('favoritePlaylists');\n }\n\n /**\n * Get activities from the default/favorite playlist\n *\n * @return Eloquent\\Builder|static\n */\n public function favorites()\n {\n return $this->playlistActivities()->whereHas('playlist', function ($query) {\n $query->where('is_default', 1);\n });\n }\n\n /**\n * @return Model|SubscriptionSet|null|object\n */\n public function subscribedBy(User $user)\n {\n if ($this->prospect === null) {\n return null;\n }\n\n return SubscriptionSet::select('activity_subscription_sets.*')\n ->where('user_id', $user->id)\n ->join('activity_subscriptions', function ($join) {\n $join\n ->on('subscription_set_id', '=', 'activity_subscription_sets.id');\n\n if ($this->account_id) {\n if ($this->opportunity_id) {\n $join\n ->where('followable_type', 'opportunity')\n ->where('followable_id', $this->opportunity_id);\n } else {\n $join\n ->where('followable_type', 'account')\n ->where('followable_id', $this->account_id);\n }\n } elseif ($this->contact_id) {\n $join\n ->where('followable_type', 'contact')\n ->where('followable_id', $this->contact_id);\n } elseif ($this->lead_id) {\n $join\n ->where('followable_type', 'lead')\n ->where('followable_id', $this->lead_id);\n }\n })\n ->first();\n }\n\n /**\n * @return array|Eloquent\\Builder[]|Eloquent\\Collection|SubscriptionSet[]\n */\n public function subscribers()\n {\n if ($this->prospect === null) {\n return [];\n }\n\n return SubscriptionSet::with(['subscriptions', 'user'])\n ->whereHas('subscriptions', function ($query) {\n if ($this->account_id) {\n if ($this->opportunity_id) {\n $query\n ->where('followable_type', 'opportunity')\n ->where('followable_id', $this->opportunity_id);\n } else {\n $query\n ->where('followable_type', 'account')\n ->where('followable_id', $this->account_id);\n }\n } elseif ($this->contact_id) {\n $query\n ->where('followable_type', 'contact')\n ->where('followable_id', $this->contact_id);\n } elseif ($this->lead_id) {\n $query\n ->where('followable_type', 'lead')\n ->where('followable_id', $this->lead_id);\n } else {\n // Nothing to join on?\n // refactor - use Jiminny specific exception\n throw new InvalidArgumentException('Cannot join on a specific customer filter.');\n }\n })\n ->whereHas('user', function ($query) {\n $query\n ->where('team_id', $this->user->team_id)\n ->where('status', User::STATUS_ACTIVE);\n })\n ->get();\n }\n\n /**\n * @return HasMany|Builder|Eloquent\\Collection|Play[]\n */\n public function plays()\n {\n return $this->hasMany(Play::class);\n }\n\n public function getPlays(): Eloquent\\Collection\n {\n return $this->getAttribute('plays');\n }\n\n public function playsBy(User $user)\n {\n /** @var Builder $builder */\n $builder = $this->plays()->where('user_id', $user->id);\n\n return $builder->get();\n }\n\n /**\n * Check if activity was played by a user\n */\n public function wasPlayedBy(User $user): bool\n {\n return $this->plays()->where('user_id', $user->id)->exists();\n }\n\n public function shares()\n {\n return $this->hasMany(Share::class);\n }\n\n /** @return BelongsTo<Participant, self> */\n public function from(): BelongsTo\n {\n return $this->belongsTo(Participant::class, 'from_participant_id');\n }\n\n /** @return BelongsTo<Participant, self> */\n public function to(): BelongsTo\n {\n return $this->belongsTo(Participant::class, 'to_participant_id');\n }\n\n /**\n * Get all of the connections through the participants.\n */\n public function connections()\n {\n return $this->hasManyThrough(\n Connection::class,\n Participant::class,\n 'activity_id',\n 'participant_id'\n );\n }\n\n public function getConnections(): Eloquent\\Collection\n {\n return $this->getAttribute('connections');\n }\n\n /**\n * Get all of the shares through the participants.\n */\n public function participantShares()\n {\n return $this->hasManyThrough(\n Participant\\Share::class,\n Participant::class,\n 'activity_id',\n 'participant_id'\n );\n }\n\n public function getParticipantShares(): Eloquent\\Collection\n {\n return $this->getAttribute('participantShares');\n }\n\n public function topicTriggers(): HasMany\n {\n return $this->hasMany(TopicTrigger::class);\n }\n\n public function activityScorecardRuleTriggers(): HasMany\n {\n return $this->hasMany(Models\\Scorecard\\ActivityScorecardRuleTrigger::class);\n }\n\n public function activityScorecardRules(): HasMany\n {\n return $this->hasMany(Models\\Scorecard\\ActivityScorecardRule::class);\n }\n\n public function questions(): HasMany\n {\n return $this->hasMany(Question::class);\n }\n\n /**\n * Get all the custom data attached to it.\n */\n public function data(): HasMany\n {\n return $this->hasMany(FieldData::class);\n }\n\n public function getData(): Eloquent\\Collection\n {\n /** @var Eloquent\\Collection */\n return $this->getAttribute('data');\n }\n\n #[Scope]\n protected function heldBetween($query, Carbon $start, Carbon $end)\n {\n // Sanity check.\n $from = min($start, $end);\n $until = max($start, $end);\n\n return $query\n ->where('actual_start_date', '>=', $from)\n ->where('actual_end_date', '<=', $until);\n }\n\n #[Scope]\n protected function scheduledBetween($query, Carbon $start, Carbon $end)\n {\n // Sanity check.\n $from = min($start, $end);\n $until = max($start, $end);\n\n return $query\n ->where('scheduled_start_date', '>=', $from)\n ->where('scheduled_end_date', '<=', $until);\n }\n\n /**\n * @param Builder<self> $query\n *\n * @return Builder<self>\n */\n #[Scope]\n protected function forTeam(Builder $query, int $teamId): Builder\n {\n /** @var Builder<self> */\n return $query->whereHas('user', static function (Builder $query) use ($teamId): void {\n $query->where('team_id', $teamId);\n });\n }\n\n /**\n * @param Builder<self> $query\n *\n * @return Builder<self>\n */\n #[Scope]\n protected function inOpenDeals(Builder $query): Builder\n {\n /** @var Builder<self> */\n return $query->whereHas(\n 'opportunity',\n static fn (Builder $query): Builder => $query\n ->where('is_closed', false)\n ->where('deleted_at', '=', null),\n );\n }\n\n /**\n * @param Builder<self> $query\n *\n * @return Builder<self>\n */\n #[Scope]\n protected function notInOpenDeals(Builder $query): Builder\n {\n /** @var Builder<self> */\n return $query->where(\n static fn (Builder $query): Builder => $query->whereNull('opportunity_id')\n ->orWhereHas(\n 'opportunity',\n static fn (Builder $query): Builder => $query->where('is_closed', true),\n )\n ->orWhereHas(\n 'opportunity',\n static fn (Builder $query): Builder => $query->withTrashed()->where('deleted_at', '!=', null),\n ),\n );\n }\n\n /**\n * Finds a participant and updates it with data. If participant doesn't exist creates a new participant from data.\n *\n * @param array $data participant data used to identify the participant and update it\n * @param bool $enterRoom true if participant is entering the room. false if we just want to update some participant data\n * @param Carbon|null $enterTime if $enterNow is true then this is the join time when the actual enter has occurred\n */\n public function updateOrCreateParticipant(\n array $data,\n bool $enterRoom = true,\n ?Carbon $enterTime = null,\n bool $nameMatching = false,\n ): Participant {\n $search = [];\n $participant = null;\n\n if (isset($data['user_id'])) {\n // Check if they already exist based on their ID.\n $search['user_id'] = $data['user_id'];\n } elseif (isset($data['provider_id'])) {\n $search['provider_id'] = $data['provider_id'];\n } elseif ($nameMatching && isset($data['name'])) {\n $search['name'] = $data['name'];\n }\n\n if (! empty($data['email'])) {\n $search['email'] = $data['email'];\n\n // If we have their email, this should be unique enough to lookup (e.g. calendar event based participant).\n unset($search['provider_id']);\n }\n\n // Search by phone number only in case nothing else is available to search by.\n if (array_key_exists('phone_number', $data) && empty($search)) {\n $search['phone_number'] = $data['phone_number'];\n }\n\n if (! empty($search)) {\n // Do a lookup now to see if we have a match on the provided data.\n $lookup = array_map(static function ($key, $value): array {\n return [$key, $value];\n }, array_keys($search), $search);\n\n $participant = $this->participants()->withTrashed()->where($lookup)->first();\n }\n\n // Do a partial match on the name and search in the team members.\n if (! $participant instanceof Participant && $nameMatching && ! empty($data['name'])) {\n $participantMatcher = app(MeetingBot\\Service\\ParticipantMatcher::class);\n\n if (! $participantMatcher instanceof MeetingBot\\Service\\ParticipantMatcher) {\n throw new LogicException('Expecting ParticipantMatcher service instance');\n }\n\n $participant = $participantMatcher->match($this, $data['name']);\n\n // If we've found good participant, avoid data overwrite in `$participant->fill($data)` below.\n if ($participant instanceof Models\\Participant && $participant->hasName()) {\n unset($data['name']); // Thoughts: should we unset also $data['user_id'] and $data['email'] ?\n }\n }\n\n if (! $participant instanceof Participant) {\n // If no match, create a new participant.\n if (empty($search)) {\n $participant = $this->participants()->create();\n } else {\n // If no match, create a new participant but avoid creating duplicates\n $participant = $this->participants()->withTrashed()->firstOrNew($search);\n }\n }\n\n // If we have just recycled a deleted participant\n if ($participant->trashed()) {\n $participant->deleted_at = null;\n }\n\n // Deal with the case when calendar syncs the event while it's in progress.\n // We should prevent change of the participant name, because speeches mapping will fail.\n if ($enterRoom === false\n && $this->isInProgress()\n && $participant->hasName()\n && isset($data['name'])\n && $data['name'] !== $participant->getName()\n ) {\n unset($data['name']);\n }\n\n // Upsert with new data.\n $participant->fill($data);\n\n if ($enterRoom) {\n if ($enterTime === null) {\n $enterTime = now();\n }\n\n // Participant enters room for the first time\n if ($participant->enter_time === null) {\n $participant->enter_time = $enterTime;\n }\n\n // If there is an exit time and it's prior to new enter_time\n if ($participant->exit_time && $participant->exit_time->lt($enterTime)) {\n // Participant has re-joined\n $participant->exit_time = null;\n }\n }\n\n $participant->save();\n\n return $participant;\n }\n\n /**\n * Updates participant CRM data\n *\n * @param array{\n * Lead|null,\n * Account|null,\n * Opportunity|null,\n * Contact|null,\n * Stage|null,\n * string|null\n *} $records\n * @param Participant $participant participant the CRM data is associated with\n */\n public function updateParticipantCrmData(array $records, Participant $participant): void\n {\n // Extract the records.\n [$lead, , , $contact] = $records;\n\n $resolver = $this->getUpdateCrmDataResolver();\n $strategy = $resolver->resolveForParticipant($lead, $contact);\n\n if ($strategy == UpdateCrmDataByStrategy::Lead) {\n if (! $participant->hasName()) {\n $participant->name = $lead->name;\n }\n\n if (! $participant->hasEmailAddress()) {\n $participant->email = $lead->email;\n }\n\n if (! $participant->hasPhoneNumber()) {\n $participant->phone_number = $lead->phone;\n }\n\n $participant->lead_id = $lead->id;\n $participant->save();\n } elseif ($strategy == UpdateCrmDataByStrategy::Contact) {\n if (! $participant->hasName()) {\n $participant->name = $contact->name;\n }\n\n if (! $participant->hasEmailAddress()) {\n $participant->email = $contact->email;\n }\n\n if (! $participant->hasPhoneNumber()) {\n $participant->phone_number = $contact->phone;\n }\n\n $participant->contact_id = $contact->id;\n $participant->save();\n }\n }\n\n /**\n * Updates activity CRM data\n *\n * @param array{\n * Lead|null,\n * Account|null,\n * Opportunity|null,\n * Contact|null,\n * Stage|null,\n * string|null\n *} $records\n */\n public function updateActivityCrmData(array $records): void\n {\n // Extract the records.\n [$lead, $account, $opportunity, $contact, $stage] = $records;\n\n $resolver = $this->getUpdateCrmDataResolver();\n $strategy = $resolver->resolveForActivity($lead, $contact, $account);\n\n if ($strategy == UpdateCrmDataByStrategy::Lead) {\n // Also update the parent activity if required, checking we don't create a mixed lead/account record.\n if ($this->account_id === null && $this->contact_id === null && $this->lead_id === null) {\n $this->lead_id = $lead->id;\n\n if ($this->stage_id === null && $stage) {\n $this->stage_id = $stage->id;\n }\n\n $this->save();\n }\n } elseif ($strategy == UpdateCrmDataByStrategy::Contact) {\n // Also update the parent activity if required, checking we don't create a mixed lead/account record.\n $this->lead_id = null;\n if ($this->stage && $this->stage->getType() === Stage::TYPE_LEAD) {\n $this->stage_id = null;\n }\n\n // Don't trust previous matched account_id as it might have been changed in the CRM\n if ($account && $account->id !== $this->account_id) {\n $this->account_id = $account->id;\n }\n\n if ($opportunity && $this->opportunity_id !== $opportunity->id) {\n $this->opportunity_id = $opportunity->id;\n // Reset stage_id when opportunity changes to avoid stale stage from previous opportunity match\n $this->stage_id = $stage?->id;\n }\n\n if ($this->stage_id === null && $stage) {\n $this->stage_id = $stage->id;\n }\n\n if ($opportunity && $this->value !== $opportunity->value) {\n $this->value = $opportunity->value;\n }\n\n // Always set contact_id when available, regardless of account_id status\n if ($this->contact_id === null && $contact) {\n $this->contact_id = $contact->id;\n }\n\n $this->save();\n } elseif ($strategy == UpdateCrmDataByStrategy::Account && $this->account_id === null) {\n // Also update the parent activity if required, checking we don't create a mixed lead/account record.\n $this->lead_id = null;\n if ($this->stage && $this->stage->getType() === Stage::TYPE_LEAD) {\n $this->stage_id = null;\n }\n\n // Update the account and opportunity on the activity record if possible.\n $this->account_id = $account->id;\n\n if ($this->stage_id === null && $stage) {\n $this->stage_id = $stage->id;\n }\n\n if ($this->opportunity_id === null && $opportunity) {\n $this->opportunity_id = $opportunity->id;\n $this->value = $opportunity->value;\n }\n\n $this->save();\n }\n }\n\n public function getActivityProspectData(): array\n {\n return [\n 'lead' => $this->lead_id,\n 'contact' => $this->contact_id,\n 'account' => $this->account_id,\n 'opportunity' => $this->opportunity_id,\n 'stage' => $this->stage_id,\n ];\n }\n\n public function isOrganizer(User $user): bool\n {\n return $this->user_id && $this->user_id === $user->id;\n }\n\n public function isJoinable(): bool\n {\n return \\in_array($this->status, [\n self::STATUS_SCHEDULED,\n self::STATUS_PENDING,\n self::STATUS_RINGING,\n self::STATUS_IN_PROGRESS,\n ], true);\n }\n\n public function isAttemptedForBotJoin(): bool\n {\n return in_array($this->getAttribute('status'), self::MEETING_BOT_JOIN_ATTEMPTED, true);\n }\n\n /**\n * Check if the activity can be saved to CRM (manual or autolog)\n */\n public function isLoggable(): bool\n {\n if ($this->getUser()->getTeam()->hasFeature(FeatureEnum::SIDEKICK_SETTINGS)) {\n $sidekickService = app(SidekickService::class);\n\n if (! $sidekickService->isSidekickEnabledForUser($this->getUser())) {\n return false;\n }\n }\n\n // If we don't know the activity type, don't try to log.\n if ($this->playbook_category_id === null) {\n return false;\n }\n\n if ($this->user->crm_required === false) {\n return false;\n }\n\n // Don't prompt for internal meetings.\n if ($this->is_internal) {\n return false;\n }\n\n // If we don't know who we are trying to log to, don't try to log.\n if ($this->prospect === null) {\n return false;\n }\n\n $validStatus = false;\n switch ($this->type) {\n case self::TYPE_SOFTPHONE:\n case self::TYPE_SOFTPHONE_INBOUND:\n $validStatus = true;\n\n break;\n case self::TYPE_CONFERENCE:\n $validStatus = in_array($this->status, [\n self::STATUS_BUSY,\n self::STATUS_NO_ANSWER,\n self::STATUS_COMPLETED,\n self::STATUS_CANCELLED,\n ], true);\n\n break;\n case self::TYPE_SMS_INBOUND:\n case self::TYPE_SMS_OUTBOUND:\n $validStatus = in_array($this->status, [\n self::STATUS_QUEUED,\n self::STATUS_SENT,\n self::STATUS_UNDELIVERED,\n self::STATUS_DELIVERED,\n self::STATUS_RECEIVED,\n ], true);\n\n break;\n }\n\n // Depending on the activity channel, we should not try to log.\n return $validStatus;\n }\n\n public function isScheduled(): bool\n {\n return $this->status === self::STATUS_SCHEDULED;\n }\n\n public function scheduledDuration(): int\n {\n if ($this->scheduled_start_time && $this->scheduled_end_time) {\n return $this->scheduled_end_time->timestamp - $this->scheduled_start_time->timestamp;\n }\n\n return 0;\n }\n\n public function isPending(): bool\n {\n return $this->status === self::STATUS_PENDING;\n }\n\n public function isCompleted(): bool\n {\n return $this->status === self::STATUS_COMPLETED;\n }\n\n public function isRinging(): bool\n {\n return $this->status === self::STATUS_RINGING;\n }\n\n public function isInProgress(): bool\n {\n return $this->status === self::STATUS_IN_PROGRESS;\n }\n\n public function isBusy(): bool\n {\n return $this->status === self::STATUS_BUSY;\n }\n\n public function isNoAnswer(): bool\n {\n return $this->status === self::STATUS_NO_ANSWER;\n }\n\n public function isFailed(): bool\n {\n return $this->status === self::STATUS_FAILED;\n }\n\n public function isCancelled(): bool\n {\n return $this->status === self::STATUS_CANCELLED;\n }\n\n public function hasEnded(int $gracePeriodMinutes = 15): bool\n {\n if ($this->isCompleted()) {\n return true;\n }\n\n if (($this->isFailed() || $this->isCancelled()) && $this->hasScheduledEndTime()) {\n return $this->getScheduledEndTime()->addMinutes($gracePeriodMinutes)->isPast();\n }\n\n return false;\n }\n\n public function hasStarted(): bool\n {\n return $this->hasActualStartTime();\n }\n\n public function isOngoing(): bool\n {\n return $this->hasActualStartTime() && ! $this->hasActualEndTime();\n }\n\n public function isTypeSmsInbound(): bool\n {\n return $this->getType() === self::TYPE_SMS_INBOUND;\n }\n\n public function isTypeSmsOutbound(): bool\n {\n return $this->getType() === self::TYPE_SMS_OUTBOUND;\n }\n\n public function isTypeSoftPhone(): bool\n {\n return $this->getType() === self::TYPE_SOFTPHONE;\n }\n\n public function isTypeSoftphoneInbound(): bool\n {\n return $this->getType() === self::TYPE_SOFTPHONE_INBOUND;\n }\n\n public function isTypeConference(): bool\n {\n return $this->getType() === self::TYPE_CONFERENCE;\n }\n\n /**\n * Get a conference elapsed time in seconds.\n *\n * @return int seconds count\n */\n public function secondsTimeElapsed(): int\n {\n if (empty($this->actual_start_time)) {\n return 0;\n }\n\n // Get number of seconds since conference actual start time\n return (int) abs(Carbon::now()->diffInRealSeconds($this->actual_start_time));\n }\n\n /**\n * Get a conference elapsed time formatted as \"1:30:20\" if more than 1 hour or \"30:20\" otherwise.\n */\n public function formattedTimeElapsed(): string\n {\n // Get number of seconds since conference actual start time.\n $elapsedSeconds = $this->secondsTimeElapsed();\n $elapsedTime = Carbon::createFromTimestampUTC($elapsedSeconds);\n\n // Format conference start time.\n return $elapsedTime->format($elapsedSeconds < 3600 ? 'i:s' : 'G:i:s');\n }\n\n public function wasScheduled(): bool\n {\n return $this->calendarEvent !== null || in_array($this->getSource(), [self::SOURCE_OUTLOOK, self::SOURCE_GOOGLE]);\n }\n\n public function isInstant(): bool\n {\n return ! $this->wasScheduled();\n }\n\n /**\n * GETTERS AND SETTERS FOLLOW BELOW\n */\n\n public function getUuid(): string\n {\n return $this->getAttribute('id_string');\n }\n\n public function getId(): int\n {\n return $this->getAttribute('id');\n }\n\n public function getFromParticipantId(): ?int\n {\n return $this->getAttribute('from_participant_id');\n }\n\n public function getFromParticipant(): ?Participant\n {\n return $this->getAttribute('from');\n }\n\n public function getToParticipantId(): ?int\n {\n return $this->getAttribute('to_participant_id');\n }\n\n public function getToParticipant(): ?Participant\n {\n return $this->getAttribute('to');\n }\n\n public function hasScheduledStartTime(): bool\n {\n return $this->getAttribute('scheduled_start_time') !== null;\n }\n\n public function getScheduledStartTime(): ?Carbon\n {\n return $this->getAttribute('scheduled_start_time');\n }\n\n public function setScheduledStartTime(DateTimeInterface $dateTime): self\n {\n $this->setAttribute('scheduled_start_time', $dateTime);\n\n return $this;\n }\n\n public function getScheduledEndTime(): ?DateTimeInterface\n {\n return $this->getAttribute('scheduled_end_time');\n }\n\n public function hasScheduledEndTime(): bool\n {\n return $this->getAttribute('scheduled_end_time') !== null;\n }\n\n public function setScheduledEndTime(DateTimeInterface $dateTime): self\n {\n $this->setAttribute('scheduled_end_time', $dateTime);\n\n return $this;\n }\n\n public function getActualStartTime(): ?Carbon\n {\n return $this->getAttribute('actual_start_time');\n }\n\n public function hasActualStartTime(): bool\n {\n return $this->getAttribute('actual_start_time') !== null;\n }\n\n public function getActualEndTime(): ?Carbon\n {\n return $this->getAttribute('actual_end_time');\n }\n\n public function hasActualEndTime(): bool\n {\n return $this->getAttribute('actual_end_time') !== null;\n }\n\n public function getType(): ?string\n {\n return $this->getAttribute('type');\n }\n\n public function getStatus(): string\n {\n return $this->getAttribute('status');\n }\n\n public function setStatus(string $status): self\n {\n $this->setAttribute('status', $status);\n\n return $this;\n }\n\n public function setActualStartTime(DateTimeInterface $dateTime): self\n {\n $this->setAttribute('actual_start_time', $dateTime);\n\n return $this;\n }\n\n public function setActualEndTime(DateTimeInterface $dateTime, bool $shouldUpdateDuration = true): self\n {\n $this->setAttribute('actual_end_time', $dateTime);\n\n if (! $shouldUpdateDuration) {\n return $this;\n }\n\n return $this->updateDuration();\n }\n\n public function updateDuration(): self\n {\n if (! $this->hasActualStartTime() || ! $this->hasActualEndTime()) {\n return $this;\n }\n\n return $this->setDuration(\n (int) abs($this->getActualStartTime()->diffInRealSeconds($this->getActualEndTime()))\n );\n }\n\n public function setDuration(int $duration): self\n {\n $this->setAttribute('duration', $duration);\n\n return $this;\n }\n\n public function getRecordingState(): string\n {\n return $this->getAttribute('recording_state');\n }\n\n public function isRecordingState(string $recordingState): bool\n {\n return $this->getRecordingState() === $recordingState;\n }\n\n public function setRecordingState(string $recordingState): self\n {\n $this->setAttribute('recording_state', $recordingState);\n\n return $this;\n }\n\n public function hasActivityType(): bool\n {\n return $this->getAttribute('category') !== null;\n }\n\n public function getActivityType(): ?PlaybookCategory\n {\n return $this->getAttribute('category');\n }\n\n public function setActivityType(int $playbookCategoryId): self\n {\n $this->setAttribute('playbook_category_id', $playbookCategoryId);\n\n return $this;\n }\n\n public function hasStage(): bool\n {\n return $this->getAttribute('stage') !== null;\n }\n\n public function getStage(): ?Stage\n {\n return $this->getAttribute('stage');\n }\n\n public function getStageId(): ?int\n {\n return $this->getAttribute('stage_id');\n }\n\n public function setStageId(?int $stageId): void\n {\n $this->setAttribute('stage_id', $stageId);\n }\n\n public function hasOpportunity(): bool\n {\n return $this->getAttribute('opportunity') !== null;\n }\n\n public function getOpportunity(): ?Opportunity\n {\n return $this->getAttribute('opportunity');\n }\n\n public function getOpportunityId(): ?int\n {\n return $this->getAttribute('opportunity_id');\n }\n\n public function setOpportunityId(?int $opportunityId): void\n {\n $this->setAttribute('opportunity_id', $opportunityId);\n }\n\n public function hasContact(): bool\n {\n return $this->getAttribute('contact') !== null;\n }\n\n public function getContact(): ?Contact\n {\n return $this->getAttribute('contact');\n }\n\n public function getContactId(): ?int\n {\n return $this->getAttribute('contact_id');\n }\n\n public function setContactId(?int $contactId): void\n {\n $this->setAttribute('contact_id', $contactId);\n }\n\n public function hasLead(): bool\n {\n return $this->getAttribute('lead') !== null;\n }\n\n public function getLead(): ?Lead\n {\n return $this->getAttribute('lead');\n }\n\n public function getLeadId(): ?int\n {\n return $this->getAttribute('lead_id');\n }\n\n public function setLeadId(?int $leadId): void\n {\n $this->setAttribute('lead_id', $leadId);\n }\n\n public function hasAccount(): bool\n {\n return $this->getAttribute('account') !== null;\n }\n\n public function getAccount(): ?Account\n {\n return $this->getAttribute('account');\n }\n\n public function getAccountId(): ?int\n {\n return $this->getAttribute('account_id');\n }\n\n public function setAccountId(?int $accountId): void\n {\n $this->setAttribute('account_id', $accountId);\n }\n\n /**\n * This method exists to avoid confusion using ->participants() or ->participants. Use the getter instead.\n *\n * @return Collection<int, Participant>|Participant[]\n */\n public function getParticipants(): Collection\n {\n return $this->participants;\n }\n\n /**\n * @deprecated use ParticipantRepository::findParticipantRoomOwner() instead\n */\n public function findParticipantRoomOwner(): ?Participant\n {\n $roomOwnerId = $this->getUserId();\n\n return $this->getParticipants()\n ->filter(static fn (Participant $participant): bool => $participant->isSameUserId($roomOwnerId))\n ->first();\n }\n\n public function hasCrmProviderId(): bool\n {\n return $this->getAttribute('crm_provider_id') !== null;\n }\n\n public function getCrmProviderId(): ?string\n {\n return $this->getAttribute('crm_provider_id');\n }\n\n public function setCrmProviderId(?string $crmProviderId): void\n {\n $this->setAttribute('crm_provider_id', $crmProviderId);\n }\n\n public function getUserId(): ?int\n {\n return $this->getAttribute('user_id');\n }\n\n public function hasUser(): bool\n {\n return $this->user()->exists();\n }\n\n public function getUser(): User\n {\n return $this->getAttribute('user');\n }\n\n public function getCreatedAt(): Carbon\n {\n return $this->getAttribute('created_at');\n }\n\n public function isInFiniteState(): bool\n {\n return $this->isFiniteState($this->getStatus());\n }\n\n public function isFiniteState(string $status): bool\n {\n $finiteStates = self::FINITE_STATES[$this->getType()] ?? [];\n\n return in_array($status, $finiteStates, true);\n }\n\n public function getParticipant(Authenticatable $user): Participant\n {\n return $this->findParticipant($user);\n }\n\n public function findParticipant(Authenticatable $user): ?Participant\n {\n if ($user instanceof User) {\n /** @var User $user */\n return $this->participants()->where('user_id', '=', $user->getId())->first();\n }\n\n throw new LogicException(sprintf('Unsupported Authenticatable implementation %s', get_class($user)));\n }\n\n public function hasLanguageCode(): bool\n {\n return $this->getAttribute('language') !== null;\n }\n\n public function getLanguageCode(): ?string\n {\n /** @var string|null */\n return $this->getAttribute('language');\n }\n\n public function getLanguageCodeHyphenated(): string\n {\n return str_replace('_', '-', $this->getLanguageCode() ?? '');\n }\n\n public function getLanguageCodeLocale(): string\n {\n [ $language ] = explode('_', $this->getLanguageCode() ?? '');\n\n return $language;\n }\n\n public function setLanguageCode(string $value): self\n {\n return $this->setAttribute('language', $value);\n }\n\n public function hasSource(): bool\n {\n return $this->getAttribute('source') !== null;\n }\n\n public function setSource(?string $source): self\n {\n return $this->setAttribute('source', $source);\n }\n\n public function isSource(string $source): bool\n {\n return $this->getAttribute('source') === $source;\n }\n\n public function getSource(): ?string\n {\n return $this->getAttribute('source');\n }\n\n public function isSourceGong(): bool\n {\n return $this->isSource(self::SOURCE_GONG);\n }\n\n public function getExternalId(): ?string\n {\n return $this->getAttribute('external_id');\n }\n\n public function setExternalId(?string $externalId): self\n {\n return $this->setAttribute('external_id', $externalId);\n }\n\n public function hasExternalId(): bool\n {\n return $this->getAttribute('external_id') !== null;\n }\n\n public function getProvider(): string\n {\n return $this->getAttribute('provider');\n }\n\n public function hasTelephonyProviderId(): bool\n {\n return $this->getAttribute('telephony_provider_id') !== null;\n }\n\n public function getTelephonyProviderId(): ?string\n {\n return $this->getAttribute('telephony_provider_id');\n }\n\n public function setTelephonyProviderId(?string $telephonyProviderId): self\n {\n return $this->setAttribute('telephony_provider_id', $telephonyProviderId);\n }\n\n public function getLocation(): ?string\n {\n return $this->getAttribute('location');\n }\n\n public function setLocation(?string $location): self\n {\n return $this->setAttribute('location', $location);\n }\n\n public function isDeleted(): bool\n {\n return $this->getAttribute('deleted_at') !== null;\n }\n\n /**\n * Check if activity recording is on and activity status is not one of the failed statuses.\n */\n public function canReviewActivity(): bool\n {\n $failedStatuses = self::$enumFailedStatuses;\n\n return (! in_array($this->recording_state, [self::RECORDING_OFF, self::RECORDING_STOPPED], true) &&\n ! in_array($this->status, $failedStatuses, true));\n }\n\n public function hasReasonCodeBotKicked(): bool\n {\n return $this->getFlag('recording_reason_code', self::FLAG_RECORDING_REASON_MEETING_BOT_KICKED);\n }\n\n public function hasReasonCodeNotCompliant(): bool\n {\n return $this->getFlag('recording_reason_code', self::FLAG_RECORDING_REASON_CONSENT_DENIED);\n }\n\n public function hasTopicTriggers(): bool\n {\n return $this->topicTriggers()->count() !== 0;\n }\n\n public function getTopicTriggers(): Collection\n {\n return $this->topicTriggers;\n }\n\n public function getTopicTriggersSorted(): Collection\n {\n $this->loadMissing([\n 'topicTriggers.participant',\n 'topicTriggers.playbackThemeTopicTrigger',\n 'topicTriggers.playbackThemeTopicTrigger',\n 'topicTriggers.playbackThemeTopicTrigger.playbackThemeTopic',\n 'topicTriggers.playbackThemeTopicTrigger.playbackThemeTopic.playbackTheme',\n ]);\n\n return $this->topicTriggers\n ->sortBy([\n 'playbackThemeTopicTrigger.playbackThemeTopic.playbackTheme.sort',\n 'playbackThemeTopicTrigger.playbackThemeTopic.sort',\n 'playbackThemeTopicTrigger.sort',\n ]);\n }\n\n public function hasQuestions(): bool\n {\n return $this->questions()->exists();\n }\n\n public function getQuestions(): Collection\n {\n return $this->questions;\n }\n\n public function hasValue(): bool\n {\n return $this->getAttribute('value') !== null;\n }\n\n public function getValue(): ?float\n {\n return $this->getAttribute('value');\n }\n\n public function setValue(?float $value): void\n {\n $this->setAttribute('value', $value);\n }\n\n public function transitionTo(string $newState, callable $callback, ?int $timeout = null): self\n {\n $newState = $this->getWorkflowStateFor(\n $this->getType(),\n $newState\n );\n\n return $this->traitTransitionTo($newState, $callback, $timeout);\n }\n\n public function getWorkflowState(): string\n {\n return $this->getWorkflowStateFor(\n $this->getType(),\n $this->getStatus()\n );\n }\n\n public function getActivityProviderDisplayName(): string\n {\n return \\Cache::remember('activity_provider_display_name-' . $this->getProvider(), 60 * 60 * 24, function () {\n $activityProviderRegistry = app()->make(ActivityProviderRegistry::class);\n\n try {\n return $activityProviderRegistry->get($this->getProvider())->getDisplayName();\n } catch (Exception $exception) {\n return ucfirst($this->getProvider());\n }\n });\n }\n\n private function getWorkflowStateFor(string $activityChannel, string $activityStatus): string\n {\n return sprintf(\n '%s::%s',\n $activityChannel,\n $activityStatus\n );\n }\n\n public function getWorkflow(): array\n {\n $map = [\n self::TYPE_SOFTPHONE => [\n self::STATUS_SCHEDULED => [\n self::STATUS_PENDING,\n self::STATUS_IN_PROGRESS,\n self::STATUS_FAILED,\n self::STATUS_BUSY,\n ],\n self::STATUS_PENDING => [\n self::STATUS_IN_PROGRESS,\n self::STATUS_FAILED,\n self::STATUS_BUSY,\n ],\n self::STATUS_RINGING => [\n self::STATUS_CANCELLED,\n self::STATUS_FAILED,\n self::STATUS_IN_PROGRESS,\n self::STATUS_BUSY,\n ],\n self::STATUS_IN_PROGRESS => [\n self::STATUS_COMPLETED,\n ],\n ],\n self::TYPE_SOFTPHONE_INBOUND => [\n self::STATUS_RINGING => [\n self::STATUS_IN_PROGRESS,\n self::STATUS_NO_ANSWER,\n self::STATUS_CANCELLED,\n self::STATUS_FAILED,\n self::STATUS_BUSY,\n ],\n self::STATUS_IN_PROGRESS => [\n self::STATUS_COMPLETED,\n ],\n ],\n ];\n\n return collect($map)\n ->mapWithKeys(function (array $currentStates, string $activityChannel): array {\n return [\n $activityChannel => collect($currentStates)\n ->mapWithKeys(function (array $possibleStates, $currentState) use ($activityChannel): array {\n $transitionName = $this->getWorkflowStateFor($activityChannel, $currentState);\n\n return [\n $transitionName => array_map(function (string $newState) use ($activityChannel) {\n return $this->getWorkflowStateFor($activityChannel, $newState);\n }, $possibleStates),\n ];\n }),\n ];\n })\n ->reduce(static function (array $carry, Collection $item): array {\n return array_merge($carry, $item->all());\n }, []);\n }\n\n public function hasPosterPath(): bool\n {\n return $this->getAttribute('poster_path') !== null;\n }\n\n public function getPosterPath(): ?string\n {\n return $this->getAttribute('poster_path');\n }\n\n /**\n * Take into account all recording settings and determine if we need to record this activity or not.\n */\n public function shouldRecord(): bool\n {\n return $this->determineRecordingReasonCode() === null;\n }\n\n public function determineRecordingReasonCode(): ?int\n {\n // Conference specific decisions.\n if ($this->isTypeConference()) {\n // If they have manually overridden the recording setting to not record.\n if ($this->hasRecordingPreference() && $this->getRecordingPreference() === false) {\n return self::FLAG_RECORDING_REASON_PREFERENCE_OVERRIDE;\n }\n\n // If they have manually overridden the recording setting to record.\n if ($this->hasRecordingPreference() && $this->getRecordingPreference() === true) {\n return null;\n }\n\n // If their team has disabled recording meetings, don't record.\n if ($this->user->team->isConferenceRecordPreferenceDisabled()) {\n return self::FLAG_RECORDING_REASON_TEAM_AUTOMATIC_DISABLED;\n }\n\n // If the host has disabled recording meetings, don't record.\n if ($this->user->checkConferenceRecordPreference() === false) {\n return self::FLAG_RECORDING_REASON_USER_AUTOMATIC_DISABLED;\n }\n\n // If it was marked internal...\n if ($this->is_internal) {\n // and their team has disabled recording internal meetings, don't record.\n if (\n $this->user->team->isConferenceRecordPreferenceEnabled()\n && ! $this->user->team->isConferenceRecordInternalPreferenceEnabled()\n ) {\n return self::FLAG_RECORDING_REASON_TEAM_INTERNAL_DISABLED;\n }\n\n // and the host has disabled recording internal meetings, don't record.\n if ($this->user->checkConferenceRecordInternalPreference() === false) {\n return self::FLAG_RECORDING_REASON_USER_INTERNAL_DISABLED;\n }\n }\n\n // If it was not scheduled and they disabled internal meetings, we cannot determine if it was internal.\n if ($this->wasScheduled() === false && $this->user->checkConferenceRecordInternalPreference() === false) {\n return self::FLAG_RECORDING_REASON_USER_INTERNAL_DISABLED_UNSCHEDULED;\n }\n }\n\n return null;\n }\n\n public function getRecordingReasonCode(): int\n {\n return $this->getAttribute('recording_reason_code');\n }\n\n public function setRecordingReasonCode(int $recordingReasonCode): self\n {\n $this->setAttribute('recording_reason_code', $recordingReasonCode);\n\n return $this;\n }\n\n // Not used today.\n public function getRecordingReasonString(): ?string\n {\n if ($this->hasRecordingReasonCompliancePrompted()) {\n return Team::COMPLIANCE_MODE_RECORDING_PROMPT;\n }\n\n if ($this->hasRecordingReasonComplianceRestricted()) {\n return Team::COMPLIANCE_MODE_RECORDING_RESTRICT;\n }\n\n if ($this->hasRecordingReasonComplianceRestrictedToOneSideRecording()) {\n return Team::COMPLIANCE_MODE_RECORDING_RESTRICT_ONE_SIDE;\n }\n\n return null;\n }\n\n public function hasRecordingReasonComplianceRestricted(): bool\n {\n return $this->getFlag('recording_reason_code', self::FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT);\n }\n\n public function hasRecordingReasonCompliancePrompted(): bool\n {\n return $this->getFlag('recording_reason_code', self::FLAG_RECORDING_REASON_COMPLIANCE_PROMPT);\n }\n\n public function hasRecordingReasonComplianceRestrictedToOneSideRecording(): bool\n {\n return $this->getFlag('recording_reason_code', self::FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT_ONE_SIDE);\n }\n\n public function getAudioTrack(): ?Track\n {\n /** @var Track|null */\n return $this->tracks()\n ->where('type', '=', Track::TYPE_AUDIO)\n ->first();\n }\n\n public function activeParticipants(): HasMany\n {\n return $this->hasMany(Participant::class)->active();\n }\n\n public function getActiveParticipants(): Eloquent\\Collection\n {\n return $this->getAttribute('activeParticipants');\n }\n\n public function crm(): Eloquent\\Relations\\BelongsTo\n {\n return $this->belongsTo(Configuration::class, 'crm_configuration_id');\n }\n\n public function activitySummaryLogs(): HasMany\n {\n return $this->hasMany(ActivitySummaryLog::class);\n }\n\n public function getCrm(): ?Configuration\n {\n return $this->getAttribute('crm');\n }\n\n public function hasCrmConfiguration(): bool\n {\n return $this->getAttribute('crm') !== null;\n }\n\n public function isProcessed(): ?bool\n {\n return $this->getAttribute('is_processed');\n }\n\n public function hasRecordingPrompt(): bool\n {\n return $this->getAttribute('has_recording_prompt') === true;\n }\n\n public function isOnAir(): bool\n {\n return $this->getAttribute('on_air') === self::ON_AIR_READY || $this->getAttribute('on_air') === self::ON_AIR_STREAMING;\n }\n\n public function setOnAir(int $onAir): self\n {\n $this->setAttribute('on_air', $onAir);\n\n return $this;\n }\n\n public function getOnAir(): ?int\n {\n return $this->getAttribute('on_air');\n }\n\n public function setTitleFromCallData(Call $call): void\n {\n $direction = $call->isOutbound() ? 'to' : 'from';\n\n $party = $this->prospect_name\n ?? $call->getContactName()\n ?? $call->getOtherPartyPhoneNumber()\n ;\n\n $this->update(['title' => sprintf('Call %s %s', $direction, $party)]);\n }\n\n /**\n * @param array{}|array{channels:string|null, format:string|null, type:string|null, status:string|null} $audioParams\n */\n public function createAudioTrack(\n string $telephonyProviderId,\n string $recordingUrl,\n array $audioParams = []\n ): Track {\n return $this->tracks()->updateOrCreate([\n 'telephony_provider_id' => $telephonyProviderId,\n ], [\n 'type' => $audioParams['type'] ?? Track::TYPE_AUDIO,\n 'status' => $audioParams['status'] ?? Track::STATUS_PENDING,\n 'format' => $audioParams['format'] ?? Track::FORMAT_WAV,\n 'provider_content_url' => $recordingUrl,\n 'start_time' => $this->actual_start_time,\n 'end_time' => $this->actual_end_time,\n ]);\n }\n\n public function createTrack(string $telephonyProviderId, array $params): Track\n {\n return $this->tracks()->updateOrCreate(\n [\n 'telephony_provider_id' => $telephonyProviderId,\n ],\n $params\n );\n }\n\n public function createOrganiserParticipant(Call $call): Participant\n {\n $user = $this->getUser();\n\n return $this->updateOrCreateParticipant([\n 'is_ghost' => 0,\n 'name' => $user->name,\n 'email' => $user->email,\n 'phone_number' => phone_e164(null, $call->getUserPhoneNumber()),\n 'enter_time' => $this->actual_start_time,\n 'exit_time' => $this->actual_end_time,\n 'user_id' => $user->id,\n ], false);\n }\n\n public function createProspectParticipant(Call $call): Participant\n {\n // not null 'name' is mandatory here to create a separate participant with 'nameMatching'\n // in case of the same phone_number with the Organiser\n $useNameMatching = $call->getUserPhoneNumber() === $call->getOtherPartyPhoneNumber();\n $defaultName = $useNameMatching ? '' : null;\n\n return $this->updateOrCreateParticipant(data: [\n 'is_ghost' => 0,\n 'name' => $this->prospect->name ?? $defaultName,\n 'email' => $this->prospect->email ?? null,\n 'phone_number' => phone_e164(null, $call->getOtherPartyPhoneNumber()),\n 'enter_time' => $this->actual_start_time,\n 'exit_time' => $this->actual_end_time,\n 'contact_id' => $this->contact_id ?? null,\n 'lead_id' => $this->lead_id ?? null,\n ], enterRoom: false, nameMatching: $useNameMatching);\n }\n\n public function updateParticipants(Participant $organiserParticipant, Participant $prospectParticipant): void\n {\n $this->update([\n 'from_participant_id' => $this->isTypeSoftPhone() ? $organiserParticipant->id : $prospectParticipant->id,\n 'to_participant_id' => $this->isTypeSoftPhone() ? $prospectParticipant->id : $organiserParticipant->id,\n ]);\n }\n\n public function hasProspect(): bool\n {\n return $this->getProspectAttribute() !== null;\n }\n\n public function isPrivate(): bool\n {\n return $this->getAttribute('is_private');\n }\n\n /** Create a new factory instance for the model. */\n protected static function newFactory(): Factory\n {\n return ActivityFactory::new();\n }\n\n public function getUpdatedAt(): Carbon\n {\n return $this->getAttribute('updated_at');\n }\n\n public function getActivitySummaryLogs(): Eloquent\\Collection\n {\n return $this->getAttribute('activitySummaryLogs');\n }\n\n public function hasProspectActivitySummaryLog(): bool\n {\n return $this->getActivitySummaryLogs()->contains(\n 'relation_type',\n ActivitySummaryLog::RELATION_OBJECT_TYPE_PROSPECT\n );\n }\n\n public function getTeam(): Team\n {\n return $this->getUser()->getTeam();\n }\n\n private function getUpdateCrmDataResolver(): UpdateCrmDataResolverInterface\n {\n $factory = app(UpdateCrmDataResolverFactory::class);\n\n return $factory->create($this);\n }\n\n public function getMeetingTrackProviderId(string $type): string\n {\n $label = match ($type) {\n Track::TYPE_VIDEO => 'v',\n Track::TYPE_AUDIO => 'a',\n default => throw new InvalidArgumentJiminnyException('Invalid track type'),\n };\n\n $startTimestamp = $this->getScheduledStartTime()?->getTimestamp();\n $teamId = $this->getTeam()->getId();\n\n return $this->getTelephonyProviderId() . ':' . $label . ':' . $startTimestamp . ':' . $teamId;\n }\n\n /**\n * Get all consent records associated with this activity\n *\n * @return \\Illuminate\\Database\\Eloquent\\Relations\\HasMany\n */\n public function participantConsents(): HasMany\n {\n return $this->hasMany(Participant\\Consent::class);\n }\n\n public function isDiallerCall(): bool\n {\n if ($this->getProvider() === Activity::PROVIDER_UPLOADER) {\n return false;\n }\n\n if (! in_array($this->getType(), [self::TYPE_SOFTPHONE, self::TYPE_SOFTPHONE_INBOUND])) {\n return false;\n }\n\n return $this->getProvider() !== self::PROVIDER_TWILIO;\n }\n\n public function getActivityDateWithFallback(): Carbon\n {\n if ($this->getActualStartTime() !== null) {\n return $this->getActualStartTime();\n }\n\n if ($this->getScheduledStartTime() !== null) {\n return $this->getScheduledStartTime();\n }\n\n return $this->getCreatedAt();\n }\n\n public function getCrmType(): ?string\n {\n // Treat uploader activities as conferences\n if ($this->getProvider() === Activity::PROVIDER_UPLOADER) {\n return Activity::TYPE_CONFERENCE;\n }\n\n return $this->getType();\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Execute","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Explain Plan","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Browse Query History","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"View Parameters","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Open Query Execution Settings…","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"In-Editor Results","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Tx: Auto","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Cancel Running Statements","depth":4,"on_screen":true,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Playground","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"jiminny","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"45","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"1","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"41","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"66","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"SELECT * FROM teams WHERE name LIKE '%litify%'; # 1069, 994, 24993\nSELECT * FROM users WHERE id = 25061;\nSELECT * FROM crm_profiles WHERE crm_configuration_id = 994;\nSELECT * FROM crm_profiles WHERE user_id = 25061;\n\nselect * from crm_configurations where id = 834;\nSELECT * FROM teams WHERE id = 882;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 882 and sa.provider = 'hubspot';\n\nSELECT * FROM crm_configurations where id = 882; # 933 - GoGlobal\nSELECT * FROM opportunities WHERE team_id = 933 order by updated_at desc;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 933 and sa.provider = 'hubspot';\n\nSELECT * FROM crm_configurations WHERE provider = 'hubspot' and crm_provider_id = 7270388;\n\nSELECT * FROM contacts where crm_configuration_id = 834;\nSELECT * FROM opportunities WHERE team_id = 933\n# AND crm_provider_id IN ('20131586060','46017317898','52543911090','53451356564','54101251892','54323768459');\nAND id IN (8482561,18352941,19042734,19232139,19445140,19472541);\nSELECT * FROM opportunity_contacts\nWHERE opportunity_id IN (8482561,18352941,19042734,19232139,19445140,19472541);\n\n# ********************************************************************\nSELECT * FROM crm_configurations where id = 485; #\nSELECT * FROM opportunities WHERE team_id = 933 order by updated_at desc;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 933 and sa.provider = 'hubspot';\n\nselect crm.provider, l.* from leads l join crm_configurations crm on l.crm_configuration_id = crm.id\nwhere crm.provider NOT IN ('salesforce', 'integration-app', 'bullhorn', 'copper')\n# and l.converted_at IS NOT NULL\n;\n\n# ********************************************************************\nSELECT * FROM activities a WHERE type IN ('email-inbound', 'email-outbound')\nand opportunity_id IS NULL\norder by id desc;\n\nSELECT * FROM teams WHERE id = 604; # 598\nSELECT * FROM activities WHERE id = 74410828; # chelseaw@allvoices.co\nSELECT * FROM accounts WHERE id = 20068382;\nSELECT * FROM accounts WHERE id = 35186038;\n\nSELECT * FROM contacts WHERE team_id = 852 and updated_at > '2026-01-23 12:30:00' order by updated_at desc;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 559 and sa.provider = 'hubspot';\n\nSELECT * FROM activities WHERE uuid_to_bin('cb6342b6-a183-401c-b0af-ede92b2ae763') = uuid;\nselect * from sidekick_settings where team_id = 781;\n\nSELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id = 26651871; # Teya\nSELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id = 7562435;\nSELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id = 8420347; # opflit 2100\n\nSELECT * FROM crm_layouts WHERE crm_configuration_id = 711;\nSELECT * FROM activities where crm_configuration_id = 711 and crm_provider_id IS NULL\nand is_internal = 0 and status = 'completed'\norder by id desc;\n\nSELECT * FROM crm_layout_entities\nWHERE crm_layout_id IN (2352, 2353);\n;\n\nSELECT * FROM crm_configurations where provider = 'hubspot' and id = 530;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 556 and sa.provider = 'hubspot';\n\nSELECT * FROM activities WHERE uuid_to_bin('c6ca4b22-7738-4563-a95d-b8a9598924ae') = uuid;\nSELECT * FROM activities WHERE uuid_to_bin('442abb2b-28bd-4be8-9c25-19e9bf02766d') = uuid;\nselect * from contacts\nwhere crm_configuration_id = 530\nand crm_provider_id = 872252;\n\nselect * from activities where crm_configuration_id = 530\nand user_id = 14343 and type like '%softphone%'\nand created_at between '2026-01-28 15:00:00' and '2026-01-28 15:10:00';\n\nSELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id = 25666868; # Teya\nSELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id = 8646335; # Teya\nSELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id IN (5933397);\n\n\nSELECT t.name, t.id, t.owner_id, c.id, c.provider, c.crm_base_url FROM teams t\nJOIN crm_configurations c ON t.id = c.team_id\nWHERE t.status = 'active';\n\nSELECT * FROM teams where id = 1091;\nSELECT * FROM crm_configurations where team_id = 1091;\nSELECT * FROM activity_providers where team_id = 1091;\nSELECT * FROM activities where crm_configuration_id = 1024 and type IN ('softphone', 'softphone-outbound')\nand provider NOT IN ('hubspot', 'aircall')\n# and telephony_provider_id = '019c1131-a22f-4792-b9ea-20adf6a02ed0'\norder by id desc;\n\n\nSELECT * FROM teams WHERE name LIKE '%Leadventure%';\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1091 and sa.provider = 'salesforce';\n\nSELECT * FROM teams WHERE name LIKE '%Wilson%'; # 862, 812\nSELECT * FROM teams where id = 862;\nSELECT * FROM crm_configurations where team_id = 862;\nSELECT * FROM activity_providers where team_id = 862;\nSELECT * FROM activities where crm_configuration_id = 812 and type IN ('softphone', 'softphone-outbound')\nand provider NOT IN ('hubspot', 'aircall')\n# and telephony_provider_id = '019c1131-a22f-4792-b9ea-20adf6a02ed0'\norder by id desc;\n\n\nSELECT t.id, crm.id, crm.provider, ap.* FROM teams t\njoin crm_configurations crm on t.id = crm.team_id\njoin activity_providers ap on t.id = ap.team_id\nwhere t.status = 'active' and ap.is_enabled = 1\nand crm.provider = 'hubspot'\nand ap.provider NOT IN ('hubspot', 'aircall', 'uploader', 'gong', 'twilio', 'zoom-bot', 'google-meet', 'ms-teams',\n 'outreach', 'close', 'ringcentral', 'dialpad', 'zoom-phone');\n\nSELECT * FROM teams where id = 1068;\nSELECT * FROM crm_configurations where team_id = 1068;\nSELECT * FROM activity_providers where team_id = 1068;\n\nSELECT * FROM activities a\nwhere crm_configuration_id = 993 and type IN ('softphone', 'softphone-outbound')\nand a.provider NOT IN ('hubspot', 'uploader', 'gong', 'twilio', 'google-meet', 'ms-teams','close'\n )\n# and telephony_provider_id = '019c1131-a22f-4792-b9ea-20adf6a02ed0'\norder by a.id desc;\n\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1068 and sa.provider = 'hubspot';\n\n# ********************************************************************\n# ********************************************************************\n# ********************************************************************\nSELECT * FROM crm_configurations where id = 882; # 933 - GoGlobal , portalId: 6017093\nSELECT * FROM opportunities WHERE team_id = 933 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 933 and updated_at > '2026-02-06 00:00:00' order by updated_at desc;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 933 and sa.provider = 'hubspot';\n\n# ********************************************************************\nSELECT * FROM crm_configurations where id = 834; # 882 - AnyVan , portalId: 5468262\nSELECT * FROM contacts WHERE crm_configuration_id = 834 and updated_at > '2026-03-30 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE crm_configuration_id = 834 and updated_at > '2026-03-04 08:00:00' order by updated_at desc;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 882 and sa.provider = 'hubspot';\nselect * from crm_layouts where crm_configuration_id = 834;\nselect * from crm_layout_entities where crm_layout_id = 2780;\nselect * from crm_fields where id IN (321153,321192,321193,321194);\n\nSELECT * FROM opportunities WHERE crm_configuration_id = 834 and id = 10993426;\n# ********************************************************************\nSELECT * FROM crm_configurations where id = 988; # 1057 - Teya (543ce4f4-168c-4571-91ea-5b35c253f06f) , portalId: 26651871\nSELECT * FROM opportunities WHERE team_id = 1057 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 1057 and updated_at > '2026-02-04 00:00:00' order by updated_at desc;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1057 and sa.provider = 'hubspot';\n\nSELECT * FROM crm_configurations where id = 533; # 559 - Connectd , portalId: 6710988\nSELECT * FROM opportunities WHERE team_id = 559 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 559 and updated_at > '2026-02-04 00:00:00' order by updated_at desc;\n\nSELECT * FROM crm_configurations where id = 801; # 852 - Rise Vision , portalId: 2700250\nSELECT * FROM opportunities WHERE team_id = 852 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 852 and updated_at > '2026-02-04 00:00:00' order by updated_at desc; # 6th last\n\nSELECT * FROM crm_configurations where id = 962; # 1034 - evergrowth.io , portalId: 143180990\nSELECT * FROM opportunities WHERE team_id = 1034 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 1034 and updated_at > '2026-02-04 00:00:00' order by updated_at desc;\n\n# ********************************************************************\nSELECT * FROM crm_configurations where id = 1037; # 1102 - Jibble , portalId: 6649755\nSELECT * FROM opportunities WHERE team_id = 1102 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 1102 and updated_at > '2026-02-04 12:30:00' order by updated_at desc; # 8\n\nSELECT * FROM crm_configurations where id = 1015; # 1049 - Travefy , portalId: 48904401\nSELECT * FROM opportunities WHERE team_id = 1049 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 1049 and updated_at > '2026-02-04 12:30:00' order by updated_at desc; # 20\n\nSELECT * FROM crm_configurations where id = 64; # 70 - SalaryFinance , portalId: 3404115\nSELECT * FROM opportunities WHERE team_id = 70 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 70 and updated_at > '2026-02-04 12:30:00' order by updated_at desc; # 6th last\n\nSELECT * FROM crm_configurations where id = 802; # 853 - Street Group , portalId: 7658438\nSELECT * FROM opportunities WHERE team_id = 853 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 853 and updated_at > '2026-02-04 12:30:00' order by updated_at desc; # 10\n\nSELECT * FROM crm_configurations where id = 872; # 921 - In Professional Development , portalId: 9238273\nSELECT * FROM opportunities WHERE team_id = 921 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 921 and updated_at > '2026-02-04 12:30:00' order by updated_at desc; # 2\n\n# ********************************************************************\nSELECT * FROM crm_configurations where id = 550; # 576 - SeedLegals , portalId: 3028661\nSELECT * FROM opportunities WHERE team_id = 576 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 576 and updated_at > '2026-02-05 14:00:00' order by updated_at desc;\n\nSELECT * FROM crm_configurations where id = 989; # 1058 - rtaoutdoor.com , portalId: 22371204\nSELECT * FROM opportunities WHERE team_id = 1058 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 1058 and updated_at > '2026-02-05 14:00:00' order by updated_at desc;\n\nSELECT * FROM crm_configurations where id = 896; # 946 - Mintago , portalId: 6621281\nSELECT * FROM opportunities WHERE team_id = 946 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 946 and updated_at > '2026-02-05 14:00:00' order by updated_at desc;\n\nSELECT * FROM crm_configurations where id = 617; # 641 - PCS , portalId: 5244937\nSELECT * FROM opportunities WHERE team_id = 641 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 641 and updated_at > '2026-02-05 14:00:00' order by updated_at desc; # 7th\n# ********************************************************************\nSELECT * FROM crm_configurations where id = 649; # 670 - Eventeny , portalId: 4492849\nSELECT * FROM opportunities WHERE team_id = 670 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 670 and updated_at > '2026-02-09 08:00:00' order by updated_at desc; #\n\nSELECT * FROM crm_configurations where id = 48; # 51 - CleanCloud , portalId: 4373137\nSELECT * FROM opportunities WHERE team_id = 51 and updated_at > '2026-03-04 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 51 and updated_at > '2026-02-09 08:00:00' order by updated_at desc;\nselect * from users where team_id = 51; # 7783\nSELECT * FROM groups WHERE uuid_to_bin('8a8d2cb6-8b55-4fa3-8b5c-5f0e3d8de59a') = uuid; # 1130\nselect * from activity_searches where user_id = 7783;\nselect * from activity_search_filters where activity_search_id IN (32291, 32292);\n\nSELECT asf.activity_search_id, asf.id, asf.value\nFROM activity_search_filters asf\nWHERE asf.filter = 'group_id'\nAND asf.value IN (\n SELECT CONCAT(\n HEX(SUBSTR(uuid, 5, 4)), '-',\n HEX(SUBSTR(uuid, 3, 2)), '-',\n HEX(SUBSTR(uuid, 1, 2)), '-',\n HEX(SUBSTR(uuid, 9, 2)), '-',\n HEX(SUBSTR(uuid, 11))\n )\n FROM groups\n WHERE deleted_at IS NOT NULL\n);\n\nSELECT * FROM crm_configurations where id = 272; # 290 - Bonham & Brook , portalId: 5705856\nSELECT * FROM opportunities WHERE team_id = 290 and updated_at > '2026-02-05 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 290 and updated_at > '2026-02-09 08:00:00' order by updated_at desc; # 6th\n# ********************************************************************\nSELECT * FROM crm_configurations where provider = 'hubspot';\nSELECT * FROM crm_configurations where id = 1056; # 1119 - Chromatic , portalId: 45602133\nSELECT * FROM opportunities WHERE team_id = 1119 and remotely_created_at > '2026-02-01 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 1119 and updated_at > '2026-02-09 09:00:00' order by updated_at desc; # null\n# ********************************************************************\n\nselect * from contacts where crm_provider_id = '003Uu00000ojD4NIAU';\nselect\n cp.*\n# DISTINCT t.id\n# cp.id, cp.user_id, t.id, cp.crm_configuration_id, cp.contact_fields\nFROM crm_profiles cp\nJOIN crm_configurations crm on crm.id = cp.crm_configuration_id\nJOIN users u on u.id = cp.user_id\nJOIN teams t ON t.id = crm.team_id\nWHERE crm.provider = 'salesforce' and t.status = 'active'\n and cp.archived_at IS NULL and u.deleted_at IS NULL\n and t.id NOT IN (1093)\n and t.id = 2\n and cp.contact_fields IS NULL;\n# and c.crm_provider_id = '003Uu00000ojD4NIAU';\n\nSELECT * FROM users WHERE id = 26484;\nSELECT * FROM crm_profiles WHERE user_id = 26484;\nSELECT * FROM social_accounts WHERE sociable_id = 26484;\nSELECT * FROM crm_configurations where provider = 'salesforce';\nselect * from users where id IN (10022, 10403);\nselect * from users where team_id IN (526);\nselect * from teams where id IN (526, 532);\nselect * from crm_configurations where id IN (500, 516);\nselect * from crm_profiles where crm_configuration_id IN (500, 516) and user_id IN (10022, 10403);\nselect * from contacts where crm_configuration_id IN (500, 516) and crm_provider_id = '003Uu00000ojD4NIAU';\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 526 and sa.provider = 'salesforce';\nselect * from team_settings where team_id IN (526, 532);\n\nselect * from users where id IN (22824);\nselect * from crm_profiles where crm_configuration_id IN (1026);\n\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1093 and sa.provider = 'salesforce';\n\nselect * from teams where id = 1099;\nselect * from users where id = 29643\n\nselect * from activity_processing_states;\n\nSELECT * FROM teams where name LIKE '%Fare%'; # 233\nSELECT * FROM opportunities where crm_configuration_id = 215\n# and crm_provider_id = 'oppo_ogESZf2P50nDrd1nGPvKDXeA6sSaTN5v51Lp4ayVzKR'\n;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1088 and sa.provider = 'hubspot';\n\nSELECT * FROM teams order by updated_at DESC\nSELECT * FROM crm_configurations WHERE id = 1019; # SimpleConsign 1088 - no social account\n\nselect * from crm_configurations where provider = 'pipedrive';\n\nselect * from teams where id = 957;\nselect * from crm_configurations where id = 957;\n\nSELECT * FROM teams WHERE name LIKE '%Prolific%'; # 544, 518, 10743\nSELECT * FROM opportunities where crm_configuration_id = 518 order by id desc;\n\nselect * from users where team_id = 1; # 26726 - Gabriela Dureva\nSELECT * FROM opportunities where user_id = 26726; # 16834447 - Prolific\nselect * from activities where user_id = 26726 order by id desc;\nselect * from contacts where crm_configuration_id = 1\nand email IN ('charlotte.ward@prolific.com', 'frankie.bryant@prolific.com'); # 2094416, 2093620\nSELECT * FROM contacts WHERE id = 6284931;\n\nSELECT p.* FROM activities a JOIN participants p ON a.id = p.activity_id\nWHERE a.user_id = 26726 and p.lead_id IN (2094416, 2093620) and a.created_at > '2026-01-01 00:00:00' order by p.email;\n\nselect * from activities where id IN (75509259,75509261,75509261,75511034,75026464,75517602,75517605);\nselect * from crm_configurations where id = 1;\n\n43801692-1aeb-32ce-acba-5b80a479701a\n44c3c9cf-6f5e-75f3-8179-bc9f75dd2b1b\n405975c0-b3d0-7aaa-821f-09d59cae6dd1\n4caf848d-4bed-2299-b248-7788d41f9fca\n49bedc3f-f196-eef3-89c3-dea6a3b4aa63\n43420989-a09d-b8f8-9806-c8bbf7a02aac\n\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1 and sa.provider = 'salesforce';\n\nSELECT * FROM activities WHERE id = 75461988;\n\nSELECT * FROM activities WHERE uuid_to_bin('d6c5052e-e972-49e9-8912-26f2f7d6c5f6') = uuid;\n\nselect * from contacts where id = 17900517;\n\nselect * from contact_roles cr join crm_configurations crm on cr.crm_configuration_id = crm.id\nwhere crm.provider != 'salesforce';\n\nselect * from users where id = 21047;\nSELECT * FROM crm_configurations WHERE id = 892;\nSELECT * FROM teams WHERE id = 942;\nselect * from opportunities where team_id = 942 order by updated_at desc;\nselect * from contacts where team_id = 942 order by updated_at desc;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 942 and sa.provider = 'hubspot';\n\nSELECT * FROM opportunities where team_id = 1 and crm_provider_id IN ('006Pq00000NeH6XIAV', '006Pq000007z8kdIAA'); # 10697889, 6621430\nSELECT * FROM crm_configurations WHERE id = 1;\nSELECT * FROM teams WHERE crm_id = 1;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1 and sa.provider = 'salesforce';\n\nselect id, user_id, opportunity_fields from crm_profiles where crm_configuration_id = 1\nSELECT * FROM opportunities where team_id = 1 order by updated_at desc; # 10697889, 6621430\n\nselect * from teams where id = 852;\nselect * from groups where id = 2286;\nselect * from sidekick_settings where team_id = 852;\nselect * from default_activity_types where team_id = 852;\n\n\nSELECT cc.provider, cc.id, p.id, u.*\nFROM users u\nLEFT JOIN crm_profiles p ON u.id = p.user_id AND p.id IS NULL -- no profile\nINNER JOIN teams t ON u.team_id = t.id AND t.status = 'active' -- team is active\nINNER JOIN crm_configurations cc ON t.crm_id = cc.id\nWHERE u.status = 1 AND u.deleted_at IS NULL\nAND u.crm_required = 1\nAND u.team_id = 1\nORDER BY u.team_id;\n\nSELECT * FROM crm_profiles cp where cp.crm_configuration_id = 1 and cp.user_id IN (\n18481\n );\n\nSELECT cc.provider, cc.id, p.id, u.*\nFROM users u\nLEFT JOIN crm_profiles p ON u.id = p.user_id\nINNER JOIN teams t ON u.team_id = t.id AND t.status = 'active'\nINNER JOIN crm_configurations cc ON t.crm_id = cc.id\nWHERE u.status = 1\n AND u.deleted_at IS NULL\n AND u.crm_required = 1\n# AND u.team_id = 1\n AND p.id IS NULL -- Move this condition to WHERE clause\nORDER BY u.team_id;\n\nSELECT * FROM opportunities WHERE id = 20002609;\nselect * from teams where id = 1122; # Velatir, 29953 - christian@velatir.com\nselect * from crm_configurations where id = 1060;\nselect * from crm_layouts where crm_configuration_id = 1060;\nSELECT * FROM crm_layout_entities WHERE crm_layout_id = 3596;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1122 and sa.provider = 'hubspot';\nselect * from opportunities where team_id = 1122 order by updated_at desc;\n\nselect * from crm_field_data where object_type = 'contact';\n\nSELECT * FROM activities WHERE uuid_to_bin('374fc8ed-3315-4c9f-9b25-318b7fd2928f') = uuid; # 76584262\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 248 and sa.provider = 'salesforce';\n\nSELECT * FROM crm_profiles where user_id = 24115; # 005QF000002CswMYAS\nSELECT * FROM users where id = 24115;\nSELECT * FROM accounts where id = 4002896;\nSELECT * FROM teams WHERE name LIKE '%adswerve%';\nSELECT * FROM opportunities where crm_configuration_id = 230 AND crm_provider_id IN (\"0069N000003GIQ9QAO\",\"0061r000019yGP9AAM\",\"0066900001S2KWlAAN\",\"0066900001TDpj2AAD\",\"0066900001b8uEwAAI\",\"0069N000001rQi0QAE\",\"006QF00000KD40mYAD\",\"006QF00000LzpRJYAZ\",\"0069N000002uomtQAA\",\"0069N000002xlMLQAY\",\"0066900001NV6ubAAD\",\"0061r00001HJp45AAD\",\"006QF00000uTlUoYAK\",\"006QF00000v0bZqYAI\");\nSELECT * FROM opportunities WHERE crm_configuration_id = 230 AND crm_provider_id = '0069N000003GIQ9QAO'; # 6272203\n\nSELECT u.id, u.email, ac.name, a.* FROM activities a\nJOIN users u ON a.user_id = u.id\nJOIN accounts ac ON a.account_id = ac.id\nWHERE\nuuid_to_bin('e3269598-b562-44fb-b5e9-9d2694dc63e0') = a.uuid or\nuuid_to_bin('66ddc3ab-4e15-45aa-af0c-248c1eece593') = a.uuid or\nuuid_to_bin('826bd328-e1cc-4213-b8d8-572454cacc07') = a.uuid;\n\nselect * from users where id = 5825;\nSELECT * FROM activities WHERE uuid_to_bin('e56aa2e8-231a-421b-ab1f-cb38ed2bf573') = uuid;\n\nselect * from activities where uuid_to_bin('91e13b2f-2d1b-45f8-b1fd-1141b6563782') = uuid;\n19594, 862\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 862 and sa.provider = 'salesforce';\n\nselect * from automated_reports where id = 36;\nselect ar.frequency, r.*, ar.* from automated_report_results r\njoin automated_reports ar on r.report_id = ar.id\nwhere ar.frequency != 'one_off';\n\nselect s.* from activity_searches s join users u ON s.user_id = u.id where u.team_id = 882;\nselect * from nudges n where n.activity_search_id\n\nselect * from teams where created_at > '2026-03-09';\nSELECT * FROM crm_layouts WHERE crm_configuration_id = 1065; # 1065\nSELECT * FROM crm_layout_entities WHERE crm_layout_id = 3617;\n\nselect * from users where team_id = 1 and name like '%Lukas%'; # 7160\n\nSELECT * FROM teams WHERE id = 575;\nselect * from opportunities where team_id = 575;\nSELECT * FROM teams WHERE name LIKE '%Integrum ESG%'; # 1126, 1065,\nselect * from opportunities where team_id = 1126;\nSELECT * FROM teams WHERE name LIKE '%Base%'; # 1125, 1063,\nselect * from opportunities where team_id = 1125;\nselect * from contacts c\nwhere c.team_id = 882;\n\nSELECT * FROM activities WHERE id = 76822967;\nSELECT * FROM crm_profiles WHERE user_id = 15440;\nSELECT * FROM crm_profiles WHERE crm_configuration_id = 555;\nSELECT * FROM crm_configurations WHERE id = 555;\nSELECT * FROM users WHERE id = 15440; # team. 581, gr. 15440, pl. 3911, act. field 162182\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 581 and sa.provider = 'salesforce';\n\nSELECT * FROM automated_report_results order by id desc;\n\nselect * from features;\nselect * from team_features where feature_id = 40;\n\nselect * from teams where id = 556;\n\nselect * from automated_reports;\nwhere id = 54; # 4fdd41f6-dcf0-30d0-b339-7345381b6044 , [\"pdf\",\"podcast\"]\nSELECT * FROM automated_report_results WHERE uuid_to_bin('822fa41b-afd3-43a9-a248-86b0e36f3131') = uuid;\nselect * from automated_report_results order by id desc;\nSELECT * FROM automated_report_results WHERE id = 1919;\n\nselect * from automated_report_results WHERE report_id = 54;\n\nselect * from opportunities where id = 7594349;\n\nSELECT * FROM teams WHERE name LIKE '%Les%'; # 711, 692, 16067 - jiminnyintegration@lesmills.com\nselect * from playbooks where team_id = 711; # event 226147\nSELECT * FROM playbook_categories WHERE playbook_id = 5515;\nSELECT * FROM crm_fields WHERE crm_configuration_id = 692 and object_type = 'event';\nSELECT * FROM crm_fields WHERE id = 226147;\nSELECT * FROM crm_field_values WHERE crm_field_id = 226147;\n\nSELECT * FROM crm_configurations WHERE id = 692;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 711 and sa.provider = 'salesforce';\n\nSELECT * FROM crm_profiles cp JOIN users u on u.id = cp.user_id WHERE u.team_id = 711;\n\nselect * from leads;\n\nselect * from calendars;\n\nSELECT\n t.id AS team_id,\n t.name,\n LOWER(SUBSTRING_INDEX(c.calendar_provider_id, '@', -1)) AS calendar_domain\nFROM teams t\nJOIN users u ON u.team_id = t.id\nJOIN calendars c ON c.user_id = u.id AND c.status = 'active' AND c.calendar_provider_id LIKE '%@%'\nLEFT JOIN team_domains td\n ON td.team_id = t.id\n AND td.deleted_at IS NULL\n AND td.domain = LOWER(SUBSTRING_INDEX(c.calendar_provider_id, '@', -1))\nGROUP BY t.id, t.name, calendar_domain\nORDER BY t.name, calendar_domain;\n\nselect * from users u join calendars c on c.user_id = u.id\nwhere u.team_id = 882;\n\n\nselect * from activities where id = 74049485; # team 563 crm 537\nselect * from activities where id = 73272382; # team 563 crm 537\nselect * from activities where id = 64400389; # team 563 crm 537\nselect * from activities where id = 58081273; # team 563 crm 537\nselect * from activities where id = 54520297; # team 563 crm 537\nselect * from participants where activity_id = 58081273;\n\nselect * from activities where crm_configuration_id = 537 and provider = 'aircall'\nand account_id = 19003658 order by updated_at desc;\n\nselect * from contacts where crm_configuration_id = 537 and id = 35957759;\nselect * from accounts where crm_configuration_id = 537 and id = 19003658;\n\nselect * from automated_report_results where id = 1976;\nselect * from automated_reports where id = 583;\nselect * from activity_searches where id = 87714;\nselect * from activity_search_filters where activity_search_id = 87714;\n\nSELECT * FROM activities WHERE uuid_to_bin('8827f672-202d-4162-9d04-73ff5f0566a9') = uuid\nor uuid_to_bin('47842446-af51-4bcb-854f-cc6560290101') = uuid;\n\nSELECT * FROM crm_configurations WHERE provider = 'hubspot';\nselect * from rate_limits;\n\nselect * from automated_report_results where media_type = 'pdf' and status = 2\nand id IN (18, 1872);\nselect * from automated_reports where id = 54;\nSELECT * FROM users WHERE id IN (24623,29443,29613);\n\nSELECT * FROM automated_report_results WHERE uuid_to_bin('822fa41b-afd3-43a9-a248-86b0e36f3131') = uuid;\n\nselect * from text_relays where created_at > '2026-05-01';\nand id IN (32415, 32416);\n# and id = 32412;\n\nselect * from users where team_id = 2 and email like '%scott%' and id = 29510;\n\nSELECT * FROM activities WHERE uuid_to_bin('67cebfc2-ed56-44a2-8c68-7a0286ed8618') = uuid; # 79763436\n\nSELECT email_provider_id, COUNT(*) as count, GROUP_CONCAT(id) as ids, GROUP_CONCAT(status) as statuses\nFROM text_relays\nWHERE email_provider_id IN ('19e2027868a64b42', '19e2033ed8ea6b10')\nGROUP BY email_provider_id;\nSELECT id, status, telephony_provider_id, created_at\nFROM activities\nWHERE id IN (80028719, 80028846);\nSELECT id, status, code, email_sent_at, created_at, updated_at\nFROM text_relays\nWHERE id IN (32415, 32416);\nSELECT id, status, code, sender, recipient, created_at\nFROM text_relays\nWHERE sender LIKE '%mario.georgiev%' OR sender LIKE '%stoyan.tomov%'\nORDER BY created_at DESC\nLIMIT 10;\n\nSELECT id, uuid, status, code, sender, recipient, created_at, updated_at\nFROM text_relays\nWHERE uuid = uuid_to_bin('0626141c-27a6-4d8c-aff8-7c8020a2c656');\n\n# ***************\nSELECT DISTINCT u.id, u.email, u.name, u.softphone_number, COUNT(a.id) as sms_count\nFROM users u\nINNER JOIN activities a ON u.id = .user_id\nWHERE a.type LIKE 'sms%'\nAND a.created_at > DATE_SUB(NOW(), INTERVAL 30 DAY)\nGROUP BY u.id, u.email, u.name, u.softphone_number\nORDER BY sms_count DESC;\n\nselect * from teams where id = 1;\n\nselect * from roles;\n\n\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1117 and sa.provider = 'hubspot';\nSELECT * FROM activities WHERE uuid_to_bin('8024fffb-2df7-4017-91f4-d9f896050248') = uuid; # 79933459 YES\nSELECT * FROM activities WHERE uuid_to_bin('25529043-8094-4781-927f-4f4da2a8185c') = uuid; # 80186192 NO\nSELECT * FROM crm_configurations WHERE id = 1053;\nSELECT * FROM teams WHERE id = 1117;\nselect * from users where id = 30249;\nselect * from playbooks where id = 5473;\nselect * from playbook_categories where id = 43783;\nselect * from playbook_categories where playbook_id = 5473;\nselect * from crm_fields where id = 659242;\nselect * from crm_field_values where crm_field_id = 659242;\n\nSELECT * FROM crm_field_data fd\n# JOIN crm_fields f ON fd.crm_field_id = f.id\n# JOIN activities a ON fd.activity_id = a.id\nWHERE activity_id = 79933459\n# AND f.crm_provider_id = 'hs_activity_type';\n\n\nSELECT * FROM activity_messages;\nselect * from text_relays where created_at > '2026-05-01';\nselect * from activities where user_id IN (7160, 18608) and created_at > '2026-05-22' order by id desc;\nselect * from users where team_id = 1 and id IN (18608, 13934, 7160);\nselect * from activities where user_id = 7160 order by id desc limit 10;\n\nselect * from accounts where team_id = 1 and name = 'Column5';\n\nselect * from users where name like '%Subra%'; # 31054, 1117\nselect * from teams where id = 1117;\nselect * from activity_searches where user_id = 31054;\nselect * from activity_search_filters where activity_search_id IN (88882, 88902);","depth":4,"on_screen":true,"value":"SELECT * FROM teams WHERE name LIKE '%litify%'; # 1069, 994, 24993\nSELECT * FROM users WHERE id = 25061;\nSELECT * FROM crm_profiles WHERE crm_configuration_id = 994;\nSELECT * FROM crm_profiles WHERE user_id = 25061;\n\nselect * from crm_configurations where id = 834;\nSELECT * FROM teams WHERE id = 882;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 882 and sa.provider = 'hubspot';\n\nSELECT * FROM crm_configurations where id = 882; # 933 - GoGlobal\nSELECT * FROM opportunities WHERE team_id = 933 order by updated_at desc;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 933 and sa.provider = 'hubspot';\n\nSELECT * FROM crm_configurations WHERE provider = 'hubspot' and crm_provider_id = 7270388;\n\nSELECT * FROM contacts where crm_configuration_id = 834;\nSELECT * FROM opportunities WHERE team_id = 933\n# AND crm_provider_id IN ('20131586060','46017317898','52543911090','53451356564','54101251892','54323768459');\nAND id IN (8482561,18352941,19042734,19232139,19445140,19472541);\nSELECT * FROM opportunity_contacts\nWHERE opportunity_id IN (8482561,18352941,19042734,19232139,19445140,19472541);\n\n# ********************************************************************\nSELECT * FROM crm_configurations where id = 485; #\nSELECT * FROM opportunities WHERE team_id = 933 order by updated_at desc;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 933 and sa.provider = 'hubspot';\n\nselect crm.provider, l.* from leads l join crm_configurations crm on l.crm_configuration_id = crm.id\nwhere crm.provider NOT IN ('salesforce', 'integration-app', 'bullhorn', 'copper')\n# and l.converted_at IS NOT NULL\n;\n\n# ********************************************************************\nSELECT * FROM activities a WHERE type IN ('email-inbound', 'email-outbound')\nand opportunity_id IS NULL\norder by id desc;\n\nSELECT * FROM teams WHERE id = 604; # 598\nSELECT * FROM activities WHERE id = 74410828; # chelseaw@allvoices.co\nSELECT * FROM accounts WHERE id = 20068382;\nSELECT * FROM accounts WHERE id = 35186038;\n\nSELECT * FROM contacts WHERE team_id = 852 and updated_at > '2026-01-23 12:30:00' order by updated_at desc;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 559 and sa.provider = 'hubspot';\n\nSELECT * FROM activities WHERE uuid_to_bin('cb6342b6-a183-401c-b0af-ede92b2ae763') = uuid;\nselect * from sidekick_settings where team_id = 781;\n\nSELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id = 26651871; # Teya\nSELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id = 7562435;\nSELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id = 8420347; # opflit 2100\n\nSELECT * FROM crm_layouts WHERE crm_configuration_id = 711;\nSELECT * FROM activities where crm_configuration_id = 711 and crm_provider_id IS NULL\nand is_internal = 0 and status = 'completed'\norder by id desc;\n\nSELECT * FROM crm_layout_entities\nWHERE crm_layout_id IN (2352, 2353);\n;\n\nSELECT * FROM crm_configurations where provider = 'hubspot' and id = 530;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 556 and sa.provider = 'hubspot';\n\nSELECT * FROM activities WHERE uuid_to_bin('c6ca4b22-7738-4563-a95d-b8a9598924ae') = uuid;\nSELECT * FROM activities WHERE uuid_to_bin('442abb2b-28bd-4be8-9c25-19e9bf02766d') = uuid;\nselect * from contacts\nwhere crm_configuration_id = 530\nand crm_provider_id = 872252;\n\nselect * from activities where crm_configuration_id = 530\nand user_id = 14343 and type like '%softphone%'\nand created_at between '2026-01-28 15:00:00' and '2026-01-28 15:10:00';\n\nSELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id = 25666868; # Teya\nSELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id = 8646335; # Teya\nSELECT * FROM crm_configurations where provider = 'hubspot' and crm_provider_id IN (5933397);\n\n\nSELECT t.name, t.id, t.owner_id, c.id, c.provider, c.crm_base_url FROM teams t\nJOIN crm_configurations c ON t.id = c.team_id\nWHERE t.status = 'active';\n\nSELECT * FROM teams where id = 1091;\nSELECT * FROM crm_configurations where team_id = 1091;\nSELECT * FROM activity_providers where team_id = 1091;\nSELECT * FROM activities where crm_configuration_id = 1024 and type IN ('softphone', 'softphone-outbound')\nand provider NOT IN ('hubspot', 'aircall')\n# and telephony_provider_id = '019c1131-a22f-4792-b9ea-20adf6a02ed0'\norder by id desc;\n\n\nSELECT * FROM teams WHERE name LIKE '%Leadventure%';\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1091 and sa.provider = 'salesforce';\n\nSELECT * FROM teams WHERE name LIKE '%Wilson%'; # 862, 812\nSELECT * FROM teams where id = 862;\nSELECT * FROM crm_configurations where team_id = 862;\nSELECT * FROM activity_providers where team_id = 862;\nSELECT * FROM activities where crm_configuration_id = 812 and type IN ('softphone', 'softphone-outbound')\nand provider NOT IN ('hubspot', 'aircall')\n# and telephony_provider_id = '019c1131-a22f-4792-b9ea-20adf6a02ed0'\norder by id desc;\n\n\nSELECT t.id, crm.id, crm.provider, ap.* FROM teams t\njoin crm_configurations crm on t.id = crm.team_id\njoin activity_providers ap on t.id = ap.team_id\nwhere t.status = 'active' and ap.is_enabled = 1\nand crm.provider = 'hubspot'\nand ap.provider NOT IN ('hubspot', 'aircall', 'uploader', 'gong', 'twilio', 'zoom-bot', 'google-meet', 'ms-teams',\n 'outreach', 'close', 'ringcentral', 'dialpad', 'zoom-phone');\n\nSELECT * FROM teams where id = 1068;\nSELECT * FROM crm_configurations where team_id = 1068;\nSELECT * FROM activity_providers where team_id = 1068;\n\nSELECT * FROM activities a\nwhere crm_configuration_id = 993 and type IN ('softphone', 'softphone-outbound')\nand a.provider NOT IN ('hubspot', 'uploader', 'gong', 'twilio', 'google-meet', 'ms-teams','close'\n )\n# and telephony_provider_id = '019c1131-a22f-4792-b9ea-20adf6a02ed0'\norder by a.id desc;\n\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1068 and sa.provider = 'hubspot';\n\n# ********************************************************************\n# ********************************************************************\n# ********************************************************************\nSELECT * FROM crm_configurations where id = 882; # 933 - GoGlobal , portalId: 6017093\nSELECT * FROM opportunities WHERE team_id = 933 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 933 and updated_at > '2026-02-06 00:00:00' order by updated_at desc;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 933 and sa.provider = 'hubspot';\n\n# ********************************************************************\nSELECT * FROM crm_configurations where id = 834; # 882 - AnyVan , portalId: 5468262\nSELECT * FROM contacts WHERE crm_configuration_id = 834 and updated_at > '2026-03-30 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE crm_configuration_id = 834 and updated_at > '2026-03-04 08:00:00' order by updated_at desc;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 882 and sa.provider = 'hubspot';\nselect * from crm_layouts where crm_configuration_id = 834;\nselect * from crm_layout_entities where crm_layout_id = 2780;\nselect * from crm_fields where id IN (321153,321192,321193,321194);\n\nSELECT * FROM opportunities WHERE crm_configuration_id = 834 and id = 10993426;\n# ********************************************************************\nSELECT * FROM crm_configurations where id = 988; # 1057 - Teya (543ce4f4-168c-4571-91ea-5b35c253f06f) , portalId: 26651871\nSELECT * FROM opportunities WHERE team_id = 1057 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 1057 and updated_at > '2026-02-04 00:00:00' order by updated_at desc;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1057 and sa.provider = 'hubspot';\n\nSELECT * FROM crm_configurations where id = 533; # 559 - Connectd , portalId: 6710988\nSELECT * FROM opportunities WHERE team_id = 559 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 559 and updated_at > '2026-02-04 00:00:00' order by updated_at desc;\n\nSELECT * FROM crm_configurations where id = 801; # 852 - Rise Vision , portalId: 2700250\nSELECT * FROM opportunities WHERE team_id = 852 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 852 and updated_at > '2026-02-04 00:00:00' order by updated_at desc; # 6th last\n\nSELECT * FROM crm_configurations where id = 962; # 1034 - evergrowth.io , portalId: 143180990\nSELECT * FROM opportunities WHERE team_id = 1034 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 1034 and updated_at > '2026-02-04 00:00:00' order by updated_at desc;\n\n# ********************************************************************\nSELECT * FROM crm_configurations where id = 1037; # 1102 - Jibble , portalId: 6649755\nSELECT * FROM opportunities WHERE team_id = 1102 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 1102 and updated_at > '2026-02-04 12:30:00' order by updated_at desc; # 8\n\nSELECT * FROM crm_configurations where id = 1015; # 1049 - Travefy , portalId: 48904401\nSELECT * FROM opportunities WHERE team_id = 1049 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 1049 and updated_at > '2026-02-04 12:30:00' order by updated_at desc; # 20\n\nSELECT * FROM crm_configurations where id = 64; # 70 - SalaryFinance , portalId: 3404115\nSELECT * FROM opportunities WHERE team_id = 70 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 70 and updated_at > '2026-02-04 12:30:00' order by updated_at desc; # 6th last\n\nSELECT * FROM crm_configurations where id = 802; # 853 - Street Group , portalId: 7658438\nSELECT * FROM opportunities WHERE team_id = 853 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 853 and updated_at > '2026-02-04 12:30:00' order by updated_at desc; # 10\n\nSELECT * FROM crm_configurations where id = 872; # 921 - In Professional Development , portalId: 9238273\nSELECT * FROM opportunities WHERE team_id = 921 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 921 and updated_at > '2026-02-04 12:30:00' order by updated_at desc; # 2\n\n# ********************************************************************\nSELECT * FROM crm_configurations where id = 550; # 576 - SeedLegals , portalId: 3028661\nSELECT * FROM opportunities WHERE team_id = 576 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 576 and updated_at > '2026-02-05 14:00:00' order by updated_at desc;\n\nSELECT * FROM crm_configurations where id = 989; # 1058 - rtaoutdoor.com , portalId: 22371204\nSELECT * FROM opportunities WHERE team_id = 1058 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 1058 and updated_at > '2026-02-05 14:00:00' order by updated_at desc;\n\nSELECT * FROM crm_configurations where id = 896; # 946 - Mintago , portalId: 6621281\nSELECT * FROM opportunities WHERE team_id = 946 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 946 and updated_at > '2026-02-05 14:00:00' order by updated_at desc;\n\nSELECT * FROM crm_configurations where id = 617; # 641 - PCS , portalId: 5244937\nSELECT * FROM opportunities WHERE team_id = 641 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 641 and updated_at > '2026-02-05 14:00:00' order by updated_at desc; # 7th\n# ********************************************************************\nSELECT * FROM crm_configurations where id = 649; # 670 - Eventeny , portalId: 4492849\nSELECT * FROM opportunities WHERE team_id = 670 and updated_at > '2026-02-18 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 670 and updated_at > '2026-02-09 08:00:00' order by updated_at desc; #\n\nSELECT * FROM crm_configurations where id = 48; # 51 - CleanCloud , portalId: 4373137\nSELECT * FROM opportunities WHERE team_id = 51 and updated_at > '2026-03-04 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 51 and updated_at > '2026-02-09 08:00:00' order by updated_at desc;\nselect * from users where team_id = 51; # 7783\nSELECT * FROM groups WHERE uuid_to_bin('8a8d2cb6-8b55-4fa3-8b5c-5f0e3d8de59a') = uuid; # 1130\nselect * from activity_searches where user_id = 7783;\nselect * from activity_search_filters where activity_search_id IN (32291, 32292);\n\nSELECT asf.activity_search_id, asf.id, asf.value\nFROM activity_search_filters asf\nWHERE asf.filter = 'group_id'\nAND asf.value IN (\n SELECT CONCAT(\n HEX(SUBSTR(uuid, 5, 4)), '-',\n HEX(SUBSTR(uuid, 3, 2)), '-',\n HEX(SUBSTR(uuid, 1, 2)), '-',\n HEX(SUBSTR(uuid, 9, 2)), '-',\n HEX(SUBSTR(uuid, 11))\n )\n FROM groups\n WHERE deleted_at IS NOT NULL\n);\n\nSELECT * FROM crm_configurations where id = 272; # 290 - Bonham & Brook , portalId: 5705856\nSELECT * FROM opportunities WHERE team_id = 290 and updated_at > '2026-02-05 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 290 and updated_at > '2026-02-09 08:00:00' order by updated_at desc; # 6th\n# ********************************************************************\nSELECT * FROM crm_configurations where provider = 'hubspot';\nSELECT * FROM crm_configurations where id = 1056; # 1119 - Chromatic , portalId: 45602133\nSELECT * FROM opportunities WHERE team_id = 1119 and remotely_created_at > '2026-02-01 00:00:00' order by updated_at desc;\nSELECT * FROM opportunities WHERE team_id = 1119 and updated_at > '2026-02-09 09:00:00' order by updated_at desc; # null\n# ********************************************************************\n\nselect * from contacts where crm_provider_id = '003Uu00000ojD4NIAU';\nselect\n cp.*\n# DISTINCT t.id\n# cp.id, cp.user_id, t.id, cp.crm_configuration_id, cp.contact_fields\nFROM crm_profiles cp\nJOIN crm_configurations crm on crm.id = cp.crm_configuration_id\nJOIN users u on u.id = cp.user_id\nJOIN teams t ON t.id = crm.team_id\nWHERE crm.provider = 'salesforce' and t.status = 'active'\n and cp.archived_at IS NULL and u.deleted_at IS NULL\n and t.id NOT IN (1093)\n and t.id = 2\n and cp.contact_fields IS NULL;\n# and c.crm_provider_id = '003Uu00000ojD4NIAU';\n\nSELECT * FROM users WHERE id = 26484;\nSELECT * FROM crm_profiles WHERE user_id = 26484;\nSELECT * FROM social_accounts WHERE sociable_id = 26484;\nSELECT * FROM crm_configurations where provider = 'salesforce';\nselect * from users where id IN (10022, 10403);\nselect * from users where team_id IN (526);\nselect * from teams where id IN (526, 532);\nselect * from crm_configurations where id IN (500, 516);\nselect * from crm_profiles where crm_configuration_id IN (500, 516) and user_id IN (10022, 10403);\nselect * from contacts where crm_configuration_id IN (500, 516) and crm_provider_id = '003Uu00000ojD4NIAU';\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 526 and sa.provider = 'salesforce';\nselect * from team_settings where team_id IN (526, 532);\n\nselect * from users where id IN (22824);\nselect * from crm_profiles where crm_configuration_id IN (1026);\n\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1093 and sa.provider = 'salesforce';\n\nselect * from teams where id = 1099;\nselect * from users where id = 29643\n\nselect * from activity_processing_states;\n\nSELECT * FROM teams where name LIKE '%Fare%'; # 233\nSELECT * FROM opportunities where crm_configuration_id = 215\n# and crm_provider_id = 'oppo_ogESZf2P50nDrd1nGPvKDXeA6sSaTN5v51Lp4ayVzKR'\n;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1088 and sa.provider = 'hubspot';\n\nSELECT * FROM teams order by updated_at DESC\nSELECT * FROM crm_configurations WHERE id = 1019; # SimpleConsign 1088 - no social account\n\nselect * from crm_configurations where provider = 'pipedrive';\n\nselect * from teams where id = 957;\nselect * from crm_configurations where id = 957;\n\nSELECT * FROM teams WHERE name LIKE '%Prolific%'; # 544, 518, 10743\nSELECT * FROM opportunities where crm_configuration_id = 518 order by id desc;\n\nselect * from users where team_id = 1; # 26726 - Gabriela Dureva\nSELECT * FROM opportunities where user_id = 26726; # 16834447 - Prolific\nselect * from activities where user_id = 26726 order by id desc;\nselect * from contacts where crm_configuration_id = 1\nand email IN ('charlotte.ward@prolific.com', 'frankie.bryant@prolific.com'); # 2094416, 2093620\nSELECT * FROM contacts WHERE id = 6284931;\n\nSELECT p.* FROM activities a JOIN participants p ON a.id = p.activity_id\nWHERE a.user_id = 26726 and p.lead_id IN (2094416, 2093620) and a.created_at > '2026-01-01 00:00:00' order by p.email;\n\nselect * from activities where id IN (75509259,75509261,75509261,75511034,75026464,75517602,75517605);\nselect * from crm_configurations where id = 1;\n\n43801692-1aeb-32ce-acba-5b80a479701a\n44c3c9cf-6f5e-75f3-8179-bc9f75dd2b1b\n405975c0-b3d0-7aaa-821f-09d59cae6dd1\n4caf848d-4bed-2299-b248-7788d41f9fca\n49bedc3f-f196-eef3-89c3-dea6a3b4aa63\n43420989-a09d-b8f8-9806-c8bbf7a02aac\n\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1 and sa.provider = 'salesforce';\n\nSELECT * FROM activities WHERE id = 75461988;\n\nSELECT * FROM activities WHERE uuid_to_bin('d6c5052e-e972-49e9-8912-26f2f7d6c5f6') = uuid;\n\nselect * from contacts where id = 17900517;\n\nselect * from contact_roles cr join crm_configurations crm on cr.crm_configuration_id = crm.id\nwhere crm.provider != 'salesforce';\n\nselect * from users where id = 21047;\nSELECT * FROM crm_configurations WHERE id = 892;\nSELECT * FROM teams WHERE id = 942;\nselect * from opportunities where team_id = 942 order by updated_at desc;\nselect * from contacts where team_id = 942 order by updated_at desc;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 942 and sa.provider = 'hubspot';\n\nSELECT * FROM opportunities where team_id = 1 and crm_provider_id IN ('006Pq00000NeH6XIAV', '006Pq000007z8kdIAA'); # 10697889, 6621430\nSELECT * FROM crm_configurations WHERE id = 1;\nSELECT * FROM teams WHERE crm_id = 1;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1 and sa.provider = 'salesforce';\n\nselect id, user_id, opportunity_fields from crm_profiles where crm_configuration_id = 1\nSELECT * FROM opportunities where team_id = 1 order by updated_at desc; # 10697889, 6621430\n\nselect * from teams where id = 852;\nselect * from groups where id = 2286;\nselect * from sidekick_settings where team_id = 852;\nselect * from default_activity_types where team_id = 852;\n\n\nSELECT cc.provider, cc.id, p.id, u.*\nFROM users u\nLEFT JOIN crm_profiles p ON u.id = p.user_id AND p.id IS NULL -- no profile\nINNER JOIN teams t ON u.team_id = t.id AND t.status = 'active' -- team is active\nINNER JOIN crm_configurations cc ON t.crm_id = cc.id\nWHERE u.status = 1 AND u.deleted_at IS NULL\nAND u.crm_required = 1\nAND u.team_id = 1\nORDER BY u.team_id;\n\nSELECT * FROM crm_profiles cp where cp.crm_configuration_id = 1 and cp.user_id IN (\n18481\n );\n\nSELECT cc.provider, cc.id, p.id, u.*\nFROM users u\nLEFT JOIN crm_profiles p ON u.id = p.user_id\nINNER JOIN teams t ON u.team_id = t.id AND t.status = 'active'\nINNER JOIN crm_configurations cc ON t.crm_id = cc.id\nWHERE u.status = 1\n AND u.deleted_at IS NULL\n AND u.crm_required = 1\n# AND u.team_id = 1\n AND p.id IS NULL -- Move this condition to WHERE clause\nORDER BY u.team_id;\n\nSELECT * FROM opportunities WHERE id = 20002609;\nselect * from teams where id = 1122; # Velatir, 29953 - christian@velatir.com\nselect * from crm_configurations where id = 1060;\nselect * from crm_layouts where crm_configuration_id = 1060;\nSELECT * FROM crm_layout_entities WHERE crm_layout_id = 3596;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1122 and sa.provider = 'hubspot';\nselect * from opportunities where team_id = 1122 order by updated_at desc;\n\nselect * from crm_field_data where object_type = 'contact';\n\nSELECT * FROM activities WHERE uuid_to_bin('374fc8ed-3315-4c9f-9b25-318b7fd2928f') = uuid; # 76584262\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 248 and sa.provider = 'salesforce';\n\nSELECT * FROM crm_profiles where user_id = 24115; # 005QF000002CswMYAS\nSELECT * FROM users where id = 24115;\nSELECT * FROM accounts where id = 4002896;\nSELECT * FROM teams WHERE name LIKE '%adswerve%';\nSELECT * FROM opportunities where crm_configuration_id = 230 AND crm_provider_id IN (\"0069N000003GIQ9QAO\",\"0061r000019yGP9AAM\",\"0066900001S2KWlAAN\",\"0066900001TDpj2AAD\",\"0066900001b8uEwAAI\",\"0069N000001rQi0QAE\",\"006QF00000KD40mYAD\",\"006QF00000LzpRJYAZ\",\"0069N000002uomtQAA\",\"0069N000002xlMLQAY\",\"0066900001NV6ubAAD\",\"0061r00001HJp45AAD\",\"006QF00000uTlUoYAK\",\"006QF00000v0bZqYAI\");\nSELECT * FROM opportunities WHERE crm_configuration_id = 230 AND crm_provider_id = '0069N000003GIQ9QAO'; # 6272203\n\nSELECT u.id, u.email, ac.name, a.* FROM activities a\nJOIN users u ON a.user_id = u.id\nJOIN accounts ac ON a.account_id = ac.id\nWHERE\nuuid_to_bin('e3269598-b562-44fb-b5e9-9d2694dc63e0') = a.uuid or\nuuid_to_bin('66ddc3ab-4e15-45aa-af0c-248c1eece593') = a.uuid or\nuuid_to_bin('826bd328-e1cc-4213-b8d8-572454cacc07') = a.uuid;\n\nselect * from users where id = 5825;\nSELECT * FROM activities WHERE uuid_to_bin('e56aa2e8-231a-421b-ab1f-cb38ed2bf573') = uuid;\n\nselect * from activities where uuid_to_bin('91e13b2f-2d1b-45f8-b1fd-1141b6563782') = uuid;\n19594, 862\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 862 and sa.provider = 'salesforce';\n\nselect * from automated_reports where id = 36;\nselect ar.frequency, r.*, ar.* from automated_report_results r\njoin automated_reports ar on r.report_id = ar.id\nwhere ar.frequency != 'one_off';\n\nselect s.* from activity_searches s join users u ON s.user_id = u.id where u.team_id = 882;\nselect * from nudges n where n.activity_search_id\n\nselect * from teams where created_at > '2026-03-09';\nSELECT * FROM crm_layouts WHERE crm_configuration_id = 1065; # 1065\nSELECT * FROM crm_layout_entities WHERE crm_layout_id = 3617;\n\nselect * from users where team_id = 1 and name like '%Lukas%'; # 7160\n\nSELECT * FROM teams WHERE id = 575;\nselect * from opportunities where team_id = 575;\nSELECT * FROM teams WHERE name LIKE '%Integrum ESG%'; # 1126, 1065,\nselect * from opportunities where team_id = 1126;\nSELECT * FROM teams WHERE name LIKE '%Base%'; # 1125, 1063,\nselect * from opportunities where team_id = 1125;\nselect * from contacts c\nwhere c.team_id = 882;\n\nSELECT * FROM activities WHERE id = 76822967;\nSELECT * FROM crm_profiles WHERE user_id = 15440;\nSELECT * FROM crm_profiles WHERE crm_configuration_id = 555;\nSELECT * FROM crm_configurations WHERE id = 555;\nSELECT * FROM users WHERE id = 15440; # team. 581, gr. 15440, pl. 3911, act. field 162182\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 581 and sa.provider = 'salesforce';\n\nSELECT * FROM automated_report_results order by id desc;\n\nselect * from features;\nselect * from team_features where feature_id = 40;\n\nselect * from teams where id = 556;\n\nselect * from automated_reports;\nwhere id = 54; # 4fdd41f6-dcf0-30d0-b339-7345381b6044 , [\"pdf\",\"podcast\"]\nSELECT * FROM automated_report_results WHERE uuid_to_bin('822fa41b-afd3-43a9-a248-86b0e36f3131') = uuid;\nselect * from automated_report_results order by id desc;\nSELECT * FROM automated_report_results WHERE id = 1919;\n\nselect * from automated_report_results WHERE report_id = 54;\n\nselect * from opportunities where id = 7594349;\n\nSELECT * FROM teams WHERE name LIKE '%Les%'; # 711, 692, 16067 - jiminnyintegration@lesmills.com\nselect * from playbooks where team_id = 711; # event 226147\nSELECT * FROM playbook_categories WHERE playbook_id = 5515;\nSELECT * FROM crm_fields WHERE crm_configuration_id = 692 and object_type = 'event';\nSELECT * FROM crm_fields WHERE id = 226147;\nSELECT * FROM crm_field_values WHERE crm_field_id = 226147;\n\nSELECT * FROM crm_configurations WHERE id = 692;\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 711 and sa.provider = 'salesforce';\n\nSELECT * FROM crm_profiles cp JOIN users u on u.id = cp.user_id WHERE u.team_id = 711;\n\nselect * from leads;\n\nselect * from calendars;\n\nSELECT\n t.id AS team_id,\n t.name,\n LOWER(SUBSTRING_INDEX(c.calendar_provider_id, '@', -1)) AS calendar_domain\nFROM teams t\nJOIN users u ON u.team_id = t.id\nJOIN calendars c ON c.user_id = u.id AND c.status = 'active' AND c.calendar_provider_id LIKE '%@%'\nLEFT JOIN team_domains td\n ON td.team_id = t.id\n AND td.deleted_at IS NULL\n AND td.domain = LOWER(SUBSTRING_INDEX(c.calendar_provider_id, '@', -1))\nGROUP BY t.id, t.name, calendar_domain\nORDER BY t.name, calendar_domain;\n\nselect * from users u join calendars c on c.user_id = u.id\nwhere u.team_id = 882;\n\n\nselect * from activities where id = 74049485; # team 563 crm 537\nselect * from activities where id = 73272382; # team 563 crm 537\nselect * from activities where id = 64400389; # team 563 crm 537\nselect * from activities where id = 58081273; # team 563 crm 537\nselect * from activities where id = 54520297; # team 563 crm 537\nselect * from participants where activity_id = 58081273;\n\nselect * from activities where crm_configuration_id = 537 and provider = 'aircall'\nand account_id = 19003658 order by updated_at desc;\n\nselect * from contacts where crm_configuration_id = 537 and id = 35957759;\nselect * from accounts where crm_configuration_id = 537 and id = 19003658;\n\nselect * from automated_report_results where id = 1976;\nselect * from automated_reports where id = 583;\nselect * from activity_searches where id = 87714;\nselect * from activity_search_filters where activity_search_id = 87714;\n\nSELECT * FROM activities WHERE uuid_to_bin('8827f672-202d-4162-9d04-73ff5f0566a9') = uuid\nor uuid_to_bin('47842446-af51-4bcb-854f-cc6560290101') = uuid;\n\nSELECT * FROM crm_configurations WHERE provider = 'hubspot';\nselect * from rate_limits;\n\nselect * from automated_report_results where media_type = 'pdf' and status = 2\nand id IN (18, 1872);\nselect * from automated_reports where id = 54;\nSELECT * FROM users WHERE id IN (24623,29443,29613);\n\nSELECT * FROM automated_report_results WHERE uuid_to_bin('822fa41b-afd3-43a9-a248-86b0e36f3131') = uuid;\n\nselect * from text_relays where created_at > '2026-05-01';\nand id IN (32415, 32416);\n# and id = 32412;\n\nselect * from users where team_id = 2 and email like '%scott%' and id = 29510;\n\nSELECT * FROM activities WHERE uuid_to_bin('67cebfc2-ed56-44a2-8c68-7a0286ed8618') = uuid; # 79763436\n\nSELECT email_provider_id, COUNT(*) as count, GROUP_CONCAT(id) as ids, GROUP_CONCAT(status) as statuses\nFROM text_relays\nWHERE email_provider_id IN ('19e2027868a64b42', '19e2033ed8ea6b10')\nGROUP BY email_provider_id;\nSELECT id, status, telephony_provider_id, created_at\nFROM activities\nWHERE id IN (80028719, 80028846);\nSELECT id, status, code, email_sent_at, created_at, updated_at\nFROM text_relays\nWHERE id IN (32415, 32416);\nSELECT id, status, code, sender, recipient, created_at\nFROM text_relays\nWHERE sender LIKE '%mario.georgiev%' OR sender LIKE '%stoyan.tomov%'\nORDER BY created_at DESC\nLIMIT 10;\n\nSELECT id, uuid, status, code, sender, recipient, created_at, updated_at\nFROM text_relays\nWHERE uuid = uuid_to_bin('0626141c-27a6-4d8c-aff8-7c8020a2c656');\n\n# ***************\nSELECT DISTINCT u.id, u.email, u.name, u.softphone_number, COUNT(a.id) as sms_count\nFROM users u\nINNER JOIN activities a ON u.id = .user_id\nWHERE a.type LIKE 'sms%'\nAND a.created_at > DATE_SUB(NOW(), INTERVAL 30 DAY)\nGROUP BY u.id, u.email, u.name, u.softphone_number\nORDER BY sms_count DESC;\n\nselect * from teams where id = 1;\n\nselect * from roles;\n\n\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1117 and sa.provider = 'hubspot';\nSELECT * FROM activities WHERE uuid_to_bin('8024fffb-2df7-4017-91f4-d9f896050248') = uuid; # 79933459 YES\nSELECT * FROM activities WHERE uuid_to_bin('25529043-8094-4781-927f-4f4da2a8185c') = uuid; # 80186192 NO\nSELECT * FROM crm_configurations WHERE id = 1053;\nSELECT * FROM teams WHERE id = 1117;\nselect * from users where id = 30249;\nselect * from playbooks where id = 5473;\nselect * from playbook_categories where id = 43783;\nselect * from playbook_categories where playbook_id = 5473;\nselect * from crm_fields where id = 659242;\nselect * from crm_field_values where crm_field_id = 659242;\n\nSELECT * FROM crm_field_data fd\n# JOIN crm_fields f ON fd.crm_field_id = f.id\n# JOIN activities a ON fd.activity_id = a.id\nWHERE activity_id = 79933459\n# AND f.crm_provider_id = 'hs_activity_type';\n\n\nSELECT * FROM activity_messages;\nselect * from text_relays where created_at > '2026-05-01';\nselect * from activities where user_id IN (7160, 18608) and created_at > '2026-05-22' order by id desc;\nselect * from users where team_id = 1 and id IN (18608, 13934, 7160);\nselect * from activities where user_id = 7160 order by id desc limit 10;\n\nselect * from accounts where team_id = 1 and name = 'Column5';\n\nselect * from users where name like '%Subra%'; # 31054, 1117\nselect * from teams where id = 1117;\nselect * from activity_searches where user_id = 31054;\nselect * from activity_search_filters where activity_search_id IN (88882, 88902);","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"}]...
|
-2226595733419206362
|
8179532969745786684
|
visual_change
|
accessibility
|
NULL
|
Project: faVsco.js, menu
#12121 on JY-20963-fix-im Project: faVsco.js, menu
#12121 on JY-20963-fix-import-on-deleted-entity, menu
Start Listening for PHP Debug Connections
ServiceTest
Run 'ServiceTest'
Debug 'ServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
4
348
9
103
4
Previous Highlighted Error
Next Highlighted Error
<?php
namespace Jiminny\Models;
use Carbon\Carbon;
use Database\Factories\ActivityFactory;
use DateTimeInterface;
use Exception;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Database\Eloquent;
use Illuminate\Database\Eloquent\Attributes\Scope;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Auth;
use InvalidArgumentException;
use Jiminny\Component\ElasticSearch;
use Jiminny\Component\MeetingBot;
use Jiminny\Component\Model\BitwiseFlagTrait;
use Jiminny\Component\PlaybackPage\Comments\Services\ActivityCommentService;
use Jiminny\Component\Sidekick\SidekickService;
use Jiminny\Component\Uuid\UuidAwareInterface;
use Jiminny\Component\Workflow;
use Jiminny\Contracts;
use Jiminny\Contracts\Crm\ProspectInterface;
use Jiminny\DTO\ImportCall\Call;
use Jiminny\Events\Activities\ActivityTypeUpdated;
use Jiminny\Events\Activities\ActivityUpdated;
use Jiminny\Events\Activities\ProspectUpdated;
use Jiminny\Events\Activities\StageUpdated;
use Jiminny\Events\Activities\StatusUpdated;
use Jiminny\Events\Activities\TitleUpdated;
use Jiminny\Exceptions\InvalidArgumentException as InvalidArgumentJiminnyException;
use Jiminny\Exceptions\LogicException;
use Jiminny\Exceptions\RuntimeException;
use Jiminny\Models;
use Jiminny\Models\Activity\ActivitySummaryLog;
use Jiminny\Models\Activity\ActivityUploadSetting;
use Jiminny\Models\Activity\AvailabilityNotification;
use Jiminny\Models\Activity\CoachRequest;
use Jiminny\Models\Activity\Comment;
use Jiminny\Models\Activity\Log;
use Jiminny\Models\Activity\Message;
use Jiminny\Models\Activity\Moment;
use Jiminny\Models\Activity\Note;
use Jiminny\Models\Activity\ParticipantSpeech;
use Jiminny\Models\Activity\Play;
use Jiminny\Models\Activity\Question;
use Jiminny\Models\Activity\Share;
use Jiminny\Models\Activity\Snapshot;
use Jiminny\Models\Activity\Stats;
use Jiminny\Models\Activity\SubscriptionSet;
use Jiminny\Models\Activity\TopicTrigger;
use Jiminny\Models\Activity\Transcription;
use Jiminny\Models\Calendar\CalendarEvent;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Models\Crm\FieldData;
use Jiminny\Models\ElasticSearch\ActivityElasticSearchTrait;
use Jiminny\Models\Feature\FeatureEnum;
use Jiminny\Models\Participant\Connection;
use Jiminny\Models\Playlist\Activity as PlaylistActivity;
use Jiminny\Services\Activity\ActivityProviderRegistry;
use Jiminny\Services\Activity\Import\DataResolvers\UpdateCrmDataByStrategy;
use Jiminny\Services\Activity\Import\DataResolvers\UpdateCrmDataResolverFactory;
use Jiminny\Services\Activity\Import\DataResolvers\UpdateCrmDataResolverInterface;
use Jiminny\Traits\Enums;
use Jiminny\Traits\RequiresUUID;
use Jiminny\Utils\CurrencyFormatter;
use NumberFormatter;
use function in_array;
/**
* Jiminny\Models\Activity
*
* @property null|int $auto_score filled from ES hydrator, not in DB!
* @property-read Account|null $account
* @property-read CalendarEvent|null $calendarEvent
* @property-read Contact|null $contact
* @property-read Lead|null $lead
* @property-read Opportunity|null $opportunity
* @property-read Stage|null $stage
* @property int $id
* @property mixed|null $uuid
* @property string|null $source
* @property string|null $external_id
* @property string $provider
* @property string|null $location
* @property string|null $telephony_provider_id
* @property int|null $from_participant_id
* @property int|null $to_participant_id
* @property int|null $device_id
* @property string|null $type
* @property int|null $playbook_category_id
* @property int $user_id
* @property int|null $lead_id
* @property int|null $account_id
* @property int|null $contact_id
* @property int|null $opportunity_id
* @property int|null $stage_id
* @property string|null $value
* @property int|null $crm_configuration_id
* @property string|null $crm_provider_id
* @property string|null $language
* @property int|null $transcription_id
* @property int $duration
* @property string $status
* @property int|null $on_air
* @property int|null $calendar_event_id
* @property string $recording_state
* @property bool|null $recording_preference
* @property int $recording_reason_code
* @property int $summary_reminder_sent
* @property \Illuminate\Support\Carbon|null $log_reminder_sent_at
* @property \Illuminate\Support\Carbon|null $organizer_notified_at
* @property bool|null $has_recording_prompt
* @property bool $is_internal
* @property int $is_locked
* @property int $is_recording
* @property bool|null $is_processed
* @property bool $is_private
* @property bool $is_instant_invite
* @property string|null $poster_path
* @property string|null $summary
* @property string|null $title
* @property string|null $description
* @property \Illuminate\Support\Carbon|null $scheduled_start_time
* @property \Illuminate\Support\Carbon|null $scheduled_end_time
* @property \Illuminate\Support\Carbon|null $actual_start_time
* @property \Illuminate\Support\Carbon|null $actual_end_time
* @property int|null $uploaded_by
* @property \Illuminate\Support\Carbon|null $deleted_at
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property string|null $average_score
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Participant> $activeParticipants
* @property-read int|null $active_participants_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Scorecard\ActivityScorecardRuleTrigger> $activityScorecardRuleTriggers
* @property-read int|null $activity_scorecard_rule_triggers_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Scorecard\ActivityScorecardRule> $activityScorecardRules
* @property-read int|null $activity_scorecard_rules_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, AvailabilityNotification> $availabilityNotifications
* @property-read int|null $availability_notifications_count
* @property-read \Jiminny\Models\PlaybookCategory|null $category
* @property-read \Illuminate\Database\Eloquent\Collection<int, CoachRequest> $coachRequests
* @property-read int|null $coach_requests_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\CoachingFeedback> $coachingFeedbacks
* @property-read int|null $coaching_feedbacks_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, Message> $coachingMessages
* @property-read int|null $coaching_messages_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, Comment> $comments
* @property-read int|null $comments_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, Connection> $connections
* @property-read int|null $connections_count
* @property-read Configuration|null $crm
* @property-read \Illuminate\Database\Eloquent\Collection<int, FieldData> $data
* @property-read int|null $data_count
* @property-read \Jiminny\Models\Device|null $device
* @property-read \Kalnoy\Nestedset\Collection<int, \Jiminny\Models\Playlist> $favoritePlaylists
* @property-read int|null $favorite_playlists_count
* @property-read \Jiminny\Models\Participant|null $from
* @property-read string|null $activity_title
* @property-read mixed $comment_count
* @property-read mixed $duration_for_humans
* @property-read string $duration_for_humans_short
* @property-read int $favorite_count
* @property-read mixed $favorites_count
* @property-read mixed $formatted_value
* @property-read string $id_string
* @property-read \Jiminny\Models\Participant|null $organizer
* @property-read mixed $play_count
* @property-read int|null $plays_count
* @property-read ?ProspectInterface $prospect
* @property-read string|null $prospect_name
* @property-read mixed $prospect_type
* @property-read mixed $share_count
* @property-read int|null $shares_count
* @property-read int|null $tracks_with_telephony_count
* @property-read int|null $visible_comments_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\CoachingFeedback> $latestCoachingFeedbacks
* @property-read int|null $latest_coaching_feedbacks_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, Log> $logs
* @property-read int|null $logs_count
* @property-read \Jiminny\Models\Track|null $masterTrack
* @property-read \Illuminate\Database\Eloquent\Collection<int, Message> $messages
* @property-read int|null $messages_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, Moment> $moments
* @property-read int|null $moments_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, Note> $notes
* @property-read int|null $notes_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Participant\Share> $participantShares
* @property-read int|null $participant_shares_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, ParticipantSpeech> $participantSpeeches
* @property-read int|null $participant_speeches_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Participant\ParticipantStats> $participantStats
* @property-read int|null $participant_stats_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Participant> $participants
* @property-read int|null $participants_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, PlaylistActivity> $playlistActivities
* @property-read int|null $playlist_activities_count
* @property-read \Kalnoy\Nestedset\Collection<int, \Jiminny\Models\Playlist> $playlists
* @property-read int|null $playlists_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, Play> $plays
* @property-read \Illuminate\Database\Eloquent\Collection<int, Question> $questions
* @property-read int|null $questions_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Session> $sessions
* @property-read int|null $sessions_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, Share> $shares
* @property-read \Illuminate\Database\Eloquent\Collection<int, Snapshot> $snapshots
* @property-read int|null $snapshots_count
* @property-read Stats|null $stats
* @property-read \Jiminny\Models\Participant|null $to
* @property-read \Illuminate\Database\Eloquent\Collection<int, TopicTrigger> $topicTriggers
* @property-read int|null $topic_triggers_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Track> $tracks
* @property-read int|null $tracks_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Track> $tracksWithTelephony
* @property-read Transcription|null $transcription
* @property-read \Jiminny\Models\User $user
* @property-read \Illuminate\Database\Eloquent\Collection<int, Comment> $visibleComments
*
* @method static \Illuminate\Database\Eloquent\Collection<int, static> all($columns = ['*'])
* @method static \Jiminny\Component\Eloquent\Builder|Activity chunkByIdDesc($count, callable $callback, $column = null, $alias = null)
* @method static \Database\Factories\ActivityFactory factory(...$parameters)
* @method static \Illuminate\Database\Eloquent\Collection<int, static> get($columns = ['*'])
* @method static \Jiminny\Component\Eloquent\Builder|Activity heldBetween(\Carbon\Carbon $start, \Carbon\Carbon $end)
* @method static \Jiminny\Component\Eloquent\Builder|Activity idOrUuId($idOrUuid, bool $first = true)
* @method static \Jiminny\Component\Eloquent\Builder|Activity newModelQuery()
* @method static \Jiminny\Component\Eloquent\Builder|Activity newQuery()
* @method static Builder|Activity onlyTrashed()
* @method static \Jiminny\Component\Eloquent\Builder|Activity query()
* @method static \Jiminny\Component\Eloquent\Builder|Activity scheduledBetween(\Carbon\Carbon $start, \Carbon\Carbon $end)
* @method static \Jiminny\Component\Eloquent\Builder|Activity inOpenDeals()
* @method static \Jiminny\Component\Eloquent\Builder|Activity notInOpenDeals()
* @method static \Jiminny\Component\Eloquent\Builder|Activity forTeam(int $teamId)
* @method static \Jiminny\Component\Eloquent\Builder|Activity search(callable $searchQuery, $key = null, $sortByResults = true)
* @method static \Jiminny\Component\Eloquent\Builder|Activity uuid(string $uuid, bool $first = true)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereAccountId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereActualEndTime($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereActualStartTime($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereAverageScore($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereCalendarEventId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereContactId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereCreatedAt($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereCrmConfigurationId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereCrmProviderId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereDeletedAt($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereDescription($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereDeviceId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereDuration($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereFromParticipantId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereHasRecordingPrompt($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereIsInstantInvite($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereIsInternal($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereIsLocked($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereIsPrivate($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereIsProcessed($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereIsRecording($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereLanguage($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereLeadId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereLocation($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereLogReminderSentAt($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereOnAir($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereOpportunityId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereOrganizerNotifiedAt($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity wherePlaybookCategoryId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity wherePosterPath($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereProvider($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereRecordingPreference($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereRecordingReasonCode($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereRecordingState($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereScheduledEndTime($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereScheduledStartTime($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereSource($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereExternalId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereStageId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereStatus($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereSummary($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereSummaryReminderSent($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereTelephonyProviderId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereTitle($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereToParticipantId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereTranscriptionId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereType($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereUpdatedAt($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereUploadedBy($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereUserId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereUuid($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereValue($value)
* @method static Builder|Activity withTrashed()
* @method static Builder|Activity withoutTrashed()
*
* @mixin \Eloquent
*/
class Activity extends Model implements
ElasticSearch\Contract\Searchable,
Workflow\Workflow\WorkflowAwareInterface,
Models\Contracts\ActivityContract,
Contracts\Model\ActivityInterface,
UuidAwareInterface
{
use HasFactory;
use Enums;
use SoftDeletes;
use RequiresUUID;
use BitwiseFlagTrait;
use ElasticSearch\Model\Searchable;
use ActivityElasticSearchTrait;
use Workflow\Workflow\WorkflowAware {
transitionTo as traitTransitionTo;
}
public const int FLAG_RECORDING_REASON_DEFAULT = 0;
// Recording Prompted but never started
public const int FLAG_RECORDING_REASON_COMPLIANCE_PROMPT = 1;
public const int FLAG_RECORDING_REASON_COMPLIANCE_RESUMED = 2;
public const int FLAG_RECORDING_REASON_NO_AUDIO = 3;
// Recording Disabled by Organization
public const int FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT = 4;
// Recording was restricted to one-side recordings only
public const int FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT_ONE_SIDE = 8;
// Recording was not started because it was internal and team setting disabled that.
public const int FLAG_RECORDING_REASON_TEAM_INTERNAL_DISABLED = 16;
// Recording was not started because it was internal and user setting disabled that.
public const int FLAG_RECORDING_REASON_USER_INTERNAL_DISABLED = 32;
// Recording was not started because user setting disabled automatic recording.
public const int FLAG_RECORDING_REASON_USER_AUTOMATIC_DISABLED = 64;
// Recording was not started because team setting disabled automatic recording.
public const int FLAG_RECORDING_REASON_TEAM_AUTOMATIC_DISABLED = 128;
// Recording was not started because user has overriden default.
public const int FLAG_RECORDING_REASON_PREFERENCE_OVERRIDE = 256;
// Recording was not started because they don't want internal, and this meeting was not scheduled/imported in time.
public const int FLAG_RECORDING_REASON_USER_INTERNAL_DISABLED_UNSCHEDULED = 512;
// Recording was not started because their team setting does excludes the meeting type.
public const int FLAG_RECORDING_REASON_UNSUPPORTED_TYPE = 1024;
// Recording was not started because the external provider disabled it (or recording is missing etc).
public const int FLAG_RECORDING_REASON_EXTERNALLY_DISABLED = 2048;
// Recording was stopped externally ("exit-meeting" Pusher event)
public const int FLAG_RECORDING_REASON_STOPPED_EXTERNALLY = 384;
// Recording couldn't be started due to Zoom hosting conflict error
public const int FLAG_RECORDING_REASON_HOSTING_CONFLICT = 448;
// meeting.failed event with reason code BOT_DENIED_FROM_LOBBY
public const int FLAG_RECORDING_REASON_MEETING_BOT_DENIED_FROM_LOBBY = 4096;
// meeting.failed event with reason code LOBBY_TIMEOUT
public const int FLAG_RECORDING_REASON_MEETING_BOT_LOBBY_TIMEOUT = 8192;
// meeting.failed event with reason code BOT_KICKED
public const int FLAG_RECORDING_REASON_MEETING_BOT_KICKED = 16384;
// meeting.failed event with reason code UNKNOWN
public const int FLAG_RECORDING_REASON_MEETING_BOT_UNKNOWN = 32768;
public const int FLAG_RECORDING_REASON_CONSENT_DENIED = 65536;
// Invalid meeting (e.g. URL is invalid, or the meeting is not found)
public const int FLAG_RECORDING_REASON_MEETING_BOT_INVALID = 131072;
// The host stopped the recording.
public const int FLAG_RECORDING_REASON_USER_STOPPED = 262144;
// Recording was not started because an alternative vendor disabled it (or overrode it).
public const int FLAG_RECORDING_REASON_VENDOR_OVERRIDE = 1048576;
// Login required meeting.failed code
public const int FLAG_RECORDING_REASON_LOGIN_REQUIRED = 524288;
// Password for meeting was not provided - meeting.failed code
public const int FLAG_RECORDING_REASON_MEETING_PASSWORD_NOT_PROVIDED = 2097152;
// meeting.failed - when the meeting is locked
public const int FLAG_RECORDING_REASON_MEETING_IS_LOCKED = 4194304;
// max recording duration reached
public const int FLAG_RECORDING_REASON_MAX_DURATION_REACHED = 8388608;
// recording size is too small
public const int FLAG_RECORDING_REASON_EMPTY_RECORDING = 16777216;
// meeting.failed - when bot is redirected to sign in page multiple times
public const int FLAG_RECORDING_REASON_MAX_RESTART_COUNT_IS_REACHED = 33554432;
// meeting.failed event with reason code CONNECTION_LOST
public const int FLAG_RECORDING_REASON_MEETING_BOT_CONNECTION_LOST = 67108864;
// recording is corrupted.
public const int FLAG_RECORDING_REASON_MEDIA_FILE_UNSUPPORTED_MIME_TYPE = 134217728;
// meeting ended in lobby
public const int FLAG_RECORDING_REASON_MEETING_ENDED_IN_LOBBY = 268435456;
// meeting not started
public const int FLAG_RECORDING_REASON_REASON_MEETING_NOT_STARTED = 536870912;
// unfinished zoom custom disclaimer
public const int FLAG_RECORDING_REASON_FEATURE_RULE_NOT_FOUND_ERROR = 1073741824;
// recording download failed - server error
public const int FLAG_RECORDING_REASON_SERVER_ERROR = 2147483648;
// recording download failed - client code 404
public const int FLAG_RECORDING_REASON_NOT_FOUND = 2147483649;
// recording download failed - client code 401, 403
public const int FLAG_RECORDING_REASON_ACCESS_DENIED = 2147483650;
// recording download failed - client code 429
public const int FLAG_RECORDING_REASON_TOO_MANY_REQUESTS = 2147483651;
// recording download failed - unknown client error
public const int FLAG_RECORDING_REASON_CLIENT_ERROR = 2147483652;
// recording download failed - unknown error
public const int FLAG_RECORDING_REASON_UNKNOWN_ERROR = 2147483653;
// It has been setup ahead of time through calendar
public const string STATUS_SCHEDULED = 'scheduled';
// It is awaiting audio.
public const string STATUS_PENDING = 'pending';
// Participant(s) dialed in, awaiting organizer.
public const string STATUS_RINGING = 'ringing';
// Call is in progress.
public const string STATUS_IN_PROGRESS = 'in-progress';
// It has ended.
public const string STATUS_COMPLETED = 'completed';
// Cancelled prior to starting.
public const string STATUS_CANCELLED = 'canceled';
public const string STATUS_DUPLICATED = 'duplicated'; // duplicated conference
public const string STATUS_STARTING_SOON = 'starting-soon';
public const string STATUS_BOT_CREATE_SENT = 'bot-create-sent';
public const string STATUS_BOT_INSTANCE_WORKER_ASSIGNED = 'worker-assigned';
public const string STATUS_BOT_INSTANCE_STARTED = 'bot-started';
// When bot instance is waiting in lobby
public const string STATUS_BOT_INSTANCE_WAITING_LOBBY = 'bot-waiting';
public const string STATUS_BUSY = 'busy';
public const string STATUS_NO_ANSWER = 'no-answer';
public const string STATUS_FAILED = 'failed'; // Used by SMS too
// SMS related
public const string STATUS_ACCEPTED = 'accepted';
public const string STATUS_QUEUED = 'queued';
public const string STATUS_SENDING = 'sending';
public const string STATUS_SENT = 'sent';
public const string STATUS_DELIVERED = 'delivered';
public const string STATUS_UNDELIVERED = 'undelivered';
public const string STATUS_RECEIVING = 'receiving';
public const string STATUS_RECEIVED = 'received';
public const string STATUS_RESENT = 'resent';
public const array SMS_STATUSES = [
Activity::STATUS_RECEIVED,
Activity::STATUS_SENT,
Activity::STATUS_DELIVERED,
];
public const array SOFT_PHONE_CONFERENCE_STATUSES = [
Activity::STATUS_IN_PROGRESS,
Activity::STATUS_COMPLETED,
];
// @todo refactor prefix from `TYPE_` to `CHANNEL_`
public const string TYPE_SOFTPHONE = 'softphone';
public const string TYPE_SOFTPHONE_INBOUND = 'softphone-inbound';
public const string TYPE_CONFERENCE = 'conference';
public const string TYPE_SMS_INBOUND = 'sms-inbound';
public const string TYPE_SMS_OUTBOUND = 'sms-outbound';
public const string TYPE_EMAIL_INBOUND = 'email-inbound';
public const string TYPE_EMAIL_OUTBOUND = 'email-outbound';
public const array CHANNELS = [
self::TYPE_SOFTPHONE,
self::TYPE_SOFTPHONE_INBOUND,
self::TYPE_CONFERENCE,
self::TYPE_SMS_INBOUND,
self::TYPE_SMS_OUTBOUND,
self::TYPE_EMAIL_INBOUND,
self::TYPE_EMAIL_OUTBOUND,
];
public const array PLAYABLE_CHANNELS = [
self::TYPE_SOFTPHONE,
self::TYPE_SOFTPHONE_INBOUND,
self::TYPE_CONFERENCE,
];
// Recording States
public const string RECORDING_OFF = 'off'; // Default state
public const string RECORDING_IN_PROGRESS = 'in-progress';
public const string RECORDING_PAUSED = 'paused';
public const string RECORDING_STOPPED = 'stopped'; // To never be resumed.
public const string RECORDING_RECORDED = 'recorded'; // At least some portion of it was recorded.
public const string RECORDING_FAILED = 'failed'; // Recording was attempted but failed for some reason.
// Live Stream States
public const int ON_AIR_DEFAULT = 0;
public const int ON_AIR_READY = 1;
public const int ON_AIR_PREPARING = 2;
public const int ON_AIR_STREAMING = 3;
public const int ON_AIR_FINISHED = 4;
public const int ON_AIR_NOT_STREAMED = 5;
public const int ON_AIR_ERROR = -1;
public const string SOURCE_GONG = 'gong';
public const string SOURCE_CHORUS = 'chorus';
public const string SOURCE_OUTLOOK = 'outlook';
public const string SOURCE_GOOGLE = 'google';
// Activity Providers
public const string PROVIDER_TWILIO = 'twilio'; // XXX: This is run via the Jiminny Provider.
public const string PROVIDER_OUTREACH = 'outreach';
public const string PROVIDER_ZOOM_BOT = 'zoom-bot';
public const string PROVIDER_SALESLOFT = 'salesloft';
public const string PROVIDER_GOOGLE = 'google';
public const string PROVIDER_AIRCALL = 'aircall';
public const string PROVIDER_JUSTCALL = 'justcall';
public const string PROVIDER_GOOGLE_MEET = 'google-meet';
public const string PROVIDER_GONG = 'gong';
public const string PROVIDER_HUBSPOT = 'hubspot';
public const string PROVIDER_CLOSE = 'close';
public const string PROVIDER_TEAMS = 'ms-teams';
public const string PROVIDER_SALESFORCE = 'salesforce';
public const string PROVIDER_GROOVE = 'groove';
public const string PROVIDER_XANT = 'xant';
public const string PROVIDER_OFFICE = 'office';
public const string PROVIDER_NATTERBOX = 'natterbox';
public const string PROVIDER_RINGCENTRAL = 'ringcentral';
public const string PROVIDER_RINGCENTRAL_VIDEO = 'ringcentral-video';
public const string PROVIDER_GOTOMEETING = 'go-to-meeting';
public const string PROVIDER_DEMODESK = 'demo-desk';
public const string PROVIDER_DIALPAD = 'dialpad';
public const string PROVIDER_ZOOM_PHONE = 'zoom-phone';
public const string PROVIDER_CLOUDCALL = 'cloudcall';
public const string PROVIDER_CLOUDCALL_US = 'cloudcall-us';
public const string PROVIDER_EIGHT_BY_EIGHT = 'eight-by-eight'; // "8x8" UK
public const string PROVIDER_EIGHT_BY_EIGHT_CA = 'eight-by-eight-ca'; // "8x8" Canada
public const string PROVIDER_EIGHT_BY_EIGHT_AP = 'eight-by-eight-ap'; // "8x8" Australia
public const string PROVIDER_EIGHT_BY_EIGHT_US_EAST = 'eight-by-eight-use'; // "8x8" US East
public const string PROVIDER_EIGHT_BY_EIGHT_US_WEST = 'eight-by-eight-usw'; // "8x8" US West
public const string PROVIDER_CONNECT_AND_SELL = 'connect-and-sell';
public const string PROVIDER_CLOUD_TALK = 'cloud-talk';
public const string PROVIDER_AMAZON_CONNECT = 'amazon-connect';
public const string PROVIDER_VONAGE = 'vonage';
public const string PROVIDER_MIGRATOR = 'migrator';
public const string PROVIDER_UPLOADER = 'uploader';
public const string PROVIDER_TALKDESK = 'talkdesk';
public const string PROVIDER_TWILIO_FLEX = 'twilio-flex';
public const string PROVIDER_TWILIO_FLEX_DIRECT = 'twilio-flex-direct';
public const string PROVIDER_TWILIO_VIDEO = 'twilio-video';
public const string PROVIDER_AVAYA = 'avaya';
public const string PROVIDER_TELUS = 'telus';
public const string PROVIDER_FIVE_NINE = 'five-nine';
public const string PROVIDER_APOLLO = 'apollo';
public const string PROVIDER_ORUM = 'orum';
public const string PROVIDER_BLOOBIRDS = 'bloobirds';
/**
* @const API_PROVIDERS
* A list of integrations that import calls via API instead of webhooks
*/
public const array API_PROVIDERS = [
self::PROVIDER_OUTREACH,
self::PROVIDER_SALESLOFT,
self::PROVIDER_HUBSPOT,
self::PROVIDER_GROOVE,
self::PROVIDER_XANT,
self::PROVIDER_NATTERBOX,
self::PROVIDER_CLOUDCALL,
self::PROVIDER_CLOUDCALL_US,
self::PROVIDER_EIGHT_BY_EIGHT,
self::PROVIDER_EIGHT_BY_EIGHT_CA,
self::PROVIDER_EIGHT_BY_EIGHT_AP,
self::PROVIDER_EIGHT_BY_EIGHT_US_EAST,
self::PROVIDER_EIGHT_BY_EIGHT_US_WEST,
self::PROVIDER_CONNECT_AND_SELL,
self::PROVIDER_CLOUD_TALK,
self::PROVIDER_AMAZON_CONNECT,
self::PROVIDER_VONAGE,
self::PROVIDER_TALKDESK,
self::PROVIDER_TWILIO_VIDEO,
self::PROVIDER_TWILIO_FLEX,
self::PROVIDER_TWILIO_FLEX_DIRECT,
self::PROVIDER_FIVE_NINE,
self::PROVIDER_APOLLO,
self::PROVIDER_ORUM,
self::PROVIDER_BLOOBIRDS,
self::PROVIDER_RINGCENTRAL,
self::PROVIDER_AVAYA,
self::PROVIDER_TELUS,
];
public const array FINITE_STATES = [
self::TYPE_SOFTPHONE => [
self::STATUS_COMPLETED,
self::STATUS_FAILED,
self::STATUS_NO_ANSWER,
self::STATUS_BUSY,
],
self::TYPE_SOFTPHONE_INBOUND => [
self::STATUS_COMPLETED,
self::STATUS_FAILED,
self::STATUS_NO_ANSWER,
self::STATUS_BUSY,
],
self::TYPE_CONFERENCE => self::FINITE_STATES_CONFERENCE,
];
public const array FINITE_STATES_CONFERENCE = [
self::STATUS_COMPLETED,
self::STATUS_FAILED,
self::STATUS_CANCELLED,
];
public const array MEETING_BOT_JOIN_ATTEMPTED = [
self::STATUS_BOT_INSTANCE_WAITING_LOBBY,
self::STATUS_BOT_INSTANCE_STARTED,
];
public static array $enumStatuses = [
self::STATUS_SCHEDULED,
self::STATUS_PENDING,
self::STATUS_RINGING,
self::STATUS_IN_PROGRESS,
self::STATUS_COMPLETED,
self::STATUS_CANCELLED,
self::STATUS_BUSY,
self::STATUS_NO_ANSWER,
self::STATUS_FAILED,
self::STATUS_ACCEPTED,
self::STATUS_QUEUED,
self::STATUS_SENDING,
self::STATUS_SENT,
self::STATUS_RESENT,
self::STATUS_DELIVERED,
self::STATUS_UNDELIVERED,
self::STATUS_RECEIVING,
self::STATUS_RECEIVED,
self::STATUS_BOT_INSTANCE_WAITING_LOBBY,
self::STATUS_STARTING_SOON,
self::STATUS_BOT_INSTANCE_WORKER_ASSIGNED,
self::STATUS_BOT_INSTANCE_STARTED,
self::STATUS_DUPLICATED,
];
public static array $enumProviders = [
self::PROVIDER_TWILIO,
self::PROVIDER_OUTREACH,
self::PROVIDER_ZOOM_BOT,
self::PROVIDER_SALESLOFT,
self::PROVIDER_AIRCALL,
self::PROVIDER_JUSTCALL,
self::PROVIDER_GOOGLE_MEET,
self::PROVIDER_GONG,
self::PROVIDER_HUBSPOT,
self::PROVIDER_CLOSE,
self::PROVIDER_TEAMS,
self::PROVIDER_SALESFORCE,
self::PROVIDER_GROOVE,
self::PROVIDER_XANT,
self::PROVIDER_GOOGLE,
self::PROVIDER_OFFICE,
self::PROVIDER_NATTERBOX,
self::PROVIDER_RINGCENTRAL,
self::PROVIDER_RINGCENTRAL_VIDEO,
self::PROVIDER_GOTOMEETING,
self::PROVIDER_DEMODESK,
self::PROVIDER_DIALPAD,
self::PROVIDER_ZOOM_PHONE,
self::PROVIDER_CLOUDCALL,
self::PROVIDER_CLOUDCALL_US,
self::PROVIDER_EIGHT_BY_EIGHT,
self::PROVIDER_EIGHT_BY_EIGHT_CA,
self::PROVIDER_EIGHT_BY_EIGHT_AP,
self::PROVIDER_EIGHT_BY_EIGHT_US_EAST,
self::PROVIDER_EIGHT_BY_EIGHT_US_WEST,
self::PROVIDER_CONNECT_AND_SELL,
self::PROVIDER_CLOUD_TALK,
self::PROVIDER_AMAZON_CONNECT,
self::PROVIDER_VONAGE,
self::PROVIDER_TALKDESK,
self::PROVIDER_TWILIO_FLEX,
self::PROVIDER_TWILIO_FLEX_DIRECT,
self::PROVIDER_TWILIO_VIDEO,
self::PROVIDER_AVAYA,
self::PROVIDER_TELUS,
self::PROVIDER_FIVE_NINE,
self::PROVIDER_APOLLO,
self::PROVIDER_ORUM,
self::PROVIDER_BLOOBIRDS,
];
public static $enumRecordingStates = [
self::RECORDING_OFF, // Default state
self::RECORDING_IN_PROGRESS,
self::RECORDING_PAUSED,
self::RECORDING_STOPPED,
self::RECORDING_RECORDED,
self::RECORDING_FAILED,
];
// @Important:
// This collection is not used anywhere, and is fully duplicated by the Channels const.
// Validate if it is referred somehow via the enum trait, and if not, remove it entirely.
// An even better strategy will be to move all those constants to a dedicated class
protected array $enumTypes = [
self::TYPE_SOFTPHONE,
self::TYPE_SOFTPHONE_INBOUND,
self::TYPE_CONFERENCE,
self::TYPE_SMS_INBOUND,
self::TYPE_SMS_OUTBOUND,
self::TYPE_EMAIL_INBOUND,
self::TYPE_EMAIL_OUTBOUND,
];
protected static $enumFailedStatuses = [
self::STATUS_NO_ANSWER,
self::STATUS_FAILED,
self::STATUS_BUSY,
self::STATUS_CANCELLED,
];
protected $table = 'activities';
protected $fillable = [
// Type of activity.
'type', // @todo refactor to `channel`
// The activity type.
'playbook_category_id',
// User who hosts the activity.
'user_id',
// Related Lead record (if applicable)
'lead_id',
// Related Account record (if applicable)
'account_id',
// Related Contact record (if applicable)
'contact_id',
// Related Opportunity record (if applicable)
'opportunity_id',
// Stage of activity.
'stage_id',
// Value of opportunity.
'value',
// If the activity relates to a CRM task.
'crm_provider_id',
// If the activity was created through an external device.
'device_id',
// the activity's language code
'language',
// transcription id
'transcription_id',
// Duration of the call, with microseconds precision.
'duration',
// One of enumStatuses above.
'status',
// Have we reminded them to log the call?
'log_reminder_sent_at',
// If activity is private or inter-org, flagged here.
'is_internal',
// Managers and above can mark a call as private, to exclude it from other team members
'is_private',
'is_processed',
// Boolean for this activity being instant invite handled.
'is_instant_invite',
// If activity is in recording state, flagged here.
'recording_state',
// If activity recording is overidden from default.
'recording_preference',
// if recording did (not) happen, why that is
'recording_reason_code',
// Average score, updated during
'average_score',
// Summary that the organizer has taken after the call.
'summary',
// Subject of the activity, usually taken from calendar event.
'title',
// Description of the activity, usually taken from calendar event.
'description',
// Start time, usually taken from calendar event.
'scheduled_start_time',
// End time, usually taken from calendar event.
'scheduled_end_time',
// When the call actually started.
'actual_start_time',
// When the call actually ended.
'actual_end_time',
// SMS: Message reference
'telephony_provider_id',
// SMS: Participant who sent message
'from_participant_id',
// SMS: Participant who should receive the message
'to_participant_id',
// When an external guest joins an organizers meeting room and the organizer is not present,
// send them an SMS notification that someone has joined.
'organizer_notified_at',
// where was the activity imported from
'source',
// The id in the source system (e.g. the bot id in Recall.ai)
'external_id',
// The provider, by default it is twilio.
'provider',
// Meeting location url
'location',
// The snapshot for displaying a poster image.
'poster_path',
'crm_configuration_id',
// If there is an automated message that the conversation is being recorded
'has_recording_prompt',
// If the activity is being live-streamed
'on_air',
'calendar_event_id',
];
protected $appends = [
'id_string',
'organizer',
];
protected $hidden = [
'uuid',
];
protected $visible = [
'id_string',
'type',
'duration',
'average_score',
'status',
'log_reminder_sent_at',
'title',
'description',
'is_internal',
'scheduled_start_time',
'scheduled_end_time',
'actual_start_time',
'actual_end_time',
'user',
'category',
'account',
'contact',
'opportunity',
'lead',
'stage',
'stats',
'participants',
'playlists',
'tracks',
'comments',
'plays',
'coachingFeedbacks',
'shares',
'favorites',
'language',
'transcription',
'is_private',
'is_instant_invite',
'on_air',
'calendar_event_id',
];
protected function casts(): array
{
return [
'scheduled_start_time' => 'datetime',
'scheduled_end_time' => 'datetime',
'actual_start_time' => 'datetime',
'actual_end_time' => 'datetime',
'organizer_notified_at' => 'datetime',
'log_reminder_sent_at' => 'datetime',
'is_internal' => 'boolean',
'duration' => 'integer',
'average_score' => 'decimal:2',
'is_private' => 'boolean',
'is_processed' => 'boolean',
'is_instant_invite' => 'boolean',
'value' => 'decimal:2',
'recording_preference' => 'boolean',
'recording_reason_code' => 'integer',
'has_recording_prompt' => 'boolean',
'on_air' => 'integer',
];
}
protected static function boot()
{
parent::boot();
static::updated(static function (Activity $activity) {
// If activity is about to start (pending, ringing, in-progress) or event is scheduled in less than 1 week
if (in_array($activity->status, [Activity::STATUS_PENDING, Activity::STATUS_RINGING, Activity::STATUS_IN_PROGRESS], true) ||
($activity->scheduled_start_time && (int) $activity->scheduled_start_time->diffInWeeks(new Carbon(), true) < 1)) {
if ($activity->isDirty('status')) {
event(new StatusUpdated($activity));
}
if ($activity->isDirty('stage_id')) {
event(new StageUpdated($activity));
}
if ($activity->isDirty(['lead_id', 'account_id', 'contact_id'])) {
event(new ProspectUpdated($activity));
}
if ($activity->isDirty('opportunity_id')) {
event(new ActivityUpdated($activity, 'activity.opportunity-updated', Auth::user()));
}
if ($activity->isDirty('title')) {
event(new TitleUpdated($activity));
}
}
if ($activity->isDirty('playbook_category_id')) {
event(new ActivityTypeUpdated($activity));
}
});
static::deleted(static function (Activity $activity) {
// Hard delete associated playlistActivities
$activity->playlistActivities()->delete();
});
}
public function getOrganizerAttribute(): ?Participant
{
$participant = $this->participants()->where('user_id', $this->user_id)->first();
if (! $participant instanceof Participant && $participant !== null) {
throw new RuntimeException(sprintf('$participant must be an instance of "%s" or null', Participant::class));
}
return $participant;
}
public function getFormattedValueAttribute()
{
$currencyCode = 'USD';
if ($this->opportunity) {
$currencyCode = $this->opportunity->getCurrencyCode();
}
$formatter = new CurrencyFormatter();
$formatter->setTextAttribute(NumberFormatter::CURRENCY_CODE, $currencyCode);
$formatter->setAttribute(NumberFormatter::MAX_FRACTION_DIGITS, 0);
return $formatter->format($this->value, $currencyCode);
}
public function getProspectNameAttribute(): ?string
{
$prospectName = null;
if ($this->lead_id) {
$prospectName = $this->lead->name;
} elseif ($this->contact_id) {
$prospectName = $this->contact->name;
} elseif ($this->account_id) {
$prospectName = $this->account->name;
}
return $prospectName;
}
public function getProspectName(): ?string
{
/** @var string|null */
return $this->getAttribute('prospect_name');
}
/**
* Get activity title depending on prospect or title
*/
public function getActivityTitleAttribute(): ?string
{
$activityTitle = null;
if ($this->prospect && $this->prospect->getName()) {
if ($this->account_id) {
$activityTitle = $this->account->name;
} elseif ($this->lead_id) {
$activityTitle = $this->lead->company;
} elseif ($this->contact_id) {
$activityTitle = $this->contact->account ? $this->contact->account->name : $this->contact->name;
}
} elseif ($this->title) {
$activityTitle = $this->title;
}
return $activityTitle;
}
public function wasRecentlyCreated(): bool
{
return $this->wasRecentlyCreated;
}
public function getProspectTypeAttribute()
{
$prospectType = null;
if ($this->lead_id) {
$prospectType = 'Lead';
} elseif ($this->contact_id) {
$prospectType = 'Contact';
} elseif ($this->account_id) {
$prospectType = 'Account';
}
return $prospectType;
}
/**
* Return the best match for prospect. Results are in the following order of priority:
* 1. Lead
* 2. Contact
* 3. Account
* 4. NULL
*/
public function getProspectAttribute(): ?ProspectInterface
{
if ($this->hasLead()) {
return $this->getLead();
}
if ($this->hasContact()) {
return $this->getContact();
}
if ($this->hasAccount()) {
return $this->getAccount();
}
return null;
}
public function getTitleAttribute($value): ?string
{
return \getActivityTitleAttribute(
$this->user->name,
$this->getType(),
$value,
$this->prospect->name ?? null,
$this->from->national_phone_number ?? null
);
}
public function getTitle(): ?string
{
return $this->getAttribute('title');
}
public function getSummary(): ?string
{
return $this->getAttribute('summary');
}
public function isInternal(): bool
{
return $this->getAttribute('is_internal');
}
public function getIsPrivate(): bool
{
return $this->getAttribute('is_private');
}
public function getDescription(): ?string
{
return $this->getAttribute('description');
}
public function hasTitle(): bool
{
return $this->getOriginal('title') !== null;
}
public function getPlayCountAttribute()
{
return $this->getPlaysCountAttribute();
}
public function getPlaysCountAttribute()
{
if (! isset($this->attributes['plays_count'])) {
$this->loadCount('plays');
}
return $this->attributes['plays_count'];
}
public function getCommentCountAttribute()
{
return $this->getCommentsCountAttribute();
}
public function getCommentsCountAttribute()
{
if (! isset($this->attributes['comments_count'])) {
$this->loadCount('comments');
}
return $this->attributes['comments_count'];
}
public function getVisibleCommentsCountAttribute()
{
if (! isset($this->attributes['visible_comments_count'])) {
$activityCommentsService = app(ActivityCommentService::class);
$user = Auth::user() instanceof User ? Auth::user() : null;
$this->attributes['visible_comments_count'] = $activityCommentsService
->getVisibleCommentsCount($this, $user);
}
return $this->attributes['visible_comments_count'];
}
public function getShareCountAttribute()
{
return $this->getSharesCountAttribute();
}
public function getSharesCountAttribute()
{
if (! isset($this->attributes['shares_count'])) {
$this->loadCount('shares');
}
return $this->attributes['shares_count'];
}
/**
* Get the count of favorites playlists this activity appears in
*/
public function getFavoriteCountAttribute(): int
{
return $this->getFavoritesCountAttribute();
}
public function getFavoritesCountAttribute()
{
if (! isset($this->attributes['favorites_count'])) {
$this->loadCount('favorites');
}
return $this->attributes['favorites_count'];
}
public function getActiveParticipantsCountAttribute()
{
if (! isset($this->attributes['active_participants_count'])) {
$this->loadCount('activeParticipants');
}
return $this->attributes['active_participants_count'];
}
public function getTracksWithTelephonyCountAttribute()
{
if (! isset($this->attributes['tracks_with_telephony_count'])) {
$this->loadCount('tracksWithTelephony');
}
return $this->attributes['tracks_with_telephony_count'];
}
/**
* @TEMP
* $this->loadCount('tracksWithTelephony') throws null pointer exception
*/
public function countTracksWithTelephony(): int
{
return $this->tracks()->whereNotNull('telephony_provider_id')->count();
}
public function getDuration(): float
{
return $this->getAttribute('duration');
}
public function getDurationForHumansAttribute()
{
return Carbon::now()->subSeconds($this->duration)->diffForHumans(now(), true);
}
public function getDurationForHumansShortAttribute(): string
{
return Carbon::now()->subSeconds($this->duration)->diffForHumans(now(), true, true);
}
public function hasRecordingPreference(): bool
{
re...
|
86375
|
NULL
|
NULL
|
NULL
|
|
86376
|
2963
|
1
|
2026-05-28T14:05:55.545789+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-28/1779 /Users/lukas/.screenpipe/data/data/2026-05-28/1779977155545_m2.jpg...
|
PhpStorm
|
faVsco.js – Activity.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
#12121 on JY-20963-fix-im Project: faVsco.js, menu
#12121 on JY-20963-fix-import-on-deleted-entity, menu
Start Listening for PHP Debug Connections
ServiceTest
Run 'ServiceTest'
Debug 'ServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
Analyzing…
Previous Highlighted Error
Next Highlighted Error
<?php
namespace Jiminny\Models;
use Carbon\Carbon;
use Database\Factories\ActivityFactory;
use DateTimeInterface;
use Exception;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Database\Eloquent;
use Illuminate\Database\Eloquent\Attributes\Scope;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Auth;
use InvalidArgumentException;
use Jiminny\Component\ElasticSearch;
use Jiminny\Component\MeetingBot;
use Jiminny\Component\Model\BitwiseFlagTrait;
use Jiminny\Component\PlaybackPage\Comments\Services\ActivityCommentService;
use Jiminny\Component\Sidekick\SidekickService;
use Jiminny\Component\Uuid\UuidAwareInterface;
use Jiminny\Component\Workflow;
use Jiminny\Contracts;
use Jiminny\Contracts\Crm\ProspectInterface;
use Jiminny\DTO\ImportCall\Call;
use Jiminny\Events\Activities\ActivityTypeUpdated;
use Jiminny\Events\Activities\ActivityUpdated;
use Jiminny\Events\Activities\ProspectUpdated;
use Jiminny\Events\Activities\StageUpdated;
use Jiminny\Events\Activities\StatusUpdated;
use Jiminny\Events\Activities\TitleUpdated;
use Jiminny\Exceptions\InvalidArgumentException as InvalidArgumentJiminnyException;
use Jiminny\Exceptions\LogicException;
use Jiminny\Exceptions\RuntimeException;
use Jiminny\Models;
use Jiminny\Models\Activity\ActivitySummaryLog;
use Jiminny\Models\Activity\ActivityUploadSetting;
use Jiminny\Models\Activity\AvailabilityNotification;
use Jiminny\Models\Activity\CoachRequest;
use Jiminny\Models\Activity\Comment;
use Jiminny\Models\Activity\Log;
use Jiminny\Models\Activity\Message;
use Jiminny\Models\Activity\Moment;
use Jiminny\Models\Activity\Note;
use Jiminny\Models\Activity\ParticipantSpeech;
use Jiminny\Models\Activity\Play;
use Jiminny\Models\Activity\Question;
use Jiminny\Models\Activity\Share;
use Jiminny\Models\Activity\Snapshot;
use Jiminny\Models\Activity\Stats;
use Jiminny\Models\Activity\SubscriptionSet;
use Jiminny\Models\Activity\TopicTrigger;
use Jiminny\Models\Activity\Transcription;
use Jiminny\Models\Calendar\CalendarEvent;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Models\Crm\FieldData;
use Jiminny\Models\ElasticSearch\ActivityElasticSearchTrait;
use Jiminny\Models\Feature\FeatureEnum;
use Jiminny\Models\Participant\Connection;
use Jiminny\Models\Playlist\Activity as PlaylistActivity;
use Jiminny\Services\Activity\ActivityProviderRegistry;
use Jiminny\Services\Activity\Import\DataResolvers\UpdateCrmDataByStrategy;
use Jiminny\Services\Activity\Import\DataResolvers\UpdateCrmDataResolverFactory;
use Jiminny\Services\Activity\Import\DataResolvers\UpdateCrmDataResolverInterface;
use Jiminny\Traits\Enums;
use Jiminny\Traits\RequiresUUID;
use Jiminny\Utils\CurrencyFormatter;
use NumberFormatter;
use function in_array;
/**
* Jiminny\Models\Activity
*
* @property null|int $auto_score filled from ES hydrator, not in DB!
* @property-read Account|null $account
* @property-read CalendarEvent|null $calendarEvent
* @property-read Contact|null $contact
* @property-read Lead|null $lead
* @property-read Opportunity|null $opportunity
* @property-read Stage|null $stage
* @property int $id
* @property mixed|null $uuid
* @property string|null $source
* @property string|null $external_id
* @property string $provider
* @property string|null $location
* @property string|null $telephony_provider_id
* @property int|null $from_participant_id
* @property int|null $to_participant_id
* @property int|null $device_id
* @property string|null $type
* @property int|null $playbook_category_id
* @property int $user_id
* @property int|null $lead_id
* @property int|null $account_id
* @property int|null $contact_id
* @property int|null $opportunity_id
* @property int|null $stage_id
* @property string|null $value
* @property int|null $crm_configuration_id
* @property string|null $crm_provider_id
* @property string|null $language
* @property int|null $transcription_id
* @property int $duration
* @property string $status
* @property int|null $on_air
* @property int|null $calendar_event_id
* @property string $recording_state
* @property bool|null $recording_preference
* @property int $recording_reason_code
* @property int $summary_reminder_sent
* @property \Illuminate\Support\Carbon|null $log_reminder_sent_at
* @property \Illuminate\Support\Carbon|null $organizer_notified_at
* @property bool|null $has_recording_prompt
* @property bool $is_internal
* @property int $is_locked
* @property int $is_recording
* @property bool|null $is_processed
* @property bool $is_private
* @property bool $is_instant_invite
* @property string|null $poster_path
* @property string|null $summary
* @property string|null $title
* @property string|null $description
* @property \Illuminate\Support\Carbon|null $scheduled_start_time
* @property \Illuminate\Support\Carbon|null $scheduled_end_time
* @property \Illuminate\Support\Carbon|null $actual_start_time
* @property \Illuminate\Support\Carbon|null $actual_end_time
* @property int|null $uploaded_by
* @property \Illuminate\Support\Carbon|null $deleted_at
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property string|null $average_score
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Participant> $activeParticipants
* @property-read int|null $active_participants_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Scorecard\ActivityScorecardRuleTrigger> $activityScorecardRuleTriggers
* @property-read int|null $activity_scorecard_rule_triggers_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Scorecard\ActivityScorecardRule> $activityScorecardRules
* @property-read int|null $activity_scorecard_rules_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, AvailabilityNotification> $availabilityNotifications
* @property-read int|null $availability_notifications_count
* @property-read \Jiminny\Models\PlaybookCategory|null $category
* @property-read \Illuminate\Database\Eloquent\Collection<int, CoachRequest> $coachRequests
* @property-read int|null $coach_requests_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\CoachingFeedback> $coachingFeedbacks
* @property-read int|null $coaching_feedbacks_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, Message> $coachingMessages
* @property-read int|null $coaching_messages_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, Comment> $comments
* @property-read int|null $comments_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, Connection> $connections
* @property-read int|null $connections_count
* @property-read Configuration|null $crm
* @property-read \Illuminate\Database\Eloquent\Collection<int, FieldData> $data
* @property-read int|null $data_count
* @property-read \Jiminny\Models\Device|null $device
* @property-read \Kalnoy\Nestedset\Collection<int, \Jiminny\Models\Playlist> $favoritePlaylists
* @property-read int|null $favorite_playlists_count
* @property-read \Jiminny\Models\Participant|null $from
* @property-read string|null $activity_title
* @property-read mixed $comment_count
* @property-read mixed $duration_for_humans
* @property-read string $duration_for_humans_short
* @property-read int $favorite_count
* @property-read mixed $favorites_count
* @property-read mixed $formatted_value
* @property-read string $id_string
* @property-read \Jiminny\Models\Participant|null $organizer
* @property-read mixed $play_count
* @property-read int|null $plays_count
* @property-read ?ProspectInterface $prospect
* @property-read string|null $prospect_name
* @property-read mixed $prospect_type
* @property-read mixed $share_count
* @property-read int|null $shares_count
* @property-read int|null $tracks_with_telephony_count
* @property-read int|null $visible_comments_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\CoachingFeedback> $latestCoachingFeedbacks
* @property-read int|null $latest_coaching_feedbacks_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, Log> $logs
* @property-read int|null $logs_count
* @property-read \Jiminny\Models\Track|null $masterTrack
* @property-read \Illuminate\Database\Eloquent\Collection<int, Message> $messages
* @property-read int|null $messages_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, Moment> $moments
* @property-read int|null $moments_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, Note> $notes
* @property-read int|null $notes_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Participant\Share> $participantShares
* @property-read int|null $participant_shares_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, ParticipantSpeech> $participantSpeeches
* @property-read int|null $participant_speeches_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Participant\ParticipantStats> $participantStats
* @property-read int|null $participant_stats_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Participant> $participants
* @property-read int|null $participants_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, PlaylistActivity> $playlistActivities
* @property-read int|null $playlist_activities_count
* @property-read \Kalnoy\Nestedset\Collection<int, \Jiminny\Models\Playlist> $playlists
* @property-read int|null $playlists_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, Play> $plays
* @property-read \Illuminate\Database\Eloquent\Collection<int, Question> $questions
* @property-read int|null $questions_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Session> $sessions
* @property-read int|null $sessions_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, Share> $shares
* @property-read \Illuminate\Database\Eloquent\Collection<int, Snapshot> $snapshots
* @property-read int|null $snapshots_count
* @property-read Stats|null $stats
* @property-read \Jiminny\Models\Participant|null $to
* @property-read \Illuminate\Database\Eloquent\Collection<int, TopicTrigger> $topicTriggers
* @property-read int|null $topic_triggers_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Track> $tracks
* @property-read int|null $tracks_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Track> $tracksWithTelephony
* @property-read Transcription|null $transcription
* @property-read \Jiminny\Models\User $user
* @property-read \Illuminate\Database\Eloquent\Collection<int, Comment> $visibleComments
*
* @method static \Illuminate\Database\Eloquent\Collection<int, static> all($columns = ['*'])
* @method static \Jiminny\Component\Eloquent\Builder|Activity chunkByIdDesc($count, callable $callback, $column = null, $alias = null)
* @method static \Database\Factories\ActivityFactory factory(...$parameters)
* @method static \Illuminate\Database\Eloquent\Collection<int, static> get($columns = ['*'])
* @method static \Jiminny\Component\Eloquent\Builder|Activity heldBetween(\Carbon\Carbon $start, \Carbon\Carbon $end)
* @method static \Jiminny\Component\Eloquent\Builder|Activity idOrUuId($idOrUuid, bool $first = true)
* @method static \Jiminny\Component\Eloquent\Builder|Activity newModelQuery()
* @method static \Jiminny\Component\Eloquent\Builder|Activity newQuery()
* @method static Builder|Activity onlyTrashed()
* @method static \Jiminny\Component\Eloquent\Builder|Activity query()
* @method static \Jiminny\Component\Eloquent\Builder|Activity scheduledBetween(\Carbon\Carbon $start, \Carbon\Carbon $end)
* @method static \Jiminny\Component\Eloquent\Builder|Activity inOpenDeals()
* @method static \Jiminny\Component\Eloquent\Builder|Activity notInOpenDeals()
* @method static \Jiminny\Component\Eloquent\Builder|Activity forTeam(int $teamId)
* @method static \Jiminny\Component\Eloquent\Builder|Activity search(callable $searchQuery, $key = null, $sortByResults = true)
* @method static \Jiminny\Component\Eloquent\Builder|Activity uuid(string $uuid, bool $first = true)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereAccountId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereActualEndTime($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereActualStartTime($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereAverageScore($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereCalendarEventId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereContactId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereCreatedAt($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereCrmConfigurationId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereCrmProviderId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereDeletedAt($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereDescription($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereDeviceId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereDuration($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereFromParticipantId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereHasRecordingPrompt($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereIsInstantInvite($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereIsInternal($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereIsLocked($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereIsPrivate($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereIsProcessed($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereIsRecording($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereLanguage($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereLeadId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereLocation($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereLogReminderSentAt($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereOnAir($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereOpportunityId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereOrganizerNotifiedAt($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity wherePlaybookCategoryId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity wherePosterPath($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereProvider($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereRecordingPreference($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereRecordingReasonCode($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereRecordingState($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereScheduledEndTime($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereScheduledStartTime($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereSource($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereExternalId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereStageId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereStatus($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereSummary($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereSummaryReminderSent($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereTelephonyProviderId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereTitle($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereToParticipantId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereTranscriptionId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereType($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereUpdatedAt($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereUploadedBy($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereUserId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereUuid($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereValue($value)
* @method static Builder|Activity withTrashed()
* @method static Builder|Activity withoutTrashed()
*
* @mixin \Eloquent
*/
class Activity extends Model implements
ElasticSearch\Contract\Searchable,
Workflow\Workflow\WorkflowAwareInterface,
Models\Contracts\ActivityContract,
Contracts\Model\ActivityInterface,
UuidAwareInterface
{
use HasFactory;
use Enums;
use SoftDeletes;
use RequiresUUID;
use BitwiseFlagTrait;
use ElasticSearch\Model\Searchable;
use ActivityElasticSearchTrait;
use Workflow\Workflow\WorkflowAware {
transitionTo as traitTransitionTo;
}
public const int FLAG_RECORDING_REASON_DEFAULT = 0;
// Recording Prompted but never started
public const int FLAG_RECORDING_REASON_COMPLIANCE_PROMPT = 1;
public const int FLAG_RECORDING_REASON_COMPLIANCE_RESUMED = 2;
public const int FLAG_RECORDING_REASON_NO_AUDIO = 3;
// Recording Disabled by Organization
public const int FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT = 4;
// Recording was restricted to one-side recordings only
public const int FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT_ONE_SIDE = 8;
// Recording was not started because it was internal and team setting disabled that.
public const int FLAG_RECORDING_REASON_TEAM_INTERNAL_DISABLED = 16;
// Recording was not started because it was internal and user setting disabled that.
public const int FLAG_RECORDING_REASON_USER_INTERNAL_DISABLED = 32;
// Recording was not started because user setting disabled automatic recording.
public const int FLAG_RECORDING_REASON_USER_AUTOMATIC_DISABLED = 64;
// Recording was not started because team setting disabled automatic recording.
public const int FLAG_RECORDING_REASON_TEAM_AUTOMATIC_DISABLED = 128;
// Recording was not started because user has overriden default.
public const int FLAG_RECORDING_REASON_PREFERENCE_OVERRIDE = 256;
// Recording was not started because they don't want internal, and this meeting was not scheduled/imported in time.
public const int FLAG_RECORDING_REASON_USER_INTERNAL_DISABLED_UNSCHEDULED = 512;
// Recording was not started because their team setting does excludes the meeting type.
public const int FLAG_RECORDING_REASON_UNSUPPORTED_TYPE = 1024;
// Recording was not started because the external provider disabled it (or recording is missing etc).
public const int FLAG_RECORDING_REASON_EXTERNALLY_DISABLED = 2048;
// Recording was stopped externally ("exit-meeting" Pusher event)
public const int FLAG_RECORDING_REASON_STOPPED_EXTERNALLY = 384;
// Recording couldn't be started due to Zoom hosting conflict error
public const int FLAG_RECORDING_REASON_HOSTING_CONFLICT = 448;
// meeting.failed event with reason code BOT_DENIED_FROM_LOBBY
public const int FLAG_RECORDING_REASON_MEETING_BOT_DENIED_FROM_LOBBY = 4096;
// meeting.failed event with reason code LOBBY_TIMEOUT
public const int FLAG_RECORDING_REASON_MEETING_BOT_LOBBY_TIMEOUT = 8192;
// meeting.failed event with reason code BOT_KICKED
public const int FLAG_RECORDING_REASON_MEETING_BOT_KICKED = 16384;
// meeting.failed event with reason code UNKNOWN
public const int FLAG_RECORDING_REASON_MEETING_BOT_UNKNOWN = 32768;
public const int FLAG_RECORDING_REASON_CONSENT_DENIED = 65536;
// Invalid meeting (e.g. URL is invalid, or the meeting is not found)
public const int FLAG_RECORDING_REASON_MEETING_BOT_INVALID = 131072;
// The host stopped the recording.
public const int FLAG_RECORDING_REASON_USER_STOPPED = 262144;
// Recording was not started because an alternative vendor disabled it (or overrode it).
public const int FLAG_RECORDING_REASON_VENDOR_OVERRIDE = 1048576;
// Login required meeting.failed code
public const int FLAG_RECORDING_REASON_LOGIN_REQUIRED = 524288;
// Password for meeting was not provided - meeting.failed code
public const int FLAG_RECORDING_REASON_MEETING_PASSWORD_NOT_PROVIDED = 2097152;
// meeting.failed - when the meeting is locked
public const int FLAG_RECORDING_REASON_MEETING_IS_LOCKED = 4194304;
// max recording duration reached
public const int FLAG_RECORDING_REASON_MAX_DURATION_REACHED = 8388608;
// recording size is too small
public const int FLAG_RECORDING_REASON_EMPTY_RECORDING = 16777216;
// meeting.failed - when bot is redirected to sign in page multiple times
public const int FLAG_RECORDING_REASON_MAX_RESTART_COUNT_IS_REACHED = 33554432;
// meeting.failed event with reason code CONNECTION_LOST
public const int FLAG_RECORDING_REASON_MEETING_BOT_CONNECTION_LOST = 67108864;
// recording is corrupted.
public const int FLAG_RECORDING_REASON_MEDIA_FILE_UNSUPPORTED_MIME_TYPE = 134217728;
// meeting ended in lobby
public const int FLAG_RECORDING_REASON_MEETING_ENDED_IN_LOBBY = 268435456;
// meeting not started
public const int FLAG_RECORDING_REASON_REASON_MEETING_NOT_STARTED = 536870912;
// unfinished zoom custom disclaimer
public const int FLAG_RECORDING_REASON_FEATURE_RULE_NOT_FOUND_ERROR = 1073741824;
// recording download failed - server error
public const int FLAG_RECORDING_REASON_SERVER_ERROR = 2147483648;
// recording download failed - client code 404
public const int FLAG_RECORDING_REASON_NOT_FOUND = 2147483649;
// recording download failed - client code 401, 403
public const int FLAG_RECORDING_REASON_ACCESS_DENIED = 2147483650;
// recording download failed - client code 429
public const int FLAG_RECORDING_REASON_TOO_MANY_REQUESTS = 2147483651;
// recording download failed - unknown client error
public const int FLAG_RECORDING_REASON_CLIENT_ERROR = 2147483652;
// recording download failed - unknown error
public const int FLAG_RECORDING_REASON_UNKNOWN_ERROR = 2147483653;
// It has been setup ahead of time through calendar
public const string STATUS_SCHEDULED = 'scheduled';
// It is awaiting audio.
public const string STATUS_PENDING = 'pending';
// Participant(s) dialed in, awaiting organizer.
public const string STATUS_RINGING = 'ringing';
// Call is in progress.
public const string STATUS_IN_PROGRESS = 'in-progress';
// It has ended.
public const string STATUS_COMPLETED = 'completed';
// Cancelled prior to starting.
public const string STATUS_CANCELLED = 'canceled';
public const string STATUS_DUPLICATED = 'duplicated'; // duplicated conference
public const string STATUS_STARTING_SOON = 'starting-soon';
public const string STATUS_BOT_CREATE_SENT = 'bot-create-sent';
public const string STATUS_BOT_INSTANCE_WORKER_ASSIGNED = 'worker-assigned';
public const string STATUS_BOT_INSTANCE_STARTED = 'bot-started';
// When bot instance is waiting in lobby
public const string STATUS_BOT_INSTANCE_WAITING_LOBBY = 'bot-waiting';
public const string STATUS_BUSY = 'busy';
public const string STATUS_NO_ANSWER = 'no-answer';
public const string STATUS_FAILED = 'failed'; // Used by SMS too
// SMS related
public const string STATUS_ACCEPTED = 'accepted';
public const string STATUS_QUEUED = 'queued';
public const string STATUS_SENDING = 'sending';
public const string STATUS_SENT = 'sent';
public const string STATUS_DELIVERED = 'delivered';
public const string STATUS_UNDELIVERED = 'undelivered';
public const string STATUS_RECEIVING = 'receiving';
public const string STATUS_RECEIVED = 'received';
public const string STATUS_RESENT = 'resent';
public const array SMS_STATUSES = [
Activity::STATUS_RECEIVED,
Activity::STATUS_SENT,
Activity::STATUS_DELIVERED,
];
public const array SOFT_PHONE_CONFERENCE_STATUSES = [
Activity::STATUS_IN_PROGRESS,
Activity::STATUS_COMPLETED,
];
// @todo refactor prefix from `TYPE_` to `CHANNEL_`
public const string TYPE_SOFTPHONE = 'softphone';
public const string TYPE_SOFTPHONE_INBOUND = 'softphone-inbound';
public const string TYPE_CONFERENCE = 'conference';
public const string TYPE_SMS_INBOUND = 'sms-inbound';
public const string TYPE_SMS_OUTBOUND = 'sms-outbound';
public const string TYPE_EMAIL_INBOUND = 'email-inbound';
public const string TYPE_EMAIL_OUTBOUND = 'email-outbound';
public const array CHANNELS = [
self::TYPE_SOFTPHONE,
self::TYPE_SOFTPHONE_INBOUND,
self::TYPE_CONFERENCE,
self::TYPE_SMS_INBOUND,
self::TYPE_SMS_OUTBOUND,
self::TYPE_EMAIL_INBOUND,
self::TYPE_EMAIL_OUTBOUND,
];
public const array PLAYABLE_CHANNELS = [
self::TYPE_SOFTPHONE,
self::TYPE_SOFTPHONE_INBOUND,
self::TYPE_CONFERENCE,
];
// Recording States
public const string RECORDING_OFF = 'off'; // Default state
public const string RECORDING_IN_PROGRESS = 'in-progress';
public const string RECORDING_PAUSED = 'paused';
public const string RECORDING_STOPPED = 'stopped'; // To never be resumed.
public const string RECORDING_RECORDED = 'recorded'; // At least some portion of it was recorded.
public const string RECORDING_FAILED = 'failed'; // Recording was attempted but failed for some reason.
// Live Stream States
public const int ON_AIR_DEFAULT = 0;
public const int ON_AIR_READY = 1;
public const int ON_AIR_PREPARING = 2;
public const int ON_AIR_STREAMING = 3;
public const int ON_AIR_FINISHED = 4;
public const int ON_AIR_NOT_STREAMED = 5;
public const int ON_AIR_ERROR = -1;
public const string SOURCE_GONG = 'gong';
public const string SOURCE_CHORUS = 'chorus';
public const string SOURCE_OUTLOOK = 'outlook';
public const string SOURCE_GOOGLE = 'google';
// Activity Providers
public const string PROVIDER_TWILIO = 'twilio'; // XXX: This is run via the Jiminny Provider.
public const string PROVIDER_OUTREACH = 'outreach';
public const string PROVIDER_ZOOM_BOT = 'zoom-bot';
public const string PROVIDER_SALESLOFT = 'salesloft';
public const string PROVIDER_GOOGLE = 'google';
public const string PROVIDER_AIRCALL = 'aircall';
public const string PROVIDER_JUSTCALL = 'justcall';
public const string PROVIDER_GOOGLE_MEET = 'google-meet';
public const string PROVIDER_GONG = 'gong';
public const string PROVIDER_HUBSPOT = 'hubspot';
public const string PROVIDER_CLOSE = 'close';
public const string PROVIDER_TEAMS = 'ms-teams';
public const string PROVIDER_SALESFORCE = 'salesforce';
public const string PROVIDER_GROOVE = 'groove';
public const string PROVIDER_XANT = 'xant';
public const string PROVIDER_OFFICE = 'office';
public const string PROVIDER_NATTERBOX = 'natterbox';
public const string PROVIDER_RINGCENTRAL = 'ringcentral';
public const string PROVIDER_RINGCENTRAL_VIDEO = 'ringcentral-video';
public const string PROVIDER_GOTOMEETING = 'go-to-meeting';
public const string PROVIDER_DEMODESK = 'demo-desk';
public const string PROVIDER_DIALPAD = 'dialpad';
public const string PROVIDER_ZOOM_PHONE = 'zoom-phone';
public const string PROVIDER_CLOUDCALL = 'cloudcall';
public const string PROVIDER_CLOUDCALL_US = 'cloudcall-us';
public const string PROVIDER_EIGHT_BY_EIGHT = 'eight-by-eight'; // "8x8" UK
public const string PROVIDER_EIGHT_BY_EIGHT_CA = 'eight-by-eight-ca'; // "8x8" Canada
public const string PROVIDER_EIGHT_BY_EIGHT_AP = 'eight-by-eight-ap'; // "8x8" Australia
public const string PROVIDER_EIGHT_BY_EIGHT_US_EAST = 'eight-by-eight-use'; // "8x8" US East
public const string PROVIDER_EIGHT_BY_EIGHT_US_WEST = 'eight-by-eight-usw'; // "8x8" US West
public const string PROVIDER_CONNECT_AND_SELL = 'connect-and-sell';
public const string PROVIDER_CLOUD_TALK = 'cloud-talk';
public const string PROVIDER_AMAZON_CONNECT = 'amazon-connect';
public const string PROVIDER_VONAGE = 'vonage';
public const string PROVIDER_MIGRATOR = 'migrator';
public const string PROVIDER_UPLOADER = 'uploader';
public const string PROVIDER_TALKDESK = 'talkdesk';
public const string PROVIDER_TWILIO_FLEX = 'twilio-flex';
public const string PROVIDER_TWILIO_FLEX_DIRECT = 'twilio-flex-direct';
public const string PROVIDER_TWILIO_VIDEO = 'twilio-video';
public const string PROVIDER_AVAYA = 'avaya';
public const string PROVIDER_TELUS = 'telus';
public const string PROVIDER_FIVE_NINE = 'five-nine';
public const string PROVIDER_APOLLO = 'apollo';
public const string PROVIDER_ORUM = 'orum';
public const string PROVIDER_BLOOBIRDS = 'bloobirds';
/**
* @const API_PROVIDERS
* A list of integrations that import calls via API instead of webhooks
*/
public const array API_PROVIDERS = [
self::PROVIDER_OUTREACH,
self::PROVIDER_SALESLOFT,
self::PROVIDER_HUBSPOT,
self::PROVIDER_GROOVE,
self::PROVIDER_XANT,
self::PROVIDER_NATTERBOX,
self::PROVIDER_CLOUDCALL,
self::PROVIDER_CLOUDCALL_US,
self::PROVIDER_EIGHT_BY_EIGHT,
self::PROVIDER_EIGHT_BY_EIGHT_CA,
self::PROVIDER_EIGHT_BY_EIGHT_AP,
self::PROVIDER_EIGHT_BY_EIGHT_US_EAST,
self::PROVIDER_EIGHT_BY_EIGHT_US_WEST,
self::PROVIDER_CONNECT_AND_SELL,
self::PROVIDER_CLOUD_TALK,
self::PROVIDER_AMAZON_CONNECT,
self::PROVIDER_VONAGE,
self::PROVIDER_TALKDESK,
self::PROVIDER_TWILIO_VIDEO,
self::PROVIDER_TWILIO_FLEX,
self::PROVIDER_TWILIO_FLEX_DIRECT,
self::PROVIDER_FIVE_NINE,
self::PROVIDER_APOLLO,
self::PROVIDER_ORUM,
self::PROVIDER_BLOOBIRDS,
self::PROVIDER_RINGCENTRAL,
self::PROVIDER_AVAYA,
self::PROVIDER_TELUS,
];
public const array FINITE_STATES = [
self::TYPE_SOFTPHONE => [
self::STATUS_COMPLETED,
self::STATUS_FAILED,
self::STATUS_NO_ANSWER,
self::STATUS_BUSY,
],
self::TYPE_SOFTPHONE_INBOUND => [
self::STATUS_COMPLETED,
self::STATUS_FAILED,
self::STATUS_NO_ANSWER,
self::STATUS_BUSY,
],
self::TYPE_CONFERENCE => self::FINITE_STATES_CONFERENCE,
];
public const array FINITE_STATES_CONFERENCE = [
self::STATUS_COMPLETED,
self::STATUS_FAILED,
self::STATUS_CANCELLED,
];
public const array MEETING_BOT_JOIN_ATTEMPTED = [
self::STATUS_BOT_INSTANCE_WAITING_LOBBY,
self::STATUS_BOT_INSTANCE_STARTED,
];
public static array $enumStatuses = [
self::STATUS_SCHEDULED,
self::STATUS_PENDING,
self::STATUS_RINGING,
self::STATUS_IN_PROGRESS,
self::STATUS_COMPLETED,
self::STATUS_CANCELLED,
self::STATUS_BUSY,
self::STATUS_NO_ANSWER,
self::STATUS_FAILED,
self::STATUS_ACCEPTED,
self::STATUS_QUEUED,
self::STATUS_SENDING,
self::STATUS_SENT,
self::STATUS_RESENT,
self::STATUS_DELIVERED,
self::STATUS_UNDELIVERED,
self::STATUS_RECEIVING,
self::STATUS_RECEIVED,
self::STATUS_BOT_INSTANCE_WAITING_LOBBY,
self::STATUS_STARTING_SOON,
self::STATUS_BOT_INSTANCE_WORKER_ASSIGNED,
self::STATUS_BOT_INSTANCE_STARTED,
self::STATUS_DUPLICATED,
];
public static array $enumProviders = [
self::PROVIDER_TWILIO,
self::PROVIDER_OUTREACH,
self::PROVIDER_ZOOM_BOT,
self::PROVIDER_SALESLOFT,
self::PROVIDER_AIRCALL,
self::PROVIDER_JUSTCALL,
self::PROVIDER_GOOGLE_MEET,
self::PROVIDER_GONG,
self::PROVIDER_HUBSPOT,
self::PROVIDER_CLOSE,
self::PROVIDER_TEAMS,
self::PROVIDER_SALESFORCE,
self::PROVIDER_GROOVE,
self::PROVIDER_XANT,
self::PROVIDER_GOOGLE,
self::PROVIDER_OFFICE,
self::PROVIDER_NATTERBOX,
self::PROVIDER_RINGCENTRAL,
self::PROVIDER_RINGCENTRAL_VIDEO,
self::PROVIDER_GOTOMEETING,
self::PROVIDER_DEMODESK,
self::PROVIDER_DIALPAD,
self::PROVIDER_ZOOM_PHONE,
self::PROVIDER_CLOUDCALL,
self::PROVIDER_CLOUDCALL_US,
self::PROVIDER_EIGHT_BY_EIGHT,
self::PROVIDER_EIGHT_BY_EIGHT_CA,
self::PROVIDER_EIGHT_BY_EIGHT_AP,
self::PROVIDER_EIGHT_BY_EIGHT_US_EAST,
self::PROVIDER_EIGHT_BY_EIGHT_US_WEST,
self::PROVIDER_CONNECT_AND_SELL,
self::PROVIDER_CLOUD_TALK,
self::PROVIDER_AMAZON_CONNECT,
self::PROVIDER_VONAGE,
self::PROVIDER_TALKDESK,
self::PROVIDER_TWILIO_FLEX,
self::PROVIDER_TWILIO_FLEX_DIRECT,
self::PROVIDER_TWILIO_VIDEO,
self::PROVIDER_AVAYA,
self::PROVIDER_TELUS,
self::PROVIDER_FIVE_NINE,
self::PROVIDER_APOLLO,
self::PROVIDER_ORUM,
self::PROVIDER_BLOOBIRDS,
];
public static $enumRecordingStates = [
self::RECORDING_OFF, // Default state
self::RECORDING_IN_PROGRESS,
self::RECORDING_PAUSED,
self::RECORDING_STOPPED,
self::RECORDING_RECORDED,
self::RECORDING_FAILED,
];
// @Important:
// This collection is not used anywhere, and is fully duplicated by the Channels const.
// Validate if it is referred somehow via the enum trait, and if not, remove it entirely.
// An even better strategy will be to move all those constants to a dedicated class
protected array $enumTypes = [
self::TYPE_SOFTPHONE,
self::TYPE_SOFTPHONE_INBOUND,
self::TYPE_CONFERENCE,
self::TYPE_SMS_INBOUND,
self::TYPE_SMS_OUTBOUND,
self::TYPE_EMAIL_INBOUND,
self::TYPE_EMAIL_OUTBOUND,
];
protected static $enumFailedStatuses = [
self::STATUS_NO_ANSWER,
self::STATUS_FAILED,
self::STATUS_BUSY,
self::STATUS_CANCELLED,
];
protected $table = 'activities';
protected $fillable = [
// Type of activity.
'type', // @todo refactor to `channel`
// The activity type.
'playbook_category_id',
// User who hosts the activity.
'user_id',
// Related Lead record (if applicable)
'lead_id',
// Related Account record (if applicable)
'account_id',
// Related Contact record (if applicable)
'contact_id',
// Related Opportunity record (if applicable)
'opportunity_id',
// Stage of activity.
'stage_id',
// Value of opportunity.
'value',
// If the activity relates to a CRM task.
'crm_provider_id',
// If the activity was created through an external device.
'device_id',
// the activity's language code
'language',
// transcription id
'transcription_id',
// Duration of the call, with microseconds precision.
'duration',
// One of enumStatuses above.
'status',
// Have we reminded them to log the call?
'log_reminder_sent_at',
// If activity is private or inter-org, flagged here.
'is_internal',
// Managers and above can mark a call as private, to exclude it from other team members
'is_private',
'is_processed',
// Boolean for this activity being instant invite handled.
'is_instant_invite',
// If activity is in recording state, flagged here.
'recording_state',
// If activity recording is overidden from default.
'recording_preference',
// if recording did (not) happen, why that is
'recording_reason_code',
// Average score, updated during
'average_score',
// Summary that the organizer has taken after the call.
'summary',
// Subject of the activity, usually taken from calendar event.
'title',
// Description of the activity, usually taken from calendar event.
'description',
// Start time, usually taken from calendar event.
'scheduled_start_time',
// End time, usually taken from calendar event.
'scheduled_end_time',
// When the call actually started.
'actual_start_time',
// When the call actually ended.
'actual_end_time',
// SMS: Message reference
'telephony_provider_id',
// SMS: Participant who sent message
'from_participant_id',
// SMS: Participant who should receive the message
'to_participant_id',
// When an external guest joins an organizers meeting room and the organizer is not present,
// send them an SMS notification that someone has joined.
'organizer_notified_at',
// where was the activity imported from
'source',
// The id in the source system (e.g. the bot id in Recall.ai)
'external_id',
// The provider, by default it is twilio.
'provider',
// Meeting location url
'location',
// The snapshot for displaying a poster image.
'poster_path',
'crm_configuration_id',
// If there is an automated message that the conversation is being recorded
'has_recording_prompt',
// If the activity is being live-streamed
'on_air',
'calendar_event_id',
];
protected $appends = [
'id_string',
'organizer',
];
protected $hidden = [
'uuid',
];
protected $visible = [
'id_string',
'type',
'duration',
'average_score',
'status',
'log_reminder_sent_at',
'title',
'description',
'is_internal',
'scheduled_start_time',
'scheduled_end_time',
'actual_start_time',
'actual_end_time',
'user',
'category',
'account',
'contact',
'opportunity',
'lead',
'stage',
'stats',
'participants',
'playlists',
'tracks',
'comments',
'plays',
'coachingFeedbacks',
'shares',
'favorites',
'language',
'transcription',
'is_private',
'is_instant_invite',
'on_air',
'calendar_event_id',
];
protected function casts(): array
{
return [
'scheduled_start_time' => 'datetime',
'scheduled_end_time' => 'datetime',
'actual_start_time' => 'datetime',
'actual_end_time' => 'datetime',
'organizer_notified_at' => 'datetime',
'log_reminder_sent_at' => 'datetime',
'is_internal' => 'boolean',
'duration' => 'integer',
'average_score' => 'decimal:2',
'is_private' => 'boolean',
'is_processed' => 'boolean',
'is_instant_invite' => 'boolean',
'value' => 'decimal:2',
'recording_preference' => 'boolean',
'recording_reason_code' => 'integer',
'has_recording_prompt' => 'boolean',
'on_air' => 'integer',
];
}
protected static function boot()
{
parent::boot();
static::updated(static function (Activity $activity) {
// If activity is about to start (pending, ringing, in-progress) or event is scheduled in less than 1 week
if (in_array($activity->status, [Activity::STATUS_PENDING, Activity::STATUS_RINGING, Activity::STATUS_IN_PROGRESS], true) ||
($activity->scheduled_start_time && (int) $activity->scheduled_start_time->diffInWeeks(new Carbon(), true) < 1)) {
if ($activity->isDirty('status')) {
event(new StatusUpdated($activity));
}
if ($activity->isDirty('stage_id')) {
event(new StageUpdated($activity));
}
if ($activity->isDirty(['lead_id', 'account_id', 'contact_id'])) {
event(new ProspectUpdated($activity));
}
if ($activity->isDirty('opportunity_id')) {
event(new ActivityUpdated($activity, 'activity.opportunity-updated', Auth::user()));
}
if ($activity->isDirty('title')) {
event(new TitleUpdated($activity));
}
}
if ($activity->isDirty('playbook_category_id')) {
event(new ActivityTypeUpdated($activity));
}
});
static::deleted(static function (Activity $activity) {
// Hard delete associated playlistActivities
$activity->playlistActivities()->delete();
});
}
public function getOrganizerAttribute(): ?Participant
{
$participant = $this->participants()->where('user_id', $this->user_id)->first();
if (! $participant instanceof Participant && $participant !== null) {
throw new RuntimeException(sprintf('$participant must be an instance of "%s" or null', Participant::class));
}
return $participant;
}
public function getFormattedValueAttribute()
{
$currencyCode = 'USD';
if ($this->opportunity) {
$currencyCode = $this->opportunity->getCurrencyCode();
}
$formatter = new CurrencyFormatter();
$formatter->setTextAttribute(NumberFormatter::CURRENCY_CODE, $currencyCode);
$formatter->setAttribute(NumberFormatter::MAX_FRACTION_DIGITS, 0);
return $formatter->format($this->value, $currencyCode);
}
public function getProspectNameAttribute(): ?string
{
$prospectName = null;
if ($this->lead_id) {
$prospectName = $this->lead->name;
} elseif ($this->contact_id) {
$prospectName = $this->contact->name;
} elseif ($this->account_id) {
$prospectName = $this->account->name;
}
return $prospectName;
}
public function getProspectName(): ?string
{
/** @var string|null */
return $this->getAttribute('prospect_name');
}
/**
* Get activity title depending on prospect or title
*/
public function getActivityTitleAttribute(): ?string
{
$activityTitle = null;
if ($this->prospect && $this->prospect->getName()) {
if ($this->account_id) {
$activityTitle = $this->account->name;
} elseif ($this->lead_id) {
$activityTitle = $this->lead->company;
} elseif ($this->contact_id) {
$activityTitle = $this->contact->account ? $this->contact->account->name : $this->contact->name;
}
} elseif ($this->title) {
$activityTitle = $this->title;
}
return $activityTitle;
}
public function wasRecentlyCreated(): bool
{
return $this->wasRecentlyCreated;
}
public function getProspectTypeAttribute()
{
$prospectType = null;
if ($this->lead_id) {
$prospectType = 'Lead';
} elseif ($this->contact_id) {
$prospectType = 'Contact';
} elseif ($this->account_id) {
$prospectType = 'Account';
}
return $prospectType;
}
/**
* Return the best match for prospect. Results are in the following order of priority:
* 1. Lead
* 2. Contact
* 3. Account
* 4. NULL
*/
public function getProspectAttribute(): ?ProspectInterface
{
if ($this->hasLead()) {
return $this->getLead();
}
if ($this->hasContact()) {
return $this->getContact();
}
if ($this->hasAccount()) {
return $this->getAccount();
}
return null;
}
public function getTitleAttribute($value): ?string
{
return \getActivityTitleAttribute(
$this->user->name,
$this->getType(),
$value,
$this->prospect->name ?? null,
$this->from->national_phone_number ?? null
);
}
public function getTitle(): ?string
{
return $this->getAttribute('title');
}
public function getSummary(): ?string
{
return $this->getAttribute('summary');
}
public function isInternal(): bool
{
return $this->getAttribute('is_internal');
}
public function getIsPrivate(): bool
{
return $this->getAttribute('is_private');
}
public function getDescription(): ?string
{
return $this->getAttribute('description');
}
public function hasTitle(): bool
{
return $this->getOriginal('title') !== null;
}
public function getPlayCountAttribute()
{
return $this->getPlaysCountAttribute();
}
public function getPlaysCountAttribute()
{
if (! isset($this->attributes['plays_count'])) {
$this->loadCount('plays');
}
return $this->attributes['plays_count'];
}
public function getCommentCountAttribute()
{
return $this->getCommentsCountAttribute();
}
public function getCommentsCountAttribute()
{
if (! isset($this->attributes['comments_count'])) {
$this->loadCount('comments');
}
return $this->attributes['comments_count'];
}
public function getVisibleCommentsCountAttribute()
{
if (! isset($this->attributes['visible_comments_count'])) {
$activityCommentsService = app(ActivityCommentService::class);
$user = Auth::user() instanceof User ? Auth::user() : null;
$this->attributes['visible_comments_count'] = $activityCommentsService
->getVisibleCommentsCount($this, $user);
}
return $this->attributes['visible_comments_count'];
}
public function getShareCountAttribute()
{
return $this->getSharesCountAttribute();
}
public function getSharesCountAttribute()
{
if (! isset($this->attributes['shares_count'])) {
$this->loadCount('shares');
}
return $this->attributes['shares_count'];
}
/**
* Get the count of favorites playlists this activity appears in
*/
public function getFavoriteCountAttribute(): int
{
return $this->getFavoritesCountAttribute();
}
public function getFavoritesCountAttribute()
{
if (! isset($this->attributes['favorites_count'])) {
$this->loadCount('favorites');
}
return $this->attributes['favorites_count'];
}
public function getActiveParticipantsCountAttribute()
{
if (! isset($this->attributes['active_participants_count'])) {
$this->loadCount('activeParticipants');
}
return $this->attributes['active_participants_count'];
}
public function getTracksWithTelephonyCountAttribute()
{
if (! isset($this->attributes['tracks_with_telephony_count'])) {
$this->loadCount('tracksWithTelephony');
}
return $this->attributes['tracks_with_telephony_count'];
}
/**
* @TEMP
* $this->loadCount('tracksWithTelephony') throws null pointer exception
*/
public function countTracksWithTelephony(): int
{
return $this->tracks()->whereNotNull('telephony_provider_id')->count();
}
public function getDuration(): float
{
return $this->getAttribute('duration');
}
public function getDurationForHumansAttribute()
{
return Carbon::now()->subSeconds($this->duration)->diffForHumans(now(), true);
}
public function getDurationForHumansShortAttribute(): string
{
return Carbon::now()->subSeconds($this->duration)->diffForHumans(now(), true, true);
}
public function hasRecordingPreference(): bool
{
ret...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"#12121 on JY-20963-fix-import-on-deleted-entity, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.11569149,"height":0.025538707},"on_screen":true,"help_text":"Pull request #12121 exists for current branch JY-20963-fix-import-on-deleted-entity, but pull request details loading failed","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.85638297,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"ServiceTest","depth":6,"bounds":{"left":0.87167555,"top":0.019952115,"width":0.043882977,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'ServiceTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'ServiceTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Analyzing…","depth":4,"bounds":{"left":0.34042552,"top":0.12529927,"width":0.019946808,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.3620346,"top":0.123703115,"width":0.00731383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.3693484,"top":0.123703115,"width":0.006981383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\nnamespace Jiminny\\Models;\n\nuse Carbon\\Carbon;\nuse Database\\Factories\\ActivityFactory;\nuse DateTimeInterface;\nuse Exception;\nuse Illuminate\\Contracts\\Auth\\Authenticatable;\nuse Illuminate\\Database\\Eloquent;\nuse Illuminate\\Database\\Eloquent\\Attributes\\Scope;\nuse Illuminate\\Database\\Eloquent\\Builder;\nuse Illuminate\\Database\\Eloquent\\Factories\\Factory;\nuse Illuminate\\Database\\Eloquent\\Factories\\HasFactory;\nuse Illuminate\\Database\\Eloquent\\Relations\\BelongsTo;\nuse Illuminate\\Database\\Eloquent\\Relations\\BelongsToMany;\nuse Illuminate\\Database\\Eloquent\\Relations\\HasMany;\nuse Illuminate\\Database\\Eloquent\\Relations\\HasManyThrough;\nuse Illuminate\\Database\\Eloquent\\Relations\\HasOne;\nuse Illuminate\\Database\\Eloquent\\SoftDeletes;\nuse Illuminate\\Support\\Collection;\nuse Illuminate\\Support\\Facades\\Auth;\nuse InvalidArgumentException;\nuse Jiminny\\Component\\ElasticSearch;\nuse Jiminny\\Component\\MeetingBot;\nuse Jiminny\\Component\\Model\\BitwiseFlagTrait;\nuse Jiminny\\Component\\PlaybackPage\\Comments\\Services\\ActivityCommentService;\nuse Jiminny\\Component\\Sidekick\\SidekickService;\nuse Jiminny\\Component\\Uuid\\UuidAwareInterface;\nuse Jiminny\\Component\\Workflow;\nuse Jiminny\\Contracts;\nuse Jiminny\\Contracts\\Crm\\ProspectInterface;\nuse Jiminny\\DTO\\ImportCall\\Call;\nuse Jiminny\\Events\\Activities\\ActivityTypeUpdated;\nuse Jiminny\\Events\\Activities\\ActivityUpdated;\nuse Jiminny\\Events\\Activities\\ProspectUpdated;\nuse Jiminny\\Events\\Activities\\StageUpdated;\nuse Jiminny\\Events\\Activities\\StatusUpdated;\nuse Jiminny\\Events\\Activities\\TitleUpdated;\nuse Jiminny\\Exceptions\\InvalidArgumentException as InvalidArgumentJiminnyException;\nuse Jiminny\\Exceptions\\LogicException;\nuse Jiminny\\Exceptions\\RuntimeException;\nuse Jiminny\\Models;\nuse Jiminny\\Models\\Activity\\ActivitySummaryLog;\nuse Jiminny\\Models\\Activity\\ActivityUploadSetting;\nuse Jiminny\\Models\\Activity\\AvailabilityNotification;\nuse Jiminny\\Models\\Activity\\CoachRequest;\nuse Jiminny\\Models\\Activity\\Comment;\nuse Jiminny\\Models\\Activity\\Log;\nuse Jiminny\\Models\\Activity\\Message;\nuse Jiminny\\Models\\Activity\\Moment;\nuse Jiminny\\Models\\Activity\\Note;\nuse Jiminny\\Models\\Activity\\ParticipantSpeech;\nuse Jiminny\\Models\\Activity\\Play;\nuse Jiminny\\Models\\Activity\\Question;\nuse Jiminny\\Models\\Activity\\Share;\nuse Jiminny\\Models\\Activity\\Snapshot;\nuse Jiminny\\Models\\Activity\\Stats;\nuse Jiminny\\Models\\Activity\\SubscriptionSet;\nuse Jiminny\\Models\\Activity\\TopicTrigger;\nuse Jiminny\\Models\\Activity\\Transcription;\nuse Jiminny\\Models\\Calendar\\CalendarEvent;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Models\\Crm\\FieldData;\nuse Jiminny\\Models\\ElasticSearch\\ActivityElasticSearchTrait;\nuse Jiminny\\Models\\Feature\\FeatureEnum;\nuse Jiminny\\Models\\Participant\\Connection;\nuse Jiminny\\Models\\Playlist\\Activity as PlaylistActivity;\nuse Jiminny\\Services\\Activity\\ActivityProviderRegistry;\nuse Jiminny\\Services\\Activity\\Import\\DataResolvers\\UpdateCrmDataByStrategy;\nuse Jiminny\\Services\\Activity\\Import\\DataResolvers\\UpdateCrmDataResolverFactory;\nuse Jiminny\\Services\\Activity\\Import\\DataResolvers\\UpdateCrmDataResolverInterface;\nuse Jiminny\\Traits\\Enums;\nuse Jiminny\\Traits\\RequiresUUID;\nuse Jiminny\\Utils\\CurrencyFormatter;\nuse NumberFormatter;\n\nuse function in_array;\n\n/**\n * Jiminny\\Models\\Activity\n *\n * @property null|int $auto_score filled from ES hydrator, not in DB!\n * @property-read Account|null $account\n * @property-read CalendarEvent|null $calendarEvent\n * @property-read Contact|null $contact\n * @property-read Lead|null $lead\n * @property-read Opportunity|null $opportunity\n * @property-read Stage|null $stage\n * @property int $id\n * @property mixed|null $uuid\n * @property string|null $source\n * @property string|null $external_id\n * @property string $provider\n * @property string|null $location\n * @property string|null $telephony_provider_id\n * @property int|null $from_participant_id\n * @property int|null $to_participant_id\n * @property int|null $device_id\n * @property string|null $type\n * @property int|null $playbook_category_id\n * @property int $user_id\n * @property int|null $lead_id\n * @property int|null $account_id\n * @property int|null $contact_id\n * @property int|null $opportunity_id\n * @property int|null $stage_id\n * @property string|null $value\n * @property int|null $crm_configuration_id\n * @property string|null $crm_provider_id\n * @property string|null $language\n * @property int|null $transcription_id\n * @property int $duration\n * @property string $status\n * @property int|null $on_air\n * @property int|null $calendar_event_id\n * @property string $recording_state\n * @property bool|null $recording_preference\n * @property int $recording_reason_code\n * @property int $summary_reminder_sent\n * @property \\Illuminate\\Support\\Carbon|null $log_reminder_sent_at\n * @property \\Illuminate\\Support\\Carbon|null $organizer_notified_at\n * @property bool|null $has_recording_prompt\n * @property bool $is_internal\n * @property int $is_locked\n * @property int $is_recording\n * @property bool|null $is_processed\n * @property bool $is_private\n * @property bool $is_instant_invite\n * @property string|null $poster_path\n * @property string|null $summary\n * @property string|null $title\n * @property string|null $description\n * @property \\Illuminate\\Support\\Carbon|null $scheduled_start_time\n * @property \\Illuminate\\Support\\Carbon|null $scheduled_end_time\n * @property \\Illuminate\\Support\\Carbon|null $actual_start_time\n * @property \\Illuminate\\Support\\Carbon|null $actual_end_time\n * @property int|null $uploaded_by\n * @property \\Illuminate\\Support\\Carbon|null $deleted_at\n * @property \\Illuminate\\Support\\Carbon|null $created_at\n * @property \\Illuminate\\Support\\Carbon|null $updated_at\n * @property string|null $average_score\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Participant> $activeParticipants\n * @property-read int|null $active_participants_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Scorecard\\ActivityScorecardRuleTrigger> $activityScorecardRuleTriggers\n * @property-read int|null $activity_scorecard_rule_triggers_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Scorecard\\ActivityScorecardRule> $activityScorecardRules\n * @property-read int|null $activity_scorecard_rules_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, AvailabilityNotification> $availabilityNotifications\n * @property-read int|null $availability_notifications_count\n * @property-read \\Jiminny\\Models\\PlaybookCategory|null $category\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, CoachRequest> $coachRequests\n * @property-read int|null $coach_requests_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\CoachingFeedback> $coachingFeedbacks\n * @property-read int|null $coaching_feedbacks_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Message> $coachingMessages\n * @property-read int|null $coaching_messages_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Comment> $comments\n * @property-read int|null $comments_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Connection> $connections\n * @property-read int|null $connections_count\n * @property-read Configuration|null $crm\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, FieldData> $data\n * @property-read int|null $data_count\n * @property-read \\Jiminny\\Models\\Device|null $device\n * @property-read \\Kalnoy\\Nestedset\\Collection<int, \\Jiminny\\Models\\Playlist> $favoritePlaylists\n * @property-read int|null $favorite_playlists_count\n * @property-read \\Jiminny\\Models\\Participant|null $from\n * @property-read string|null $activity_title\n * @property-read mixed $comment_count\n * @property-read mixed $duration_for_humans\n * @property-read string $duration_for_humans_short\n * @property-read int $favorite_count\n * @property-read mixed $favorites_count\n * @property-read mixed $formatted_value\n * @property-read string $id_string\n * @property-read \\Jiminny\\Models\\Participant|null $organizer\n * @property-read mixed $play_count\n * @property-read int|null $plays_count\n * @property-read ?ProspectInterface $prospect\n * @property-read string|null $prospect_name\n * @property-read mixed $prospect_type\n * @property-read mixed $share_count\n * @property-read int|null $shares_count\n * @property-read int|null $tracks_with_telephony_count\n * @property-read int|null $visible_comments_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\CoachingFeedback> $latestCoachingFeedbacks\n * @property-read int|null $latest_coaching_feedbacks_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Log> $logs\n * @property-read int|null $logs_count\n * @property-read \\Jiminny\\Models\\Track|null $masterTrack\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Message> $messages\n * @property-read int|null $messages_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Moment> $moments\n * @property-read int|null $moments_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Note> $notes\n * @property-read int|null $notes_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Participant\\Share> $participantShares\n * @property-read int|null $participant_shares_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, ParticipantSpeech> $participantSpeeches\n * @property-read int|null $participant_speeches_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Participant\\ParticipantStats> $participantStats\n * @property-read int|null $participant_stats_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Participant> $participants\n * @property-read int|null $participants_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, PlaylistActivity> $playlistActivities\n * @property-read int|null $playlist_activities_count\n * @property-read \\Kalnoy\\Nestedset\\Collection<int, \\Jiminny\\Models\\Playlist> $playlists\n * @property-read int|null $playlists_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Play> $plays\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Question> $questions\n * @property-read int|null $questions_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Session> $sessions\n * @property-read int|null $sessions_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Share> $shares\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Snapshot> $snapshots\n * @property-read int|null $snapshots_count\n * @property-read Stats|null $stats\n * @property-read \\Jiminny\\Models\\Participant|null $to\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, TopicTrigger> $topicTriggers\n * @property-read int|null $topic_triggers_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Track> $tracks\n * @property-read int|null $tracks_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Track> $tracksWithTelephony\n * @property-read Transcription|null $transcription\n * @property-read \\Jiminny\\Models\\User $user\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Comment> $visibleComments\n *\n * @method static \\Illuminate\\Database\\Eloquent\\Collection<int, static> all($columns = ['*'])\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity chunkByIdDesc($count, callable $callback, $column = null, $alias = null)\n * @method static \\Database\\Factories\\ActivityFactory factory(...$parameters)\n * @method static \\Illuminate\\Database\\Eloquent\\Collection<int, static> get($columns = ['*'])\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity heldBetween(\\Carbon\\Carbon $start, \\Carbon\\Carbon $end)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity idOrUuId($idOrUuid, bool $first = true)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity newModelQuery()\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity newQuery()\n * @method static Builder|Activity onlyTrashed()\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity query()\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity scheduledBetween(\\Carbon\\Carbon $start, \\Carbon\\Carbon $end)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity inOpenDeals()\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity notInOpenDeals()\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity forTeam(int $teamId)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity search(callable $searchQuery, $key = null, $sortByResults = true)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity uuid(string $uuid, bool $first = true)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereAccountId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereActualEndTime($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereActualStartTime($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereAverageScore($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereCalendarEventId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereContactId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereCreatedAt($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereCrmConfigurationId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereCrmProviderId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereDeletedAt($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereDescription($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereDeviceId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereDuration($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereFromParticipantId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereHasRecordingPrompt($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereIsInstantInvite($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereIsInternal($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereIsLocked($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereIsPrivate($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereIsProcessed($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereIsRecording($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereLanguage($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereLeadId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereLocation($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereLogReminderSentAt($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereOnAir($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereOpportunityId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereOrganizerNotifiedAt($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity wherePlaybookCategoryId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity wherePosterPath($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereProvider($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereRecordingPreference($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereRecordingReasonCode($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereRecordingState($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereScheduledEndTime($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereScheduledStartTime($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereSource($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereExternalId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereStageId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereStatus($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereSummary($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereSummaryReminderSent($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereTelephonyProviderId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereTitle($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereToParticipantId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereTranscriptionId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereType($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereUpdatedAt($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereUploadedBy($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereUserId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereUuid($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereValue($value)\n * @method static Builder|Activity withTrashed()\n * @method static Builder|Activity withoutTrashed()\n *\n * @mixin \\Eloquent\n */\nclass Activity extends Model implements\n ElasticSearch\\Contract\\Searchable,\n Workflow\\Workflow\\WorkflowAwareInterface,\n Models\\Contracts\\ActivityContract,\n Contracts\\Model\\ActivityInterface,\n UuidAwareInterface\n{\n use HasFactory;\n\n use Enums;\n use SoftDeletes;\n use RequiresUUID;\n use BitwiseFlagTrait;\n use ElasticSearch\\Model\\Searchable;\n use ActivityElasticSearchTrait;\n\n use Workflow\\Workflow\\WorkflowAware {\n transitionTo as traitTransitionTo;\n }\n\n public const int FLAG_RECORDING_REASON_DEFAULT = 0;\n\n // Recording Prompted but never started\n public const int FLAG_RECORDING_REASON_COMPLIANCE_PROMPT = 1;\n public const int FLAG_RECORDING_REASON_COMPLIANCE_RESUMED = 2;\n public const int FLAG_RECORDING_REASON_NO_AUDIO = 3;\n\n // Recording Disabled by Organization\n public const int FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT = 4;\n\n // Recording was restricted to one-side recordings only\n public const int FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT_ONE_SIDE = 8;\n\n // Recording was not started because it was internal and team setting disabled that.\n public const int FLAG_RECORDING_REASON_TEAM_INTERNAL_DISABLED = 16;\n\n // Recording was not started because it was internal and user setting disabled that.\n public const int FLAG_RECORDING_REASON_USER_INTERNAL_DISABLED = 32;\n\n // Recording was not started because user setting disabled automatic recording.\n public const int FLAG_RECORDING_REASON_USER_AUTOMATIC_DISABLED = 64;\n\n // Recording was not started because team setting disabled automatic recording.\n public const int FLAG_RECORDING_REASON_TEAM_AUTOMATIC_DISABLED = 128;\n\n // Recording was not started because user has overriden default.\n public const int FLAG_RECORDING_REASON_PREFERENCE_OVERRIDE = 256;\n\n // Recording was not started because they don't want internal, and this meeting was not scheduled/imported in time.\n public const int FLAG_RECORDING_REASON_USER_INTERNAL_DISABLED_UNSCHEDULED = 512;\n\n // Recording was not started because their team setting does excludes the meeting type.\n public const int FLAG_RECORDING_REASON_UNSUPPORTED_TYPE = 1024;\n\n // Recording was not started because the external provider disabled it (or recording is missing etc).\n public const int FLAG_RECORDING_REASON_EXTERNALLY_DISABLED = 2048;\n\n // Recording was stopped externally (\"exit-meeting\" Pusher event)\n public const int FLAG_RECORDING_REASON_STOPPED_EXTERNALLY = 384;\n\n // Recording couldn't be started due to Zoom hosting conflict error\n public const int FLAG_RECORDING_REASON_HOSTING_CONFLICT = 448;\n\n // meeting.failed event with reason code BOT_DENIED_FROM_LOBBY\n public const int FLAG_RECORDING_REASON_MEETING_BOT_DENIED_FROM_LOBBY = 4096;\n\n // meeting.failed event with reason code LOBBY_TIMEOUT\n public const int FLAG_RECORDING_REASON_MEETING_BOT_LOBBY_TIMEOUT = 8192;\n\n // meeting.failed event with reason code BOT_KICKED\n public const int FLAG_RECORDING_REASON_MEETING_BOT_KICKED = 16384;\n\n // meeting.failed event with reason code UNKNOWN\n public const int FLAG_RECORDING_REASON_MEETING_BOT_UNKNOWN = 32768;\n\n public const int FLAG_RECORDING_REASON_CONSENT_DENIED = 65536;\n\n // Invalid meeting (e.g. URL is invalid, or the meeting is not found)\n public const int FLAG_RECORDING_REASON_MEETING_BOT_INVALID = 131072;\n\n // The host stopped the recording.\n public const int FLAG_RECORDING_REASON_USER_STOPPED = 262144;\n\n // Recording was not started because an alternative vendor disabled it (or overrode it).\n public const int FLAG_RECORDING_REASON_VENDOR_OVERRIDE = 1048576;\n\n // Login required meeting.failed code\n public const int FLAG_RECORDING_REASON_LOGIN_REQUIRED = 524288;\n\n // Password for meeting was not provided - meeting.failed code\n public const int FLAG_RECORDING_REASON_MEETING_PASSWORD_NOT_PROVIDED = 2097152;\n\n // meeting.failed - when the meeting is locked\n public const int FLAG_RECORDING_REASON_MEETING_IS_LOCKED = 4194304;\n\n // max recording duration reached\n public const int FLAG_RECORDING_REASON_MAX_DURATION_REACHED = 8388608;\n\n // recording size is too small\n public const int FLAG_RECORDING_REASON_EMPTY_RECORDING = 16777216;\n\n // meeting.failed - when bot is redirected to sign in page multiple times\n public const int FLAG_RECORDING_REASON_MAX_RESTART_COUNT_IS_REACHED = 33554432;\n\n // meeting.failed event with reason code CONNECTION_LOST\n public const int FLAG_RECORDING_REASON_MEETING_BOT_CONNECTION_LOST = 67108864;\n\n // recording is corrupted.\n public const int FLAG_RECORDING_REASON_MEDIA_FILE_UNSUPPORTED_MIME_TYPE = 134217728;\n\n // meeting ended in lobby\n public const int FLAG_RECORDING_REASON_MEETING_ENDED_IN_LOBBY = 268435456;\n\n // meeting not started\n public const int FLAG_RECORDING_REASON_REASON_MEETING_NOT_STARTED = 536870912;\n\n // unfinished zoom custom disclaimer\n public const int FLAG_RECORDING_REASON_FEATURE_RULE_NOT_FOUND_ERROR = 1073741824;\n\n // recording download failed - server error\n public const int FLAG_RECORDING_REASON_SERVER_ERROR = 2147483648;\n\n // recording download failed - client code 404\n public const int FLAG_RECORDING_REASON_NOT_FOUND = 2147483649;\n\n // recording download failed - client code 401, 403\n public const int FLAG_RECORDING_REASON_ACCESS_DENIED = 2147483650;\n\n // recording download failed - client code 429\n public const int FLAG_RECORDING_REASON_TOO_MANY_REQUESTS = 2147483651;\n\n // recording download failed - unknown client error\n public const int FLAG_RECORDING_REASON_CLIENT_ERROR = 2147483652;\n\n // recording download failed - unknown error\n public const int FLAG_RECORDING_REASON_UNKNOWN_ERROR = 2147483653;\n\n // It has been setup ahead of time through calendar\n public const string STATUS_SCHEDULED = 'scheduled';\n\n // It is awaiting audio.\n public const string STATUS_PENDING = 'pending';\n\n // Participant(s) dialed in, awaiting organizer.\n public const string STATUS_RINGING = 'ringing';\n\n // Call is in progress.\n public const string STATUS_IN_PROGRESS = 'in-progress';\n\n // It has ended.\n public const string STATUS_COMPLETED = 'completed';\n\n // Cancelled prior to starting.\n public const string STATUS_CANCELLED = 'canceled';\n\n public const string STATUS_DUPLICATED = 'duplicated'; // duplicated conference\n\n public const string STATUS_STARTING_SOON = 'starting-soon';\n\n public const string STATUS_BOT_CREATE_SENT = 'bot-create-sent';\n\n public const string STATUS_BOT_INSTANCE_WORKER_ASSIGNED = 'worker-assigned';\n\n public const string STATUS_BOT_INSTANCE_STARTED = 'bot-started';\n\n // When bot instance is waiting in lobby\n public const string STATUS_BOT_INSTANCE_WAITING_LOBBY = 'bot-waiting';\n\n public const string STATUS_BUSY = 'busy';\n public const string STATUS_NO_ANSWER = 'no-answer';\n public const string STATUS_FAILED = 'failed'; // Used by SMS too\n\n // SMS related\n public const string STATUS_ACCEPTED = 'accepted';\n public const string STATUS_QUEUED = 'queued';\n public const string STATUS_SENDING = 'sending';\n public const string STATUS_SENT = 'sent';\n public const string STATUS_DELIVERED = 'delivered';\n public const string STATUS_UNDELIVERED = 'undelivered';\n public const string STATUS_RECEIVING = 'receiving';\n public const string STATUS_RECEIVED = 'received';\n public const string STATUS_RESENT = 'resent';\n\n public const array SMS_STATUSES = [\n Activity::STATUS_RECEIVED,\n Activity::STATUS_SENT,\n Activity::STATUS_DELIVERED,\n ];\n\n public const array SOFT_PHONE_CONFERENCE_STATUSES = [\n Activity::STATUS_IN_PROGRESS,\n Activity::STATUS_COMPLETED,\n ];\n\n // @todo refactor prefix from `TYPE_` to `CHANNEL_`\n public const string TYPE_SOFTPHONE = 'softphone';\n public const string TYPE_SOFTPHONE_INBOUND = 'softphone-inbound';\n public const string TYPE_CONFERENCE = 'conference';\n public const string TYPE_SMS_INBOUND = 'sms-inbound';\n public const string TYPE_SMS_OUTBOUND = 'sms-outbound';\n public const string TYPE_EMAIL_INBOUND = 'email-inbound';\n public const string TYPE_EMAIL_OUTBOUND = 'email-outbound';\n\n public const array CHANNELS = [\n self::TYPE_SOFTPHONE,\n self::TYPE_SOFTPHONE_INBOUND,\n self::TYPE_CONFERENCE,\n self::TYPE_SMS_INBOUND,\n self::TYPE_SMS_OUTBOUND,\n self::TYPE_EMAIL_INBOUND,\n self::TYPE_EMAIL_OUTBOUND,\n ];\n\n public const array PLAYABLE_CHANNELS = [\n self::TYPE_SOFTPHONE,\n self::TYPE_SOFTPHONE_INBOUND,\n self::TYPE_CONFERENCE,\n ];\n\n // Recording States\n public const string RECORDING_OFF = 'off'; // Default state\n public const string RECORDING_IN_PROGRESS = 'in-progress';\n public const string RECORDING_PAUSED = 'paused';\n public const string RECORDING_STOPPED = 'stopped'; // To never be resumed.\n public const string RECORDING_RECORDED = 'recorded'; // At least some portion of it was recorded.\n public const string RECORDING_FAILED = 'failed'; // Recording was attempted but failed for some reason.\n\n // Live Stream States\n public const int ON_AIR_DEFAULT = 0;\n public const int ON_AIR_READY = 1;\n public const int ON_AIR_PREPARING = 2;\n public const int ON_AIR_STREAMING = 3;\n public const int ON_AIR_FINISHED = 4;\n public const int ON_AIR_NOT_STREAMED = 5;\n public const int ON_AIR_ERROR = -1;\n\n public const string SOURCE_GONG = 'gong';\n public const string SOURCE_CHORUS = 'chorus';\n public const string SOURCE_OUTLOOK = 'outlook';\n public const string SOURCE_GOOGLE = 'google';\n\n // Activity Providers\n public const string PROVIDER_TWILIO = 'twilio'; // XXX: This is run via the Jiminny Provider.\n public const string PROVIDER_OUTREACH = 'outreach';\n public const string PROVIDER_ZOOM_BOT = 'zoom-bot';\n public const string PROVIDER_SALESLOFT = 'salesloft';\n public const string PROVIDER_GOOGLE = 'google';\n public const string PROVIDER_AIRCALL = 'aircall';\n public const string PROVIDER_JUSTCALL = 'justcall';\n public const string PROVIDER_GOOGLE_MEET = 'google-meet';\n public const string PROVIDER_GONG = 'gong';\n public const string PROVIDER_HUBSPOT = 'hubspot';\n public const string PROVIDER_CLOSE = 'close';\n public const string PROVIDER_TEAMS = 'ms-teams';\n public const string PROVIDER_SALESFORCE = 'salesforce';\n public const string PROVIDER_GROOVE = 'groove';\n public const string PROVIDER_XANT = 'xant';\n public const string PROVIDER_OFFICE = 'office';\n public const string PROVIDER_NATTERBOX = 'natterbox';\n public const string PROVIDER_RINGCENTRAL = 'ringcentral';\n public const string PROVIDER_RINGCENTRAL_VIDEO = 'ringcentral-video';\n public const string PROVIDER_GOTOMEETING = 'go-to-meeting';\n public const string PROVIDER_DEMODESK = 'demo-desk';\n public const string PROVIDER_DIALPAD = 'dialpad';\n public const string PROVIDER_ZOOM_PHONE = 'zoom-phone';\n public const string PROVIDER_CLOUDCALL = 'cloudcall';\n public const string PROVIDER_CLOUDCALL_US = 'cloudcall-us';\n public const string PROVIDER_EIGHT_BY_EIGHT = 'eight-by-eight'; // \"8x8\" UK\n public const string PROVIDER_EIGHT_BY_EIGHT_CA = 'eight-by-eight-ca'; // \"8x8\" Canada\n public const string PROVIDER_EIGHT_BY_EIGHT_AP = 'eight-by-eight-ap'; // \"8x8\" Australia\n public const string PROVIDER_EIGHT_BY_EIGHT_US_EAST = 'eight-by-eight-use'; // \"8x8\" US East\n public const string PROVIDER_EIGHT_BY_EIGHT_US_WEST = 'eight-by-eight-usw'; // \"8x8\" US West\n public const string PROVIDER_CONNECT_AND_SELL = 'connect-and-sell';\n public const string PROVIDER_CLOUD_TALK = 'cloud-talk';\n public const string PROVIDER_AMAZON_CONNECT = 'amazon-connect';\n public const string PROVIDER_VONAGE = 'vonage';\n public const string PROVIDER_MIGRATOR = 'migrator';\n public const string PROVIDER_UPLOADER = 'uploader';\n public const string PROVIDER_TALKDESK = 'talkdesk';\n public const string PROVIDER_TWILIO_FLEX = 'twilio-flex';\n public const string PROVIDER_TWILIO_FLEX_DIRECT = 'twilio-flex-direct';\n public const string PROVIDER_TWILIO_VIDEO = 'twilio-video';\n public const string PROVIDER_AVAYA = 'avaya';\n public const string PROVIDER_TELUS = 'telus';\n public const string PROVIDER_FIVE_NINE = 'five-nine';\n public const string PROVIDER_APOLLO = 'apollo';\n public const string PROVIDER_ORUM = 'orum';\n public const string PROVIDER_BLOOBIRDS = 'bloobirds';\n\n /**\n * @const API_PROVIDERS\n * A list of integrations that import calls via API instead of webhooks\n */\n public const array API_PROVIDERS = [\n self::PROVIDER_OUTREACH,\n self::PROVIDER_SALESLOFT,\n self::PROVIDER_HUBSPOT,\n self::PROVIDER_GROOVE,\n self::PROVIDER_XANT,\n self::PROVIDER_NATTERBOX,\n self::PROVIDER_CLOUDCALL,\n self::PROVIDER_CLOUDCALL_US,\n self::PROVIDER_EIGHT_BY_EIGHT,\n self::PROVIDER_EIGHT_BY_EIGHT_CA,\n self::PROVIDER_EIGHT_BY_EIGHT_AP,\n self::PROVIDER_EIGHT_BY_EIGHT_US_EAST,\n self::PROVIDER_EIGHT_BY_EIGHT_US_WEST,\n self::PROVIDER_CONNECT_AND_SELL,\n self::PROVIDER_CLOUD_TALK,\n self::PROVIDER_AMAZON_CONNECT,\n self::PROVIDER_VONAGE,\n self::PROVIDER_TALKDESK,\n self::PROVIDER_TWILIO_VIDEO,\n self::PROVIDER_TWILIO_FLEX,\n self::PROVIDER_TWILIO_FLEX_DIRECT,\n self::PROVIDER_FIVE_NINE,\n self::PROVIDER_APOLLO,\n self::PROVIDER_ORUM,\n self::PROVIDER_BLOOBIRDS,\n self::PROVIDER_RINGCENTRAL,\n self::PROVIDER_AVAYA,\n self::PROVIDER_TELUS,\n ];\n\n public const array FINITE_STATES = [\n self::TYPE_SOFTPHONE => [\n self::STATUS_COMPLETED,\n self::STATUS_FAILED,\n self::STATUS_NO_ANSWER,\n self::STATUS_BUSY,\n ],\n self::TYPE_SOFTPHONE_INBOUND => [\n self::STATUS_COMPLETED,\n self::STATUS_FAILED,\n self::STATUS_NO_ANSWER,\n self::STATUS_BUSY,\n ],\n self::TYPE_CONFERENCE => self::FINITE_STATES_CONFERENCE,\n ];\n\n public const array FINITE_STATES_CONFERENCE = [\n self::STATUS_COMPLETED,\n self::STATUS_FAILED,\n self::STATUS_CANCELLED,\n ];\n\n public const array MEETING_BOT_JOIN_ATTEMPTED = [\n self::STATUS_BOT_INSTANCE_WAITING_LOBBY,\n self::STATUS_BOT_INSTANCE_STARTED,\n ];\n\n public static array $enumStatuses = [\n self::STATUS_SCHEDULED,\n self::STATUS_PENDING,\n self::STATUS_RINGING,\n self::STATUS_IN_PROGRESS,\n self::STATUS_COMPLETED,\n self::STATUS_CANCELLED,\n self::STATUS_BUSY,\n self::STATUS_NO_ANSWER,\n self::STATUS_FAILED,\n self::STATUS_ACCEPTED,\n self::STATUS_QUEUED,\n self::STATUS_SENDING,\n self::STATUS_SENT,\n self::STATUS_RESENT,\n self::STATUS_DELIVERED,\n self::STATUS_UNDELIVERED,\n self::STATUS_RECEIVING,\n self::STATUS_RECEIVED,\n self::STATUS_BOT_INSTANCE_WAITING_LOBBY,\n self::STATUS_STARTING_SOON,\n self::STATUS_BOT_INSTANCE_WORKER_ASSIGNED,\n self::STATUS_BOT_INSTANCE_STARTED,\n self::STATUS_DUPLICATED,\n ];\n\n public static array $enumProviders = [\n self::PROVIDER_TWILIO,\n self::PROVIDER_OUTREACH,\n self::PROVIDER_ZOOM_BOT,\n self::PROVIDER_SALESLOFT,\n self::PROVIDER_AIRCALL,\n self::PROVIDER_JUSTCALL,\n self::PROVIDER_GOOGLE_MEET,\n self::PROVIDER_GONG,\n self::PROVIDER_HUBSPOT,\n self::PROVIDER_CLOSE,\n self::PROVIDER_TEAMS,\n self::PROVIDER_SALESFORCE,\n self::PROVIDER_GROOVE,\n self::PROVIDER_XANT,\n self::PROVIDER_GOOGLE,\n self::PROVIDER_OFFICE,\n self::PROVIDER_NATTERBOX,\n self::PROVIDER_RINGCENTRAL,\n self::PROVIDER_RINGCENTRAL_VIDEO,\n self::PROVIDER_GOTOMEETING,\n self::PROVIDER_DEMODESK,\n self::PROVIDER_DIALPAD,\n self::PROVIDER_ZOOM_PHONE,\n self::PROVIDER_CLOUDCALL,\n self::PROVIDER_CLOUDCALL_US,\n self::PROVIDER_EIGHT_BY_EIGHT,\n self::PROVIDER_EIGHT_BY_EIGHT_CA,\n self::PROVIDER_EIGHT_BY_EIGHT_AP,\n self::PROVIDER_EIGHT_BY_EIGHT_US_EAST,\n self::PROVIDER_EIGHT_BY_EIGHT_US_WEST,\n self::PROVIDER_CONNECT_AND_SELL,\n self::PROVIDER_CLOUD_TALK,\n self::PROVIDER_AMAZON_CONNECT,\n self::PROVIDER_VONAGE,\n self::PROVIDER_TALKDESK,\n self::PROVIDER_TWILIO_FLEX,\n self::PROVIDER_TWILIO_FLEX_DIRECT,\n self::PROVIDER_TWILIO_VIDEO,\n self::PROVIDER_AVAYA,\n self::PROVIDER_TELUS,\n self::PROVIDER_FIVE_NINE,\n self::PROVIDER_APOLLO,\n self::PROVIDER_ORUM,\n self::PROVIDER_BLOOBIRDS,\n ];\n\n public static $enumRecordingStates = [\n self::RECORDING_OFF, // Default state\n self::RECORDING_IN_PROGRESS,\n self::RECORDING_PAUSED,\n self::RECORDING_STOPPED,\n self::RECORDING_RECORDED,\n self::RECORDING_FAILED,\n ];\n\n // @Important:\n // This collection is not used anywhere, and is fully duplicated by the Channels const.\n // Validate if it is referred somehow via the enum trait, and if not, remove it entirely.\n // An even better strategy will be to move all those constants to a dedicated class\n protected array $enumTypes = [\n self::TYPE_SOFTPHONE,\n self::TYPE_SOFTPHONE_INBOUND,\n self::TYPE_CONFERENCE,\n self::TYPE_SMS_INBOUND,\n self::TYPE_SMS_OUTBOUND,\n self::TYPE_EMAIL_INBOUND,\n self::TYPE_EMAIL_OUTBOUND,\n ];\n\n protected static $enumFailedStatuses = [\n self::STATUS_NO_ANSWER,\n self::STATUS_FAILED,\n self::STATUS_BUSY,\n self::STATUS_CANCELLED,\n ];\n\n protected $table = 'activities';\n\n protected $fillable = [\n // Type of activity.\n 'type', // @todo refactor to `channel`\n // The activity type.\n 'playbook_category_id',\n // User who hosts the activity.\n 'user_id',\n // Related Lead record (if applicable)\n 'lead_id',\n // Related Account record (if applicable)\n 'account_id',\n // Related Contact record (if applicable)\n 'contact_id',\n // Related Opportunity record (if applicable)\n 'opportunity_id',\n // Stage of activity.\n 'stage_id',\n // Value of opportunity.\n 'value',\n // If the activity relates to a CRM task.\n 'crm_provider_id',\n // If the activity was created through an external device.\n 'device_id',\n // the activity's language code\n 'language',\n // transcription id\n 'transcription_id',\n // Duration of the call, with microseconds precision.\n 'duration',\n // One of enumStatuses above.\n 'status',\n // Have we reminded them to log the call?\n 'log_reminder_sent_at',\n // If activity is private or inter-org, flagged here.\n 'is_internal',\n // Managers and above can mark a call as private, to exclude it from other team members\n 'is_private',\n 'is_processed',\n // Boolean for this activity being instant invite handled.\n 'is_instant_invite',\n // If activity is in recording state, flagged here.\n 'recording_state',\n // If activity recording is overidden from default.\n 'recording_preference',\n // if recording did (not) happen, why that is\n 'recording_reason_code',\n // Average score, updated during\n 'average_score',\n // Summary that the organizer has taken after the call.\n 'summary',\n // Subject of the activity, usually taken from calendar event.\n 'title',\n // Description of the activity, usually taken from calendar event.\n 'description',\n // Start time, usually taken from calendar event.\n 'scheduled_start_time',\n // End time, usually taken from calendar event.\n 'scheduled_end_time',\n // When the call actually started.\n 'actual_start_time',\n // When the call actually ended.\n 'actual_end_time',\n // SMS: Message reference\n 'telephony_provider_id',\n // SMS: Participant who sent message\n 'from_participant_id',\n // SMS: Participant who should receive the message\n 'to_participant_id',\n // When an external guest joins an organizers meeting room and the organizer is not present,\n // send them an SMS notification that someone has joined.\n 'organizer_notified_at',\n // where was the activity imported from\n 'source',\n // The id in the source system (e.g. the bot id in Recall.ai)\n 'external_id',\n // The provider, by default it is twilio.\n 'provider',\n // Meeting location url\n 'location',\n // The snapshot for displaying a poster image.\n 'poster_path',\n 'crm_configuration_id',\n // If there is an automated message that the conversation is being recorded\n 'has_recording_prompt',\n // If the activity is being live-streamed\n 'on_air',\n 'calendar_event_id',\n ];\n\n protected $appends = [\n 'id_string',\n 'organizer',\n ];\n\n protected $hidden = [\n 'uuid',\n ];\n\n protected $visible = [\n 'id_string',\n 'type',\n 'duration',\n 'average_score',\n 'status',\n 'log_reminder_sent_at',\n 'title',\n 'description',\n 'is_internal',\n 'scheduled_start_time',\n 'scheduled_end_time',\n 'actual_start_time',\n 'actual_end_time',\n 'user',\n 'category',\n 'account',\n 'contact',\n 'opportunity',\n 'lead',\n 'stage',\n 'stats',\n 'participants',\n 'playlists',\n 'tracks',\n 'comments',\n 'plays',\n 'coachingFeedbacks',\n 'shares',\n 'favorites',\n 'language',\n 'transcription',\n 'is_private',\n 'is_instant_invite',\n 'on_air',\n 'calendar_event_id',\n ];\n\n protected function casts(): array\n {\n return [\n 'scheduled_start_time' => 'datetime',\n 'scheduled_end_time' => 'datetime',\n 'actual_start_time' => 'datetime',\n 'actual_end_time' => 'datetime',\n 'organizer_notified_at' => 'datetime',\n 'log_reminder_sent_at' => 'datetime',\n 'is_internal' => 'boolean',\n 'duration' => 'integer',\n 'average_score' => 'decimal:2',\n 'is_private' => 'boolean',\n 'is_processed' => 'boolean',\n 'is_instant_invite' => 'boolean',\n 'value' => 'decimal:2',\n 'recording_preference' => 'boolean',\n 'recording_reason_code' => 'integer',\n 'has_recording_prompt' => 'boolean',\n 'on_air' => 'integer',\n ];\n }\n\n protected static function boot()\n {\n parent::boot();\n\n static::updated(static function (Activity $activity) {\n // If activity is about to start (pending, ringing, in-progress) or event is scheduled in less than 1 week\n if (in_array($activity->status, [Activity::STATUS_PENDING, Activity::STATUS_RINGING, Activity::STATUS_IN_PROGRESS], true) ||\n ($activity->scheduled_start_time && (int) $activity->scheduled_start_time->diffInWeeks(new Carbon(), true) < 1)) {\n if ($activity->isDirty('status')) {\n event(new StatusUpdated($activity));\n }\n\n if ($activity->isDirty('stage_id')) {\n event(new StageUpdated($activity));\n }\n\n if ($activity->isDirty(['lead_id', 'account_id', 'contact_id'])) {\n event(new ProspectUpdated($activity));\n }\n\n if ($activity->isDirty('opportunity_id')) {\n event(new ActivityUpdated($activity, 'activity.opportunity-updated', Auth::user()));\n }\n\n if ($activity->isDirty('title')) {\n event(new TitleUpdated($activity));\n }\n }\n\n if ($activity->isDirty('playbook_category_id')) {\n event(new ActivityTypeUpdated($activity));\n }\n });\n\n static::deleted(static function (Activity $activity) {\n // Hard delete associated playlistActivities\n $activity->playlistActivities()->delete();\n });\n }\n\n public function getOrganizerAttribute(): ?Participant\n {\n $participant = $this->participants()->where('user_id', $this->user_id)->first();\n\n if (! $participant instanceof Participant && $participant !== null) {\n throw new RuntimeException(sprintf('$participant must be an instance of \"%s\" or null', Participant::class));\n }\n\n return $participant;\n }\n\n public function getFormattedValueAttribute()\n {\n $currencyCode = 'USD';\n if ($this->opportunity) {\n $currencyCode = $this->opportunity->getCurrencyCode();\n }\n\n $formatter = new CurrencyFormatter();\n $formatter->setTextAttribute(NumberFormatter::CURRENCY_CODE, $currencyCode);\n $formatter->setAttribute(NumberFormatter::MAX_FRACTION_DIGITS, 0);\n\n return $formatter->format($this->value, $currencyCode);\n }\n\n public function getProspectNameAttribute(): ?string\n {\n $prospectName = null;\n\n if ($this->lead_id) {\n $prospectName = $this->lead->name;\n } elseif ($this->contact_id) {\n $prospectName = $this->contact->name;\n } elseif ($this->account_id) {\n $prospectName = $this->account->name;\n }\n\n return $prospectName;\n }\n\n public function getProspectName(): ?string\n {\n /** @var string|null */\n return $this->getAttribute('prospect_name');\n }\n\n /**\n * Get activity title depending on prospect or title\n */\n public function getActivityTitleAttribute(): ?string\n {\n $activityTitle = null;\n if ($this->prospect && $this->prospect->getName()) {\n if ($this->account_id) {\n $activityTitle = $this->account->name;\n } elseif ($this->lead_id) {\n $activityTitle = $this->lead->company;\n } elseif ($this->contact_id) {\n $activityTitle = $this->contact->account ? $this->contact->account->name : $this->contact->name;\n }\n } elseif ($this->title) {\n $activityTitle = $this->title;\n }\n\n return $activityTitle;\n }\n\n public function wasRecentlyCreated(): bool\n {\n return $this->wasRecentlyCreated;\n }\n\n public function getProspectTypeAttribute()\n {\n $prospectType = null;\n\n if ($this->lead_id) {\n $prospectType = 'Lead';\n } elseif ($this->contact_id) {\n $prospectType = 'Contact';\n } elseif ($this->account_id) {\n $prospectType = 'Account';\n }\n\n return $prospectType;\n }\n\n /**\n * Return the best match for prospect. Results are in the following order of priority:\n * 1. Lead\n * 2. Contact\n * 3. Account\n * 4. NULL\n */\n public function getProspectAttribute(): ?ProspectInterface\n {\n if ($this->hasLead()) {\n return $this->getLead();\n }\n\n if ($this->hasContact()) {\n return $this->getContact();\n }\n\n if ($this->hasAccount()) {\n return $this->getAccount();\n }\n\n return null;\n }\n\n public function getTitleAttribute($value): ?string\n {\n return \\getActivityTitleAttribute(\n $this->user->name,\n $this->getType(),\n $value,\n $this->prospect->name ?? null,\n $this->from->national_phone_number ?? null\n );\n }\n\n public function getTitle(): ?string\n {\n return $this->getAttribute('title');\n }\n\n public function getSummary(): ?string\n {\n return $this->getAttribute('summary');\n }\n\n public function isInternal(): bool\n {\n return $this->getAttribute('is_internal');\n }\n\n public function getIsPrivate(): bool\n {\n return $this->getAttribute('is_private');\n }\n\n public function getDescription(): ?string\n {\n return $this->getAttribute('description');\n }\n\n public function hasTitle(): bool\n {\n return $this->getOriginal('title') !== null;\n }\n\n public function getPlayCountAttribute()\n {\n return $this->getPlaysCountAttribute();\n }\n\n public function getPlaysCountAttribute()\n {\n if (! isset($this->attributes['plays_count'])) {\n $this->loadCount('plays');\n }\n\n return $this->attributes['plays_count'];\n }\n\n public function getCommentCountAttribute()\n {\n return $this->getCommentsCountAttribute();\n }\n\n public function getCommentsCountAttribute()\n {\n if (! isset($this->attributes['comments_count'])) {\n $this->loadCount('comments');\n }\n\n return $this->attributes['comments_count'];\n }\n\n public function getVisibleCommentsCountAttribute()\n {\n if (! isset($this->attributes['visible_comments_count'])) {\n $activityCommentsService = app(ActivityCommentService::class);\n $user = Auth::user() instanceof User ? Auth::user() : null;\n $this->attributes['visible_comments_count'] = $activityCommentsService\n ->getVisibleCommentsCount($this, $user);\n }\n\n return $this->attributes['visible_comments_count'];\n }\n\n public function getShareCountAttribute()\n {\n return $this->getSharesCountAttribute();\n }\n\n public function getSharesCountAttribute()\n {\n if (! isset($this->attributes['shares_count'])) {\n $this->loadCount('shares');\n }\n\n return $this->attributes['shares_count'];\n }\n\n\n /**\n * Get the count of favorites playlists this activity appears in\n */\n public function getFavoriteCountAttribute(): int\n {\n return $this->getFavoritesCountAttribute();\n }\n\n public function getFavoritesCountAttribute()\n {\n if (! isset($this->attributes['favorites_count'])) {\n $this->loadCount('favorites');\n }\n\n return $this->attributes['favorites_count'];\n }\n\n public function getActiveParticipantsCountAttribute()\n {\n if (! isset($this->attributes['active_participants_count'])) {\n $this->loadCount('activeParticipants');\n }\n\n return $this->attributes['active_participants_count'];\n }\n\n public function getTracksWithTelephonyCountAttribute()\n {\n if (! isset($this->attributes['tracks_with_telephony_count'])) {\n $this->loadCount('tracksWithTelephony');\n }\n\n return $this->attributes['tracks_with_telephony_count'];\n }\n\n /**\n * @TEMP\n * $this->loadCount('tracksWithTelephony') throws null pointer exception\n */\n public function countTracksWithTelephony(): int\n {\n return $this->tracks()->whereNotNull('telephony_provider_id')->count();\n }\n\n public function getDuration(): float\n {\n return $this->getAttribute('duration');\n }\n\n public function getDurationForHumansAttribute()\n {\n return Carbon::now()->subSeconds($this->duration)->diffForHumans(now(), true);\n }\n\n public function getDurationForHumansShortAttribute(): string\n {\n return Carbon::now()->subSeconds($this->duration)->diffForHumans(now(), true, true);\n }\n\n public function hasRecordingPreference(): bool\n {\n return $this->getAttribute('recording_preference') !== null;\n }\n\n public function getRecordingPreference()\n {\n return $this->getAttribute('recording_preference');\n }\n\n /** @return BelongsTo<User, self> */\n public function user(): BelongsTo\n {\n return $this->belongsTo(User::class)->with('group');\n }\n\n public function device()\n {\n return $this->belongsTo(Device::class);\n }\n\n public function category()\n {\n return $this->belongsTo(PlaybookCategory::class, 'playbook_category_id');\n }\n\n public function getCategory(): ?PlaybookCategory\n {\n return $this->getAttribute('category');\n }\n\n public function getPlaybookCategoryId(): ?int\n {\n return $this->getAttribute('playbook_category_id');\n }\n\n public function hasStats(): bool\n {\n return $this->getAttribute('stats') !== null;\n }\n\n public function getStats(): ?Stats\n {\n return $this->getAttribute('stats');\n }\n\n public function stats(): HasOne\n {\n return $this->hasOne(Stats::class);\n }\n\n public function participantStats(): Eloquent\\Relations\\HasManyThrough\n {\n return $this->hasManyThrough(\n Models\\Participant\\ParticipantStats::class,\n Participant::class,\n 'activity_id',\n 'participant_id'\n );\n }\n\n public function getParticipantStats(): Eloquent\\Collection\n {\n return $this->getAttribute('participantStats');\n }\n\n public function account()\n {\n return $this->belongsTo(Account::class);\n }\n\n public function contact()\n {\n return $this->belongsTo(Contact::class)->with(['account']);\n }\n\n public function lead()\n {\n return $this->belongsTo(Lead::class)->with(['stage', 'recordType']);\n }\n\n /**\n * @return BelongsTo<Opportunity, self>\n */\n public function opportunity(): BelongsTo\n {\n /** @var BelongsTo<Opportunity, self> */\n return $this->belongsTo(Opportunity::class);\n }\n\n public function stage()\n {\n return $this->belongsTo(Stage::class);\n }\n\n /**\n * @return HasMany<Session>\n */\n public function sessions(): HasMany\n {\n return $this->hasMany(Session::class);\n }\n\n /**\n * @return HasMany|ParticipantSpeech[]|Eloquent\\Collection\n */\n public function participantSpeeches()\n {\n return $this->hasMany(ParticipantSpeech::class);\n }\n\n public function getParticipantSpeeches(): Eloquent\\Collection\n {\n return $this->getAttribute('participantSpeeches');\n }\n\n /**\n * @return HasMany|Log[]|Eloquent\\Collection\n */\n public function logs()\n {\n return $this->hasMany(Log::class);\n }\n\n /**\n * @return HasMany|Moment[]|Eloquent\\Collection\n */\n public function moments()\n {\n return $this->hasMany(Moment::class);\n }\n\n /**\n * @return HasMany|Note[]|Eloquent\\Collection\n */\n public function notes()\n {\n return $this->hasMany(Note::class);\n }\n\n /**\n * @return Eloquent\\Collection|Note[]\n */\n public function getNotes(): Eloquent\\Collection\n {\n return $this->getAttribute('notes');\n }\n\n /**\n * @return HasMany|Message[]|Eloquent\\Collection\n */\n public function messages()\n {\n return $this->hasMany(Message::class);\n }\n\n public function coachingMessages(): HasMany\n {\n return $this->hasMany(Message::class)\n ->where('is_private', 1);\n }\n\n public function getCoachingMessages(): Eloquent\\Collection\n {\n return $this->getAttribute('coachingMessages');\n }\n\n public function participants(): HasMany\n {\n return $this->hasMany(Participant::class);\n }\n\n public function getSnapshots(): Eloquent\\Collection\n {\n return $this->getAttribute('snapshots');\n }\n\n /** @return HasMany<Track> */\n public function tracks(): HasMany\n {\n return $this->hasMany(Track::class);\n }\n\n public function tracksWithTelephony(): HasMany\n {\n return $this->hasMany(Track::class)->whereNotNull('telephony_provider_id');\n }\n\n public function getTracksWithTelephony(): Eloquent\\Collection\n {\n return $this->getAttribute('tracksWithTelephony');\n }\n\n /** @return Collection|Track[] */\n public function getTracks(): Eloquent\\Collection\n {\n return $this->getAttribute('tracks');\n }\n\n public function masterTrack(): HasOne\n {\n return $this->hasOne(Track::class)->where('is_master', 1)\n ->whereIn('format', [Track::FORMAT_WAV, Track::FORMAT_M3U8])\n ->latest();\n }\n\n public function getMasterTrack(): ?Track\n {\n /** @var Track|null */\n return $this->getAttribute('masterTrack');\n }\n\n public function transcription(): Eloquent\\Relations\\BelongsTo\n {\n return $this->belongsTo(Transcription::class, 'transcription_id');\n }\n\n public function findTranscriptionPromptSummaries(): Collection\n {\n $transcriptionId = $this->getTranscriptionId();\n if (is_null($transcriptionId)) {\n return new Collection();\n }\n\n return Models\\AiPrompt::query()\n ->where('transcription_id', $transcriptionId)\n ->get();\n }\n\n public function getTranscription(): Transcription\n {\n return $this->getAttribute('transcription');\n }\n\n public function hasTranscription(): bool\n {\n return $this->getAttribute('transcription') !== null;\n }\n\n public function setTranscriptionId(int $transcriptionId): Activity\n {\n $this->setAttribute('transcription_id', $transcriptionId);\n\n return $this;\n }\n\n public function unsetTranscriptionId(): self\n {\n $this->setAttribute('transcription_id', null);\n\n return $this;\n }\n\n public function getTranscriptionId(): ?int\n {\n return $this->getAttribute('transcription_id');\n }\n\n /** @deprecated */\n public function hasTranscriptionId(): bool\n {\n return $this->getAttribute('transcription_id') !== null;\n }\n\n public function coachRequests()\n {\n return $this->hasMany(CoachRequest::class);\n }\n\n public function availabilityNotifications()\n {\n return $this->hasMany(AvailabilityNotification::class);\n }\n\n public function processingStates()\n {\n return $this->hasMany(Models\\Activity\\ActivityProcessingState::class);\n }\n\n public function uploadSettings()\n {\n return $this->hasMany(ActivityUploadSetting::class);\n }\n\n public function comments()\n {\n return $this->hasMany(Comment::class);\n }\n\n public function getComments(): Eloquent\\Collection\n {\n return $this->getAttribute('comments');\n }\n\n public function visibleComments()\n {\n $rel = $this->hasMany(Comment::class);\n // Doesn't have auth()->user() in some tests, breaks the build\n if ($user = auth()->user()) {\n return $rel->visibleThreads($user->id);\n }\n\n return $rel;\n }\n\n public function snapshots(): HasMany\n {\n return $this->hasMany(Snapshot::class);\n }\n\n public function calendarEvent()\n {\n return $this->belongsTo(CalendarEvent::class);\n }\n\n public function getCalendarEvent(): ?CalendarEvent\n {\n return $this->getAttribute('calendarEvent');\n }\n\n public function latestCoachingFeedbacks(): HasMany\n {\n return $this->hasMany(CoachingFeedback::class)->latest();\n }\n\n public function playlists(): BelongsToMany\n {\n return $this->belongsToMany(Playlist::class, 'playlist_activities')\n ->withPivot('id', 'uuid', 'user_id', 'start_time', 'end_time')\n ->using(PlaylistActivity::class)\n ->withTimestamps();\n }\n\n public function coachingFeedbacks(): HasMany\n {\n return $this->hasMany(CoachingFeedback::class);\n }\n\n /**\n * @return Eloquent\\Collection|CoachingFeedback[]\n */\n public function getCoachingFeedback(?int $visibility = null): Eloquent\\Collection\n {\n $feedbacks = $this->coachingFeedbacks();\n if ($visibility !== null) {\n $feedbacks = $feedbacks->where('visibility', $visibility);\n }\n\n return $feedbacks->get();\n }\n\n /** @return Eloquent\\Collection<int, PlaylistActivity> */\n public function favoritedBy(User $user): Eloquent\\Collection\n {\n return $this->favorites()->where('user_id', $user->getId())->get();\n }\n\n /**\n * Checks whether consumer has added this activity to their favorites playlist\n * In addition a default playlist gets created if not already present\n */\n public function wasFavoritedBy(User $user): bool\n {\n $playlist = $user->favoritePlaylist();\n\n return $playlist\n ->activities()\n ->where('activity_id', '=', $this->getId())\n ->exists();\n }\n\n /**\n * @return HasMany<PlaylistActivity>\n */\n public function playlistActivities(): HasMany\n {\n return $this->hasMany(PlaylistActivity::class);\n }\n\n /**\n * @return HasManyThrough<Playlist>\n */\n public function favoritePlaylists(): HasManyThrough\n {\n return $this->hasManyThrough(\n Playlist::class,\n PlaylistActivity::class,\n 'activity_id',\n 'id',\n 'id',\n 'playlist_id'\n )->where('is_default', 1);\n }\n\n /**\n * @return Eloquent\\Collection<int, Playlist>\n */\n public function getFavoritePlaylists(): Eloquent\\Collection\n {\n return $this->getAttribute('favoritePlaylists');\n }\n\n /**\n * Get activities from the default/favorite playlist\n *\n * @return Eloquent\\Builder|static\n */\n public function favorites()\n {\n return $this->playlistActivities()->whereHas('playlist', function ($query) {\n $query->where('is_default', 1);\n });\n }\n\n /**\n * @return Model|SubscriptionSet|null|object\n */\n public function subscribedBy(User $user)\n {\n if ($this->prospect === null) {\n return null;\n }\n\n return SubscriptionSet::select('activity_subscription_sets.*')\n ->where('user_id', $user->id)\n ->join('activity_subscriptions', function ($join) {\n $join\n ->on('subscription_set_id', '=', 'activity_subscription_sets.id');\n\n if ($this->account_id) {\n if ($this->opportunity_id) {\n $join\n ->where('followable_type', 'opportunity')\n ->where('followable_id', $this->opportunity_id);\n } else {\n $join\n ->where('followable_type', 'account')\n ->where('followable_id', $this->account_id);\n }\n } elseif ($this->contact_id) {\n $join\n ->where('followable_type', 'contact')\n ->where('followable_id', $this->contact_id);\n } elseif ($this->lead_id) {\n $join\n ->where('followable_type', 'lead')\n ->where('followable_id', $this->lead_id);\n }\n })\n ->first();\n }\n\n /**\n * @return array|Eloquent\\Builder[]|Eloquent\\Collection|SubscriptionSet[]\n */\n public function subscribers()\n {\n if ($this->prospect === null) {\n return [];\n }\n\n return SubscriptionSet::with(['subscriptions', 'user'])\n ->whereHas('subscriptions', function ($query) {\n if ($this->account_id) {\n if ($this->opportunity_id) {\n $query\n ->where('followable_type', 'opportunity')\n ->where('followable_id', $this->opportunity_id);\n } else {\n $query\n ->where('followable_type', 'account')\n ->where('followable_id', $this->account_id);\n }\n } elseif ($this->contact_id) {\n $query\n ->where('followable_type', 'contact')\n ->where('followable_id', $this->contact_id);\n } elseif ($this->lead_id) {\n $query\n ->where('followable_type', 'lead')\n ->where('followable_id', $this->lead_id);\n } else {\n // Nothing to join on?\n // refactor - use Jiminny specific exception\n throw new InvalidArgumentException('Cannot join on a specific customer filter.');\n }\n })\n ->whereHas('user', function ($query) {\n $query\n ->where('team_id', $this->user->team_id)\n ->where('status', User::STATUS_ACTIVE);\n })\n ->get();\n }\n\n /**\n * @return HasMany|Builder|Eloquent\\Collection|Play[]\n */\n public function plays()\n {\n return $this->hasMany(Play::class);\n }\n\n public function getPlays(): Eloquent\\Collection\n {\n return $this->getAttribute('plays');\n }\n\n public function playsBy(User $user)\n {\n /** @var Builder $builder */\n $builder = $this->plays()->where('user_id', $user->id);\n\n return $builder->get();\n }\n\n /**\n * Check if activity was played by a user\n */\n public function wasPlayedBy(User $user): bool\n {\n return $this->plays()->where('user_id', $user->id)->exists();\n }\n\n public function shares()\n {\n return $this->hasMany(Share::class);\n }\n\n /** @return BelongsTo<Participant, self> */\n public function from(): BelongsTo\n {\n return $this->belongsTo(Participant::class, 'from_participant_id');\n }\n\n /** @return BelongsTo<Participant, self> */\n public function to(): BelongsTo\n {\n return $this->belongsTo(Participant::class, 'to_participant_id');\n }\n\n /**\n * Get all of the connections through the participants.\n */\n public function connections()\n {\n return $this->hasManyThrough(\n Connection::class,\n Participant::class,\n 'activity_id',\n 'participant_id'\n );\n }\n\n public function getConnections(): Eloquent\\Collection\n {\n return $this->getAttribute('connections');\n }\n\n /**\n * Get all of the shares through the participants.\n */\n public function participantShares()\n {\n return $this->hasManyThrough(\n Participant\\Share::class,\n Participant::class,\n 'activity_id',\n 'participant_id'\n );\n }\n\n public function getParticipantShares(): Eloquent\\Collection\n {\n return $this->getAttribute('participantShares');\n }\n\n public function topicTriggers(): HasMany\n {\n return $this->hasMany(TopicTrigger::class);\n }\n\n public function activityScorecardRuleTriggers(): HasMany\n {\n return $this->hasMany(Models\\Scorecard\\ActivityScorecardRuleTrigger::class);\n }\n\n public function activityScorecardRules(): HasMany\n {\n return $this->hasMany(Models\\Scorecard\\ActivityScorecardRule::class);\n }\n\n public function questions(): HasMany\n {\n return $this->hasMany(Question::class);\n }\n\n /**\n * Get all the custom data attached to it.\n */\n public function data(): HasMany\n {\n return $this->hasMany(FieldData::class);\n }\n\n public function getData(): Eloquent\\Collection\n {\n /** @var Eloquent\\Collection */\n return $this->getAttribute('data');\n }\n\n #[Scope]\n protected function heldBetween($query, Carbon $start, Carbon $end)\n {\n // Sanity check.\n $from = min($start, $end);\n $until = max($start, $end);\n\n return $query\n ->where('actual_start_date', '>=', $from)\n ->where('actual_end_date', '<=', $until);\n }\n\n #[Scope]\n protected function scheduledBetween($query, Carbon $start, Carbon $end)\n {\n // Sanity check.\n $from = min($start, $end);\n $until = max($start, $end);\n\n return $query\n ->where('scheduled_start_date', '>=', $from)\n ->where('scheduled_end_date', '<=', $until);\n }\n\n /**\n * @param Builder<self> $query\n *\n * @return Builder<self>\n */\n #[Scope]\n protected function forTeam(Builder $query, int $teamId): Builder\n {\n /** @var Builder<self> */\n return $query->whereHas('user', static function (Builder $query) use ($teamId): void {\n $query->where('team_id', $teamId);\n });\n }\n\n /**\n * @param Builder<self> $query\n *\n * @return Builder<self>\n */\n #[Scope]\n protected function inOpenDeals(Builder $query): Builder\n {\n /** @var Builder<self> */\n return $query->whereHas(\n 'opportunity',\n static fn (Builder $query): Builder => $query\n ->where('is_closed', false)\n ->where('deleted_at', '=', null),\n );\n }\n\n /**\n * @param Builder<self> $query\n *\n * @return Builder<self>\n */\n #[Scope]\n protected function notInOpenDeals(Builder $query): Builder\n {\n /** @var Builder<self> */\n return $query->where(\n static fn (Builder $query): Builder => $query->whereNull('opportunity_id')\n ->orWhereHas(\n 'opportunity',\n static fn (Builder $query): Builder => $query->where('is_closed', true),\n )\n ->orWhereHas(\n 'opportunity',\n static fn (Builder $query): Builder => $query->withTrashed()->where('deleted_at', '!=', null),\n ),\n );\n }\n\n /**\n * Finds a participant and updates it with data. If participant doesn't exist creates a new participant from data.\n *\n * @param array $data participant data used to identify the participant and update it\n * @param bool $enterRoom true if participant is entering the room. false if we just want to update some participant data\n * @param Carbon|null $enterTime if $enterNow is true then this is the join time when the actual enter has occurred\n */\n public function updateOrCreateParticipant(\n array $data,\n bool $enterRoom = true,\n ?Carbon $enterTime = null,\n bool $nameMatching = false,\n ): Participant {\n $search = [];\n $participant = null;\n\n if (isset($data['user_id'])) {\n // Check if they already exist based on their ID.\n $search['user_id'] = $data['user_id'];\n } elseif (isset($data['provider_id'])) {\n $search['provider_id'] = $data['provider_id'];\n } elseif ($nameMatching && isset($data['name'])) {\n $search['name'] = $data['name'];\n }\n\n if (! empty($data['email'])) {\n $search['email'] = $data['email'];\n\n // If we have their email, this should be unique enough to lookup (e.g. calendar event based participant).\n unset($search['provider_id']);\n }\n\n // Search by phone number only in case nothing else is available to search by.\n if (array_key_exists('phone_number', $data) && empty($search)) {\n $search['phone_number'] = $data['phone_number'];\n }\n\n if (! empty($search)) {\n // Do a lookup now to see if we have a match on the provided data.\n $lookup = array_map(static function ($key, $value): array {\n return [$key, $value];\n }, array_keys($search), $search);\n\n $participant = $this->participants()->withTrashed()->where($lookup)->first();\n }\n\n // Do a partial match on the name and search in the team members.\n if (! $participant instanceof Participant && $nameMatching && ! empty($data['name'])) {\n $participantMatcher = app(MeetingBot\\Service\\ParticipantMatcher::class);\n\n if (! $participantMatcher instanceof MeetingBot\\Service\\ParticipantMatcher) {\n throw new LogicException('Expecting ParticipantMatcher service instance');\n }\n\n $participant = $participantMatcher->match($this, $data['name']);\n\n // If we've found good participant, avoid data overwrite in `$participant->fill($data)` below.\n if ($participant instanceof Models\\Participant && $participant->hasName()) {\n unset($data['name']); // Thoughts: should we unset also $data['user_id'] and $data['email'] ?\n }\n }\n\n if (! $participant instanceof Participant) {\n // If no match, create a new participant.\n if (empty($search)) {\n $participant = $this->participants()->create();\n } else {\n // If no match, create a new participant but avoid creating duplicates\n $participant = $this->participants()->withTrashed()->firstOrNew($search);\n }\n }\n\n // If we have just recycled a deleted participant\n if ($participant->trashed()) {\n $participant->deleted_at = null;\n }\n\n // Deal with the case when calendar syncs the event while it's in progress.\n // We should prevent change of the participant name, because speeches mapping will fail.\n if ($enterRoom === false\n && $this->isInProgress()\n && $participant->hasName()\n && isset($data['name'])\n && $data['name'] !== $participant->getName()\n ) {\n unset($data['name']);\n }\n\n // Upsert with new data.\n $participant->fill($data);\n\n if ($enterRoom) {\n if ($enterTime === null) {\n $enterTime = now();\n }\n\n // Participant enters room for the first time\n if ($participant->enter_time === null) {\n $participant->enter_time = $enterTime;\n }\n\n // If there is an exit time and it's prior to new enter_time\n if ($participant->exit_time && $participant->exit_time->lt($enterTime)) {\n // Participant has re-joined\n $participant->exit_time = null;\n }\n }\n\n $participant->save();\n\n return $participant;\n }\n\n /**\n * Updates participant CRM data\n *\n * @param array{\n * Lead|null,\n * Account|null,\n * Opportunity|null,\n * Contact|null,\n * Stage|null,\n * string|null\n *} $records\n * @param Participant $participant participant the CRM data is associated with\n */\n public function updateParticipantCrmData(array $records, Participant $participant): void\n {\n // Extract the records.\n [$lead, , , $contact] = $records;\n\n $resolver = $this->getUpdateCrmDataResolver();\n $strategy = $resolver->resolveForParticipant($lead, $contact);\n\n if ($strategy == UpdateCrmDataByStrategy::Lead) {\n if (! $participant->hasName()) {\n $participant->name = $lead->name;\n }\n\n if (! $participant->hasEmailAddress()) {\n $participant->email = $lead->email;\n }\n\n if (! $participant->hasPhoneNumber()) {\n $participant->phone_number = $lead->phone;\n }\n\n $participant->lead_id = $lead->id;\n $participant->save();\n } elseif ($strategy == UpdateCrmDataByStrategy::Contact) {\n if (! $participant->hasName()) {\n $participant->name = $contact->name;\n }\n\n if (! $participant->hasEmailAddress()) {\n $participant->email = $contact->email;\n }\n\n if (! $participant->hasPhoneNumber()) {\n $participant->phone_number = $contact->phone;\n }\n\n $participant->contact_id = $contact->id;\n $participant->save();\n }\n }\n\n /**\n * Updates activity CRM data\n *\n * @param array{\n * Lead|null,\n * Account|null,\n * Opportunity|null,\n * Contact|null,\n * Stage|null,\n * string|null\n *} $records\n */\n public function updateActivityCrmData(array $records): void\n {\n // Extract the records.\n [$lead, $account, $opportunity, $contact, $stage] = $records;\n\n $resolver = $this->getUpdateCrmDataResolver();\n $strategy = $resolver->resolveForActivity($lead, $contact, $account);\n\n if ($strategy == UpdateCrmDataByStrategy::Lead) {\n // Also update the parent activity if required, checking we don't create a mixed lead/account record.\n if ($this->account_id === null && $this->contact_id === null && $this->lead_id === null) {\n $this->lead_id = $lead->id;\n\n if ($this->stage_id === null && $stage) {\n $this->stage_id = $stage->id;\n }\n\n $this->save();\n }\n } elseif ($strategy == UpdateCrmDataByStrategy::Contact) {\n // Also update the parent activity if required, checking we don't create a mixed lead/account record.\n $this->lead_id = null;\n if ($this->stage && $this->stage->getType() === Stage::TYPE_LEAD) {\n $this->stage_id = null;\n }\n\n // Don't trust previous matched account_id as it might have been changed in the CRM\n if ($account && $account->id !== $this->account_id) {\n $this->account_id = $account->id;\n }\n\n if ($opportunity && $this->opportunity_id !== $opportunity->id) {\n $this->opportunity_id = $opportunity->id;\n // Reset stage_id when opportunity changes to avoid stale stage from previous opportunity match\n $this->stage_id = $stage?->id;\n }\n\n if ($this->stage_id === null && $stage) {\n $this->stage_id = $stage->id;\n }\n\n if ($opportunity && $this->value !== $opportunity->value) {\n $this->value = $opportunity->value;\n }\n\n // Always set contact_id when available, regardless of account_id status\n if ($this->contact_id === null && $contact) {\n $this->contact_id = $contact->id;\n }\n\n $this->save();\n } elseif ($strategy == UpdateCrmDataByStrategy::Account && $this->account_id === null) {\n // Also update the parent activity if required, checking we don't create a mixed lead/account record.\n $this->lead_id = null;\n if ($this->stage && $this->stage->getType() === Stage::TYPE_LEAD) {\n $this->stage_id = null;\n }\n\n // Update the account and opportunity on the activity record if possible.\n $this->account_id = $account->id;\n\n if ($this->stage_id === null && $stage) {\n $this->stage_id = $stage->id;\n }\n\n if ($this->opportunity_id === null && $opportunity) {\n $this->opportunity_id = $opportunity->id;\n $this->value = $opportunity->value;\n }\n\n $this->save();\n }\n }\n\n public function getActivityProspectData(): array\n {\n return [\n 'lead' => $this->lead_id,\n 'contact' => $this->contact_id,\n 'account' => $this->account_id,\n 'opportunity' => $this->opportunity_id,\n 'stage' => $this->stage_id,\n ];\n }\n\n public function isOrganizer(User $user): bool\n {\n return $this->user_id && $this->user_id === $user->id;\n }\n\n public function isJoinable(): bool\n {\n return \\in_array($this->status, [\n self::STATUS_SCHEDULED,\n self::STATUS_PENDING,\n self::STATUS_RINGING,\n self::STATUS_IN_PROGRESS,\n ], true);\n }\n\n public function isAttemptedForBotJoin(): bool\n {\n return in_array($this->getAttribute('status'), self::MEETING_BOT_JOIN_ATTEMPTED, true);\n }\n\n /**\n * Check if the activity can be saved to CRM (manual or autolog)\n */\n public function isLoggable(): bool\n {\n if ($this->getUser()->getTeam()->hasFeature(FeatureEnum::SIDEKICK_SETTINGS)) {\n $sidekickService = app(SidekickService::class);\n\n if (! $sidekickService->isSidekickEnabledForUser($this->getUser())) {\n return false;\n }\n }\n\n // If we don't know the activity type, don't try to log.\n if ($this->playbook_category_id === null) {\n return false;\n }\n\n if ($this->user->crm_required === false) {\n return false;\n }\n\n // Don't prompt for internal meetings.\n if ($this->is_internal) {\n return false;\n }\n\n // If we don't know who we are trying to log to, don't try to log.\n if ($this->prospect === null) {\n return false;\n }\n\n $validStatus = false;\n switch ($this->type) {\n case self::TYPE_SOFTPHONE:\n case self::TYPE_SOFTPHONE_INBOUND:\n $validStatus = true;\n\n break;\n case self::TYPE_CONFERENCE:\n $validStatus = in_array($this->status, [\n self::STATUS_BUSY,\n self::STATUS_NO_ANSWER,\n self::STATUS_COMPLETED,\n self::STATUS_CANCELLED,\n ], true);\n\n break;\n case self::TYPE_SMS_INBOUND:\n case self::TYPE_SMS_OUTBOUND:\n $validStatus = in_array($this->status, [\n self::STATUS_QUEUED,\n self::STATUS_SENT,\n self::STATUS_UNDELIVERED,\n self::STATUS_DELIVERED,\n self::STATUS_RECEIVED,\n ], true);\n\n break;\n }\n\n // Depending on the activity channel, we should not try to log.\n return $validStatus;\n }\n\n public function isScheduled(): bool\n {\n return $this->status === self::STATUS_SCHEDULED;\n }\n\n public function scheduledDuration(): int\n {\n if ($this->scheduled_start_time && $this->scheduled_end_time) {\n return $this->scheduled_end_time->timestamp - $this->scheduled_start_time->timestamp;\n }\n\n return 0;\n }\n\n public function isPending(): bool\n {\n return $this->status === self::STATUS_PENDING;\n }\n\n public function isCompleted(): bool\n {\n return $this->status === self::STATUS_COMPLETED;\n }\n\n public function isRinging(): bool\n {\n return $this->status === self::STATUS_RINGING;\n }\n\n public function isInProgress(): bool\n {\n return $this->status === self::STATUS_IN_PROGRESS;\n }\n\n public function isBusy(): bool\n {\n return $this->status === self::STATUS_BUSY;\n }\n\n public function isNoAnswer(): bool\n {\n return $this->status === self::STATUS_NO_ANSWER;\n }\n\n public function isFailed(): bool\n {\n return $this->status === self::STATUS_FAILED;\n }\n\n public function isCancelled(): bool\n {\n return $this->status === self::STATUS_CANCELLED;\n }\n\n public function hasEnded(int $gracePeriodMinutes = 15): bool\n {\n if ($this->isCompleted()) {\n return true;\n }\n\n if (($this->isFailed() || $this->isCancelled()) && $this->hasScheduledEndTime()) {\n return $this->getScheduledEndTime()->addMinutes($gracePeriodMinutes)->isPast();\n }\n\n return false;\n }\n\n public function hasStarted(): bool\n {\n return $this->hasActualStartTime();\n }\n\n public function isOngoing(): bool\n {\n return $this->hasActualStartTime() && ! $this->hasActualEndTime();\n }\n\n public function isTypeSmsInbound(): bool\n {\n return $this->getType() === self::TYPE_SMS_INBOUND;\n }\n\n public function isTypeSmsOutbound(): bool\n {\n return $this->getType() === self::TYPE_SMS_OUTBOUND;\n }\n\n public function isTypeSoftPhone(): bool\n {\n return $this->getType() === self::TYPE_SOFTPHONE;\n }\n\n public function isTypeSoftphoneInbound(): bool\n {\n return $this->getType() === self::TYPE_SOFTPHONE_INBOUND;\n }\n\n public function isTypeConference(): bool\n {\n return $this->getType() === self::TYPE_CONFERENCE;\n }\n\n /**\n * Get a conference elapsed time in seconds.\n *\n * @return int seconds count\n */\n public function secondsTimeElapsed(): int\n {\n if (empty($this->actual_start_time)) {\n return 0;\n }\n\n // Get number of seconds since conference actual start time\n return (int) abs(Carbon::now()->diffInRealSeconds($this->actual_start_time));\n }\n\n /**\n * Get a conference elapsed time formatted as \"1:30:20\" if more than 1 hour or \"30:20\" otherwise.\n */\n public function formattedTimeElapsed(): string\n {\n // Get number of seconds since conference actual start time.\n $elapsedSeconds = $this->secondsTimeElapsed();\n $elapsedTime = Carbon::createFromTimestampUTC($elapsedSeconds);\n\n // Format conference start time.\n return $elapsedTime->format($elapsedSeconds < 3600 ? 'i:s' : 'G:i:s');\n }\n\n public function wasScheduled(): bool\n {\n return $this->calendarEvent !== null || in_array($this->getSource(), [self::SOURCE_OUTLOOK, self::SOURCE_GOOGLE]);\n }\n\n public function isInstant(): bool\n {\n return ! $this->wasScheduled();\n }\n\n /**\n * GETTERS AND SETTERS FOLLOW BELOW\n */\n\n public function getUuid(): string\n {\n return $this->getAttribute('id_string');\n }\n\n public function getId(): int\n {\n return $this->getAttribute('id');\n }\n\n public function getFromParticipantId(): ?int\n {\n return $this->getAttribute('from_participant_id');\n }\n\n public function getFromParticipant(): ?Participant\n {\n return $this->getAttribute('from');\n }\n\n public function getToParticipantId(): ?int\n {\n return $this->getAttribute('to_participant_id');\n }\n\n public function getToParticipant(): ?Participant\n {\n return $this->getAttribute('to');\n }\n\n public function hasScheduledStartTime(): bool\n {\n return $this->getAttribute('scheduled_start_time') !== null;\n }\n\n public function getScheduledStartTime(): ?Carbon\n {\n return $this->getAttribute('scheduled_start_time');\n }\n\n public function setScheduledStartTime(DateTimeInterface $dateTime): self\n {\n $this->setAttribute('scheduled_start_time', $dateTime);\n\n return $this;\n }\n\n public function getScheduledEndTime(): ?DateTimeInterface\n {\n return $this->getAttribute('scheduled_end_time');\n }\n\n public function hasScheduledEndTime(): bool\n {\n return $this->getAttribute('scheduled_end_time') !== null;\n }\n\n public function setScheduledEndTime(DateTimeInterface $dateTime): self\n {\n $this->setAttribute('scheduled_end_time', $dateTime);\n\n return $this;\n }\n\n public function getActualStartTime(): ?Carbon\n {\n return $this->getAttribute('actual_start_time');\n }\n\n public function hasActualStartTime(): bool\n {\n return $this->getAttribute('actual_start_time') !== null;\n }\n\n public function getActualEndTime(): ?Carbon\n {\n return $this->getAttribute('actual_end_time');\n }\n\n public function hasActualEndTime(): bool\n {\n return $this->getAttribute('actual_end_time') !== null;\n }\n\n public function getType(): ?string\n {\n return $this->getAttribute('type');\n }\n\n public function getStatus(): string\n {\n return $this->getAttribute('status');\n }\n\n public function setStatus(string $status): self\n {\n $this->setAttribute('status', $status);\n\n return $this;\n }\n\n public function setActualStartTime(DateTimeInterface $dateTime): self\n {\n $this->setAttribute('actual_start_time', $dateTime);\n\n return $this;\n }\n\n public function setActualEndTime(DateTimeInterface $dateTime, bool $shouldUpdateDuration = true): self\n {\n $this->setAttribute('actual_end_time', $dateTime);\n\n if (! $shouldUpdateDuration) {\n return $this;\n }\n\n return $this->updateDuration();\n }\n\n public function updateDuration(): self\n {\n if (! $this->hasActualStartTime() || ! $this->hasActualEndTime()) {\n return $this;\n }\n\n return $this->setDuration(\n (int) abs($this->getActualStartTime()->diffInRealSeconds($this->getActualEndTime()))\n );\n }\n\n public function setDuration(int $duration): self\n {\n $this->setAttribute('duration', $duration);\n\n return $this;\n }\n\n public function getRecordingState(): string\n {\n return $this->getAttribute('recording_state');\n }\n\n public function isRecordingState(string $recordingState): bool\n {\n return $this->getRecordingState() === $recordingState;\n }\n\n public function setRecordingState(string $recordingState): self\n {\n $this->setAttribute('recording_state', $recordingState);\n\n return $this;\n }\n\n public function hasActivityType(): bool\n {\n return $this->getAttribute('category') !== null;\n }\n\n public function getActivityType(): ?PlaybookCategory\n {\n return $this->getAttribute('category');\n }\n\n public function setActivityType(int $playbookCategoryId): self\n {\n $this->setAttribute('playbook_category_id', $playbookCategoryId);\n\n return $this;\n }\n\n public function hasStage(): bool\n {\n return $this->getAttribute('stage') !== null;\n }\n\n public function getStage(): ?Stage\n {\n return $this->getAttribute('stage');\n }\n\n public function getStageId(): ?int\n {\n return $this->getAttribute('stage_id');\n }\n\n public function setStageId(?int $stageId): void\n {\n $this->setAttribute('stage_id', $stageId);\n }\n\n public function hasOpportunity(): bool\n {\n return $this->getAttribute('opportunity') !== null;\n }\n\n public function getOpportunity(): ?Opportunity\n {\n return $this->getAttribute('opportunity');\n }\n\n public function getOpportunityId(): ?int\n {\n return $this->getAttribute('opportunity_id');\n }\n\n public function setOpportunityId(?int $opportunityId): void\n {\n $this->setAttribute('opportunity_id', $opportunityId);\n }\n\n public function hasContact(): bool\n {\n return $this->getAttribute('contact') !== null;\n }\n\n public function getContact(): ?Contact\n {\n return $this->getAttribute('contact');\n }\n\n public function getContactId(): ?int\n {\n return $this->getAttribute('contact_id');\n }\n\n public function setContactId(?int $contactId): void\n {\n $this->setAttribute('contact_id', $contactId);\n }\n\n public function hasLead(): bool\n {\n return $this->getAttribute('lead') !== null;\n }\n\n public function getLead(): ?Lead\n {\n return $this->getAttribute('lead');\n }\n\n public function getLeadId(): ?int\n {\n return $this->getAttribute('lead_id');\n }\n\n public function setLeadId(?int $leadId): void\n {\n $this->setAttribute('lead_id', $leadId);\n }\n\n public function hasAccount(): bool\n {\n return $this->getAttribute('account') !== null;\n }\n\n public function getAccount(): ?Account\n {\n return $this->getAttribute('account');\n }\n\n public function getAccountId(): ?int\n {\n return $this->getAttribute('account_id');\n }\n\n public function setAccountId(?int $accountId): void\n {\n $this->setAttribute('account_id', $accountId);\n }\n\n /**\n * This method exists to avoid confusion using ->participants() or ->participants. Use the getter instead.\n *\n * @return Collection<int, Participant>|Participant[]\n */\n public function getParticipants(): Collection\n {\n return $this->participants;\n }\n\n /**\n * @deprecated use ParticipantRepository::findParticipantRoomOwner() instead\n */\n public function findParticipantRoomOwner(): ?Participant\n {\n $roomOwnerId = $this->getUserId();\n\n return $this->getParticipants()\n ->filter(static fn (Participant $participant): bool => $participant->isSameUserId($roomOwnerId))\n ->first();\n }\n\n public function hasCrmProviderId(): bool\n {\n return $this->getAttribute('crm_provider_id') !== null;\n }\n\n public function getCrmProviderId(): ?string\n {\n return $this->getAttribute('crm_provider_id');\n }\n\n public function setCrmProviderId(?string $crmProviderId): void\n {\n $this->setAttribute('crm_provider_id', $crmProviderId);\n }\n\n public function getUserId(): ?int\n {\n return $this->getAttribute('user_id');\n }\n\n public function hasUser(): bool\n {\n return $this->user()->exists();\n }\n\n public function getUser(): User\n {\n return $this->getAttribute('user');\n }\n\n public function getCreatedAt(): Carbon\n {\n return $this->getAttribute('created_at');\n }\n\n public function isInFiniteState(): bool\n {\n return $this->isFiniteState($this->getStatus());\n }\n\n public function isFiniteState(string $status): bool\n {\n $finiteStates = self::FINITE_STATES[$this->getType()] ?? [];\n\n return in_array($status, $finiteStates, true);\n }\n\n public function getParticipant(Authenticatable $user): Participant\n {\n return $this->findParticipant($user);\n }\n\n public function findParticipant(Authenticatable $user): ?Participant\n {\n if ($user instanceof User) {\n /** @var User $user */\n return $this->participants()->where('user_id', '=', $user->getId())->first();\n }\n\n throw new LogicException(sprintf('Unsupported Authenticatable implementation %s', get_class($user)));\n }\n\n public function hasLanguageCode(): bool\n {\n return $this->getAttribute('language') !== null;\n }\n\n public function getLanguageCode(): ?string\n {\n /** @var string|null */\n return $this->getAttribute('language');\n }\n\n public function getLanguageCodeHyphenated(): string\n {\n return str_replace('_', '-', $this->getLanguageCode() ?? '');\n }\n\n public function getLanguageCodeLocale(): string\n {\n [ $language ] = explode('_', $this->getLanguageCode() ?? '');\n\n return $language;\n }\n\n public function setLanguageCode(string $value): self\n {\n return $this->setAttribute('language', $value);\n }\n\n public function hasSource(): bool\n {\n return $this->getAttribute('source') !== null;\n }\n\n public function setSource(?string $source): self\n {\n return $this->setAttribute('source', $source);\n }\n\n public function isSource(string $source): bool\n {\n return $this->getAttribute('source') === $source;\n }\n\n public function getSource(): ?string\n {\n return $this->getAttribute('source');\n }\n\n public function isSourceGong(): bool\n {\n return $this->isSource(self::SOURCE_GONG);\n }\n\n public function getExternalId(): ?string\n {\n return $this->getAttribute('external_id');\n }\n\n public function setExternalId(?string $externalId): self\n {\n return $this->setAttribute('external_id', $externalId);\n }\n\n public function hasExternalId(): bool\n {\n return $this->getAttribute('external_id') !== null;\n }\n\n public function getProvider(): string\n {\n return $this->getAttribute('provider');\n }\n\n public function hasTelephonyProviderId(): bool\n {\n return $this->getAttribute('telephony_provider_id') !== null;\n }\n\n public function getTelephonyProviderId(): ?string\n {\n return $this->getAttribute('telephony_provider_id');\n }\n\n public function setTelephonyProviderId(?string $telephonyProviderId): self\n {\n return $this->setAttribute('telephony_provider_id', $telephonyProviderId);\n }\n\n public function getLocation(): ?string\n {\n return $this->getAttribute('location');\n }\n\n public function setLocation(?string $location): self\n {\n return $this->setAttribute('location', $location);\n }\n\n public function isDeleted(): bool\n {\n return $this->getAttribute('deleted_at') !== null;\n }\n\n /**\n * Check if activity recording is on and activity status is not one of the failed statuses.\n */\n public function canReviewActivity(): bool\n {\n $failedStatuses = self::$enumFailedStatuses;\n\n return (! in_array($this->recording_state, [self::RECORDING_OFF, self::RECORDING_STOPPED], true) &&\n ! in_array($this->status, $failedStatuses, true));\n }\n\n public function hasReasonCodeBotKicked(): bool\n {\n return $this->getFlag('recording_reason_code', self::FLAG_RECORDING_REASON_MEETING_BOT_KICKED);\n }\n\n public function hasReasonCodeNotCompliant(): bool\n {\n return $this->getFlag('recording_reason_code', self::FLAG_RECORDING_REASON_CONSENT_DENIED);\n }\n\n public function hasTopicTriggers(): bool\n {\n return $this->topicTriggers()->count() !== 0;\n }\n\n public function getTopicTriggers(): Collection\n {\n return $this->topicTriggers;\n }\n\n public function getTopicTriggersSorted(): Collection\n {\n $this->loadMissing([\n 'topicTriggers.participant',\n 'topicTriggers.playbackThemeTopicTrigger',\n 'topicTriggers.playbackThemeTopicTrigger',\n 'topicTriggers.playbackThemeTopicTrigger.playbackThemeTopic',\n 'topicTriggers.playbackThemeTopicTrigger.playbackThemeTopic.playbackTheme',\n ]);\n\n return $this->topicTriggers\n ->sortBy([\n 'playbackThemeTopicTrigger.playbackThemeTopic.playbackTheme.sort',\n 'playbackThemeTopicTrigger.playbackThemeTopic.sort',\n 'playbackThemeTopicTrigger.sort',\n ]);\n }\n\n public function hasQuestions(): bool\n {\n return $this->questions()->exists();\n }\n\n public function getQuestions(): Collection\n {\n return $this->questions;\n }\n\n public function hasValue(): bool\n {\n return $this->getAttribute('value') !== null;\n }\n\n public function getValue(): ?float\n {\n return $this->getAttribute('value');\n }\n\n public function setValue(?float $value): void\n {\n $this->setAttribute('value', $value);\n }\n\n public function transitionTo(string $newState, callable $callback, ?int $timeout = null): self\n {\n $newState = $this->getWorkflowStateFor(\n $this->getType(),\n $newState\n );\n\n return $this->traitTransitionTo($newState, $callback, $timeout);\n }\n\n public function getWorkflowState(): string\n {\n return $this->getWorkflowStateFor(\n $this->getType(),\n $this->getStatus()\n );\n }\n\n public function getActivityProviderDisplayName(): string\n {\n return \\Cache::remember('activity_provider_display_name-' . $this->getProvider(), 60 * 60 * 24, function () {\n $activityProviderRegistry = app()->make(ActivityProviderRegistry::class);\n\n try {\n return $activityProviderRegistry->get($this->getProvider())->getDisplayName();\n } catch (Exception $exception) {\n return ucfirst($this->getProvider());\n }\n });\n }\n\n private function getWorkflowStateFor(string $activityChannel, string $activityStatus): string\n {\n return sprintf(\n '%s::%s',\n $activityChannel,\n $activityStatus\n );\n }\n\n public function getWorkflow(): array\n {\n $map = [\n self::TYPE_SOFTPHONE => [\n self::STATUS_SCHEDULED => [\n self::STATUS_PENDING,\n self::STATUS_IN_PROGRESS,\n self::STATUS_FAILED,\n self::STATUS_BUSY,\n ],\n self::STATUS_PENDING => [\n self::STATUS_IN_PROGRESS,\n self::STATUS_FAILED,\n self::STATUS_BUSY,\n ],\n self::STATUS_RINGING => [\n self::STATUS_CANCELLED,\n self::STATUS_FAILED,\n self::STATUS_IN_PROGRESS,\n self::STATUS_BUSY,\n ],\n self::STATUS_IN_PROGRESS => [\n self::STATUS_COMPLETED,\n ],\n ],\n self::TYPE_SOFTPHONE_INBOUND => [\n self::STATUS_RINGING => [\n self::STATUS_IN_PROGRESS,\n self::STATUS_NO_ANSWER,\n self::STATUS_CANCELLED,\n self::STATUS_FAILED,\n self::STATUS_BUSY,\n ],\n self::STATUS_IN_PROGRESS => [\n self::STATUS_COMPLETED,\n ],\n ],\n ];\n\n return collect($map)\n ->mapWithKeys(function (array $currentStates, string $activityChannel): array {\n return [\n $activityChannel => collect($currentStates)\n ->mapWithKeys(function (array $possibleStates, $currentState) use ($activityChannel): array {\n $transitionName = $this->getWorkflowStateFor($activityChannel, $currentState);\n\n return [\n $transitionName => array_map(function (string $newState) use ($activityChannel) {\n return $this->getWorkflowStateFor($activityChannel, $newState);\n }, $possibleStates),\n ];\n }),\n ];\n })\n ->reduce(static function (array $carry, Collection $item): array {\n return array_merge($carry, $item->all());\n }, []);\n }\n\n public function hasPosterPath(): bool\n {\n return $this->getAttribute('poster_path') !== null;\n }\n\n public function getPosterPath(): ?string\n {\n return $this->getAttribute('poster_path');\n }\n\n /**\n * Take into account all recording settings and determine if we need to record this activity or not.\n */\n public function shouldRecord(): bool\n {\n return $this->determineRecordingReasonCode() === null;\n }\n\n public function determineRecordingReasonCode(): ?int\n {\n // Conference specific decisions.\n if ($this->isTypeConference()) {\n // If they have manually overridden the recording setting to not record.\n if ($this->hasRecordingPreference() && $this->getRecordingPreference() === false) {\n return self::FLAG_RECORDING_REASON_PREFERENCE_OVERRIDE;\n }\n\n // If they have manually overridden the recording setting to record.\n if ($this->hasRecordingPreference() && $this->getRecordingPreference() === true) {\n return null;\n }\n\n // If their team has disabled recording meetings, don't record.\n if ($this->user->team->isConferenceRecordPreferenceDisabled()) {\n return self::FLAG_RECORDING_REASON_TEAM_AUTOMATIC_DISABLED;\n }\n\n // If the host has disabled recording meetings, don't record.\n if ($this->user->checkConferenceRecordPreference() === false) {\n return self::FLAG_RECORDING_REASON_USER_AUTOMATIC_DISABLED;\n }\n\n // If it was marked internal...\n if ($this->is_internal) {\n // and their team has disabled recording internal meetings, don't record.\n if (\n $this->user->team->isConferenceRecordPreferenceEnabled()\n && ! $this->user->team->isConferenceRecordInternalPreferenceEnabled()\n ) {\n return self::FLAG_RECORDING_REASON_TEAM_INTERNAL_DISABLED;\n }\n\n // and the host has disabled recording internal meetings, don't record.\n if ($this->user->checkConferenceRecordInternalPreference() === false) {\n return self::FLAG_RECORDING_REASON_USER_INTERNAL_DISABLED;\n }\n }\n\n // If it was not scheduled and they disabled internal meetings, we cannot determine if it was internal.\n if ($this->wasScheduled() === false && $this->user->checkConferenceRecordInternalPreference() === false) {\n return self::FLAG_RECORDING_REASON_USER_INTERNAL_DISABLED_UNSCHEDULED;\n }\n }\n\n return null;\n }\n\n public function getRecordingReasonCode(): int\n {\n return $this->getAttribute('recording_reason_code');\n }\n\n public function setRecordingReasonCode(int $recordingReasonCode): self\n {\n $this->setAttribute('recording_reason_code', $recordingReasonCode);\n\n return $this;\n }\n\n // Not used today.\n public function getRecordingReasonString(): ?string\n {\n if ($this->hasRecordingReasonCompliancePrompted()) {\n return Team::COMPLIANCE_MODE_RECORDING_PROMPT;\n }\n\n if ($this->hasRecordingReasonComplianceRestricted()) {\n return Team::COMPLIANCE_MODE_RECORDING_RESTRICT;\n }\n\n if ($this->hasRecordingReasonComplianceRestrictedToOneSideRecording()) {\n return Team::COMPLIANCE_MODE_RECORDING_RESTRICT_ONE_SIDE;\n }\n\n return null;\n }\n\n public function hasRecordingReasonComplianceRestricted(): bool\n {\n return $this->getFlag('recording_reason_code', self::FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT);\n }\n\n public function hasRecordingReasonCompliancePrompted(): bool\n {\n return $this->getFlag('recording_reason_code', self::FLAG_RECORDING_REASON_COMPLIANCE_PROMPT);\n }\n\n public function hasRecordingReasonComplianceRestrictedToOneSideRecording(): bool\n {\n return $this->getFlag('recording_reason_code', self::FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT_ONE_SIDE);\n }\n\n public function getAudioTrack(): ?Track\n {\n /** @var Track|null */\n return $this->tracks()\n ->where('type', '=', Track::TYPE_AUDIO)\n ->first();\n }\n\n public function activeParticipants(): HasMany\n {\n return $this->hasMany(Participant::class)->active();\n }\n\n public function getActiveParticipants(): Eloquent\\Collection\n {\n return $this->getAttribute('activeParticipants');\n }\n\n public function crm(): Eloquent\\Relations\\BelongsTo\n {\n return $this->belongsTo(Configuration::class, 'crm_configuration_id');\n }\n\n public function activitySummaryLogs(): HasMany\n {\n return $this->hasMany(ActivitySummaryLog::class);\n }\n\n public function getCrm(): ?Configuration\n {\n return $this->getAttribute('crm');\n }\n\n public function hasCrmConfiguration(): bool\n {\n return $this->getAttribute('crm') !== null;\n }\n\n public function isProcessed(): ?bool\n {\n return $this->getAttribute('is_processed');\n }\n\n public function hasRecordingPrompt(): bool\n {\n return $this->getAttribute('has_recording_prompt') === true;\n }\n\n public function isOnAir(): bool\n {\n return $this->getAttribute('on_air') === self::ON_AIR_READY || $this->getAttribute('on_air') === self::ON_AIR_STREAMING;\n }\n\n public function setOnAir(int $onAir): self\n {\n $this->setAttribute('on_air', $onAir);\n\n return $this;\n }\n\n public function getOnAir(): ?int\n {\n return $this->getAttribute('on_air');\n }\n\n public function setTitleFromCallData(Call $call): void\n {\n $direction = $call->isOutbound() ? 'to' : 'from';\n\n $party = $this->prospect_name\n ?? $call->getContactName()\n ?? $call->getOtherPartyPhoneNumber()\n ;\n\n $this->update(['title' => sprintf('Call %s %s', $direction, $party)]);\n }\n\n /**\n * @param array{}|array{channels:string|null, format:string|null, type:string|null, status:string|null} $audioParams\n */\n public function createAudioTrack(\n string $telephonyProviderId,\n string $recordingUrl,\n array $audioParams = []\n ): Track {\n return $this->tracks()->updateOrCreate([\n 'telephony_provider_id' => $telephonyProviderId,\n ], [\n 'type' => $audioParams['type'] ?? Track::TYPE_AUDIO,\n 'status' => $audioParams['status'] ?? Track::STATUS_PENDING,\n 'format' => $audioParams['format'] ?? Track::FORMAT_WAV,\n 'provider_content_url' => $recordingUrl,\n 'start_time' => $this->actual_start_time,\n 'end_time' => $this->actual_end_time,\n ]);\n }\n\n public function createTrack(string $telephonyProviderId, array $params): Track\n {\n return $this->tracks()->updateOrCreate(\n [\n 'telephony_provider_id' => $telephonyProviderId,\n ],\n $params\n );\n }\n\n public function createOrganiserParticipant(Call $call): Participant\n {\n $user = $this->getUser();\n\n return $this->updateOrCreateParticipant([\n 'is_ghost' => 0,\n 'name' => $user->name,\n 'email' => $user->email,\n 'phone_number' => phone_e164(null, $call->getUserPhoneNumber()),\n 'enter_time' => $this->actual_start_time,\n 'exit_time' => $this->actual_end_time,\n 'user_id' => $user->id,\n ], false);\n }\n\n public function createProspectParticipant(Call $call): Participant\n {\n // not null 'name' is mandatory here to create a separate participant with 'nameMatching'\n // in case of the same phone_number with the Organiser\n $useNameMatching = $call->getUserPhoneNumber() === $call->getOtherPartyPhoneNumber();\n $defaultName = $useNameMatching ? '' : null;\n\n return $this->updateOrCreateParticipant(data: [\n 'is_ghost' => 0,\n 'name' => $this->prospect->name ?? $defaultName,\n 'email' => $this->prospect->email ?? null,\n 'phone_number' => phone_e164(null, $call->getOtherPartyPhoneNumber()),\n 'enter_time' => $this->actual_start_time,\n 'exit_time' => $this->actual_end_time,\n 'contact_id' => $this->contact_id ?? null,\n 'lead_id' => $this->lead_id ?? null,\n ], enterRoom: false, nameMatching: $useNameMatching);\n }\n\n public function updateParticipants(Participant $organiserParticipant, Participant $prospectParticipant): void\n {\n $this->update([\n 'from_participant_id' => $this->isTypeSoftPhone() ? $organiserParticipant->id : $prospectParticipant->id,\n 'to_participant_id' => $this->isTypeSoftPhone() ? $prospectParticipant->id : $organiserParticipant->id,\n ]);\n }\n\n public function hasProspect(): bool\n {\n return $this->getProspectAttribute() !== null;\n }\n\n public function isPrivate(): bool\n {\n return $this->getAttribute('is_private');\n }\n\n /** Create a new factory instance for the model. */\n protected static function newFactory(): Factory\n {\n return ActivityFactory::new();\n }\n\n public function getUpdatedAt(): Carbon\n {\n return $this->getAttribute('updated_at');\n }\n\n public function getActivitySummaryLogs(): Eloquent\\Collection\n {\n return $this->getAttribute('activitySummaryLogs');\n }\n\n public function hasProspectActivitySummaryLog(): bool\n {\n return $this->getActivitySummaryLogs()->contains(\n 'relation_type',\n ActivitySummaryLog::RELATION_OBJECT_TYPE_PROSPECT\n );\n }\n\n public function getTeam(): Team\n {\n return $this->getUser()->getTeam();\n }\n\n private function getUpdateCrmDataResolver(): UpdateCrmDataResolverInterface\n {\n $factory = app(UpdateCrmDataResolverFactory::class);\n\n return $factory->create($this);\n }\n\n public function getMeetingTrackProviderId(string $type): string\n {\n $label = match ($type) {\n Track::TYPE_VIDEO => 'v',\n Track::TYPE_AUDIO => 'a',\n default => throw new InvalidArgumentJiminnyException('Invalid track type'),\n };\n\n $startTimestamp = $this->getScheduledStartTime()?->getTimestamp();\n $teamId = $this->getTeam()->getId();\n\n return $this->getTelephonyProviderId() . ':' . $label . ':' . $startTimestamp . ':' . $teamId;\n }\n\n /**\n * Get all consent records associated with this activity\n *\n * @return \\Illuminate\\Database\\Eloquent\\Relations\\HasMany\n */\n public function participantConsents(): HasMany\n {\n return $this->hasMany(Participant\\Consent::class);\n }\n\n public function isDiallerCall(): bool\n {\n if ($this->getProvider() === Activity::PROVIDER_UPLOADER) {\n return false;\n }\n\n if (! in_array($this->getType(), [self::TYPE_SOFTPHONE, self::TYPE_SOFTPHONE_INBOUND])) {\n return false;\n }\n\n return $this->getProvider() !== self::PROVIDER_TWILIO;\n }\n\n public function getActivityDateWithFallback(): Carbon\n {\n if ($this->getActualStartTime() !== null) {\n return $this->getActualStartTime();\n }\n\n if ($this->getScheduledStartTime() !== null) {\n return $this->getScheduledStartTime();\n }\n\n return $this->getCreatedAt();\n }\n\n public function getCrmType(): ?string\n {\n // Treat uploader activities as conferences\n if ($this->getProvider() === Activity::PROVIDER_UPLOADER) {\n return Activity::TYPE_CONFERENCE;\n }\n\n return $this->getType();\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\nnamespace Jiminny\\Models;\n\nuse Carbon\\Carbon;\nuse Database\\Factories\\ActivityFactory;\nuse DateTimeInterface;\nuse Exception;\nuse Illuminate\\Contracts\\Auth\\Authenticatable;\nuse Illuminate\\Database\\Eloquent;\nuse Illuminate\\Database\\Eloquent\\Attributes\\Scope;\nuse Illuminate\\Database\\Eloquent\\Builder;\nuse Illuminate\\Database\\Eloquent\\Factories\\Factory;\nuse Illuminate\\Database\\Eloquent\\Factories\\HasFactory;\nuse Illuminate\\Database\\Eloquent\\Relations\\BelongsTo;\nuse Illuminate\\Database\\Eloquent\\Relations\\BelongsToMany;\nuse Illuminate\\Database\\Eloquent\\Relations\\HasMany;\nuse Illuminate\\Database\\Eloquent\\Relations\\HasManyThrough;\nuse Illuminate\\Database\\Eloquent\\Relations\\HasOne;\nuse Illuminate\\Database\\Eloquent\\SoftDeletes;\nuse Illuminate\\Support\\Collection;\nuse Illuminate\\Support\\Facades\\Auth;\nuse InvalidArgumentException;\nuse Jiminny\\Component\\ElasticSearch;\nuse Jiminny\\Component\\MeetingBot;\nuse Jiminny\\Component\\Model\\BitwiseFlagTrait;\nuse Jiminny\\Component\\PlaybackPage\\Comments\\Services\\ActivityCommentService;\nuse Jiminny\\Component\\Sidekick\\SidekickService;\nuse Jiminny\\Component\\Uuid\\UuidAwareInterface;\nuse Jiminny\\Component\\Workflow;\nuse Jiminny\\Contracts;\nuse Jiminny\\Contracts\\Crm\\ProspectInterface;\nuse Jiminny\\DTO\\ImportCall\\Call;\nuse Jiminny\\Events\\Activities\\ActivityTypeUpdated;\nuse Jiminny\\Events\\Activities\\ActivityUpdated;\nuse Jiminny\\Events\\Activities\\ProspectUpdated;\nuse Jiminny\\Events\\Activities\\StageUpdated;\nuse Jiminny\\Events\\Activities\\StatusUpdated;\nuse Jiminny\\Events\\Activities\\TitleUpdated;\nuse Jiminny\\Exceptions\\InvalidArgumentException as InvalidArgumentJiminnyException;\nuse Jiminny\\Exceptions\\LogicException;\nuse Jiminny\\Exceptions\\RuntimeException;\nuse Jiminny\\Models;\nuse Jiminny\\Models\\Activity\\ActivitySummaryLog;\nuse Jiminny\\Models\\Activity\\ActivityUploadSetting;\nuse Jiminny\\Models\\Activity\\AvailabilityNotification;\nuse Jiminny\\Models\\Activity\\CoachRequest;\nuse Jiminny\\Models\\Activity\\Comment;\nuse Jiminny\\Models\\Activity\\Log;\nuse Jiminny\\Models\\Activity\\Message;\nuse Jiminny\\Models\\Activity\\Moment;\nuse Jiminny\\Models\\Activity\\Note;\nuse Jiminny\\Models\\Activity\\ParticipantSpeech;\nuse Jiminny\\Models\\Activity\\Play;\nuse Jiminny\\Models\\Activity\\Question;\nuse Jiminny\\Models\\Activity\\Share;\nuse Jiminny\\Models\\Activity\\Snapshot;\nuse Jiminny\\Models\\Activity\\Stats;\nuse Jiminny\\Models\\Activity\\SubscriptionSet;\nuse Jiminny\\Models\\Activity\\TopicTrigger;\nuse Jiminny\\Models\\Activity\\Transcription;\nuse Jiminny\\Models\\Calendar\\CalendarEvent;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Models\\Crm\\FieldData;\nuse Jiminny\\Models\\ElasticSearch\\ActivityElasticSearchTrait;\nuse Jiminny\\Models\\Feature\\FeatureEnum;\nuse Jiminny\\Models\\Participant\\Connection;\nuse Jiminny\\Models\\Playlist\\Activity as PlaylistActivity;\nuse Jiminny\\Services\\Activity\\ActivityProviderRegistry;\nuse Jiminny\\Services\\Activity\\Import\\DataResolvers\\UpdateCrmDataByStrategy;\nuse Jiminny\\Services\\Activity\\Import\\DataResolvers\\UpdateCrmDataResolverFactory;\nuse Jiminny\\Services\\Activity\\Import\\DataResolvers\\UpdateCrmDataResolverInterface;\nuse Jiminny\\Traits\\Enums;\nuse Jiminny\\Traits\\RequiresUUID;\nuse Jiminny\\Utils\\CurrencyFormatter;\nuse NumberFormatter;\n\nuse function in_array;\n\n/**\n * Jiminny\\Models\\Activity\n *\n * @property null|int $auto_score filled from ES hydrator, not in DB!\n * @property-read Account|null $account\n * @property-read CalendarEvent|null $calendarEvent\n * @property-read Contact|null $contact\n * @property-read Lead|null $lead\n * @property-read Opportunity|null $opportunity\n * @property-read Stage|null $stage\n * @property int $id\n * @property mixed|null $uuid\n * @property string|null $source\n * @property string|null $external_id\n * @property string $provider\n * @property string|null $location\n * @property string|null $telephony_provider_id\n * @property int|null $from_participant_id\n * @property int|null $to_participant_id\n * @property int|null $device_id\n * @property string|null $type\n * @property int|null $playbook_category_id\n * @property int $user_id\n * @property int|null $lead_id\n * @property int|null $account_id\n * @property int|null $contact_id\n * @property int|null $opportunity_id\n * @property int|null $stage_id\n * @property string|null $value\n * @property int|null $crm_configuration_id\n * @property string|null $crm_provider_id\n * @property string|null $language\n * @property int|null $transcription_id\n * @property int $duration\n * @property string $status\n * @property int|null $on_air\n * @property int|null $calendar_event_id\n * @property string $recording_state\n * @property bool|null $recording_preference\n * @property int $recording_reason_code\n * @property int $summary_reminder_sent\n * @property \\Illuminate\\Support\\Carbon|null $log_reminder_sent_at\n * @property \\Illuminate\\Support\\Carbon|null $organizer_notified_at\n * @property bool|null $has_recording_prompt\n * @property bool $is_internal\n * @property int $is_locked\n * @property int $is_recording\n * @property bool|null $is_processed\n * @property bool $is_private\n * @property bool $is_instant_invite\n * @property string|null $poster_path\n * @property string|null $summary\n * @property string|null $title\n * @property string|null $description\n * @property \\Illuminate\\Support\\Carbon|null $scheduled_start_time\n * @property \\Illuminate\\Support\\Carbon|null $scheduled_end_time\n * @property \\Illuminate\\Support\\Carbon|null $actual_start_time\n * @property \\Illuminate\\Support\\Carbon|null $actual_end_time\n * @property int|null $uploaded_by\n * @property \\Illuminate\\Support\\Carbon|null $deleted_at\n * @property \\Illuminate\\Support\\Carbon|null $created_at\n * @property \\Illuminate\\Support\\Carbon|null $updated_at\n * @property string|null $average_score\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Participant> $activeParticipants\n * @property-read int|null $active_participants_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Scorecard\\ActivityScorecardRuleTrigger> $activityScorecardRuleTriggers\n * @property-read int|null $activity_scorecard_rule_triggers_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Scorecard\\ActivityScorecardRule> $activityScorecardRules\n * @property-read int|null $activity_scorecard_rules_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, AvailabilityNotification> $availabilityNotifications\n * @property-read int|null $availability_notifications_count\n * @property-read \\Jiminny\\Models\\PlaybookCategory|null $category\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, CoachRequest> $coachRequests\n * @property-read int|null $coach_requests_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\CoachingFeedback> $coachingFeedbacks\n * @property-read int|null $coaching_feedbacks_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Message> $coachingMessages\n * @property-read int|null $coaching_messages_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Comment> $comments\n * @property-read int|null $comments_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Connection> $connections\n * @property-read int|null $connections_count\n * @property-read Configuration|null $crm\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, FieldData> $data\n * @property-read int|null $data_count\n * @property-read \\Jiminny\\Models\\Device|null $device\n * @property-read \\Kalnoy\\Nestedset\\Collection<int, \\Jiminny\\Models\\Playlist> $favoritePlaylists\n * @property-read int|null $favorite_playlists_count\n * @property-read \\Jiminny\\Models\\Participant|null $from\n * @property-read string|null $activity_title\n * @property-read mixed $comment_count\n * @property-read mixed $duration_for_humans\n * @property-read string $duration_for_humans_short\n * @property-read int $favorite_count\n * @property-read mixed $favorites_count\n * @property-read mixed $formatted_value\n * @property-read string $id_string\n * @property-read \\Jiminny\\Models\\Participant|null $organizer\n * @property-read mixed $play_count\n * @property-read int|null $plays_count\n * @property-read ?ProspectInterface $prospect\n * @property-read string|null $prospect_name\n * @property-read mixed $prospect_type\n * @property-read mixed $share_count\n * @property-read int|null $shares_count\n * @property-read int|null $tracks_with_telephony_count\n * @property-read int|null $visible_comments_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\CoachingFeedback> $latestCoachingFeedbacks\n * @property-read int|null $latest_coaching_feedbacks_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Log> $logs\n * @property-read int|null $logs_count\n * @property-read \\Jiminny\\Models\\Track|null $masterTrack\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Message> $messages\n * @property-read int|null $messages_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Moment> $moments\n * @property-read int|null $moments_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Note> $notes\n * @property-read int|null $notes_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Participant\\Share> $participantShares\n * @property-read int|null $participant_shares_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, ParticipantSpeech> $participantSpeeches\n * @property-read int|null $participant_speeches_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Participant\\ParticipantStats> $participantStats\n * @property-read int|null $participant_stats_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Participant> $participants\n * @property-read int|null $participants_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, PlaylistActivity> $playlistActivities\n * @property-read int|null $playlist_activities_count\n * @property-read \\Kalnoy\\Nestedset\\Collection<int, \\Jiminny\\Models\\Playlist> $playlists\n * @property-read int|null $playlists_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Play> $plays\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Question> $questions\n * @property-read int|null $questions_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Session> $sessions\n * @property-read int|null $sessions_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Share> $shares\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Snapshot> $snapshots\n * @property-read int|null $snapshots_count\n * @property-read Stats|null $stats\n * @property-read \\Jiminny\\Models\\Participant|null $to\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, TopicTrigger> $topicTriggers\n * @property-read int|null $topic_triggers_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Track> $tracks\n * @property-read int|null $tracks_count\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, \\Jiminny\\Models\\Track> $tracksWithTelephony\n * @property-read Transcription|null $transcription\n * @property-read \\Jiminny\\Models\\User $user\n * @property-read \\Illuminate\\Database\\Eloquent\\Collection<int, Comment> $visibleComments\n *\n * @method static \\Illuminate\\Database\\Eloquent\\Collection<int, static> all($columns = ['*'])\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity chunkByIdDesc($count, callable $callback, $column = null, $alias = null)\n * @method static \\Database\\Factories\\ActivityFactory factory(...$parameters)\n * @method static \\Illuminate\\Database\\Eloquent\\Collection<int, static> get($columns = ['*'])\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity heldBetween(\\Carbon\\Carbon $start, \\Carbon\\Carbon $end)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity idOrUuId($idOrUuid, bool $first = true)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity newModelQuery()\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity newQuery()\n * @method static Builder|Activity onlyTrashed()\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity query()\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity scheduledBetween(\\Carbon\\Carbon $start, \\Carbon\\Carbon $end)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity inOpenDeals()\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity notInOpenDeals()\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity forTeam(int $teamId)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity search(callable $searchQuery, $key = null, $sortByResults = true)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity uuid(string $uuid, bool $first = true)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereAccountId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereActualEndTime($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereActualStartTime($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereAverageScore($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereCalendarEventId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereContactId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereCreatedAt($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereCrmConfigurationId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereCrmProviderId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereDeletedAt($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereDescription($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereDeviceId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereDuration($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereFromParticipantId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereHasRecordingPrompt($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereIsInstantInvite($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereIsInternal($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereIsLocked($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereIsPrivate($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereIsProcessed($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereIsRecording($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereLanguage($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereLeadId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereLocation($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereLogReminderSentAt($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereOnAir($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereOpportunityId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereOrganizerNotifiedAt($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity wherePlaybookCategoryId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity wherePosterPath($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereProvider($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereRecordingPreference($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereRecordingReasonCode($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereRecordingState($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereScheduledEndTime($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereScheduledStartTime($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereSource($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereExternalId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereStageId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereStatus($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereSummary($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereSummaryReminderSent($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereTelephonyProviderId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereTitle($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereToParticipantId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereTranscriptionId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereType($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereUpdatedAt($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereUploadedBy($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereUserId($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereUuid($value)\n * @method static \\Jiminny\\Component\\Eloquent\\Builder|Activity whereValue($value)\n * @method static Builder|Activity withTrashed()\n * @method static Builder|Activity withoutTrashed()\n *\n * @mixin \\Eloquent\n */\nclass Activity extends Model implements\n ElasticSearch\\Contract\\Searchable,\n Workflow\\Workflow\\WorkflowAwareInterface,\n Models\\Contracts\\ActivityContract,\n Contracts\\Model\\ActivityInterface,\n UuidAwareInterface\n{\n use HasFactory;\n\n use Enums;\n use SoftDeletes;\n use RequiresUUID;\n use BitwiseFlagTrait;\n use ElasticSearch\\Model\\Searchable;\n use ActivityElasticSearchTrait;\n\n use Workflow\\Workflow\\WorkflowAware {\n transitionTo as traitTransitionTo;\n }\n\n public const int FLAG_RECORDING_REASON_DEFAULT = 0;\n\n // Recording Prompted but never started\n public const int FLAG_RECORDING_REASON_COMPLIANCE_PROMPT = 1;\n public const int FLAG_RECORDING_REASON_COMPLIANCE_RESUMED = 2;\n public const int FLAG_RECORDING_REASON_NO_AUDIO = 3;\n\n // Recording Disabled by Organization\n public const int FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT = 4;\n\n // Recording was restricted to one-side recordings only\n public const int FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT_ONE_SIDE = 8;\n\n // Recording was not started because it was internal and team setting disabled that.\n public const int FLAG_RECORDING_REASON_TEAM_INTERNAL_DISABLED = 16;\n\n // Recording was not started because it was internal and user setting disabled that.\n public const int FLAG_RECORDING_REASON_USER_INTERNAL_DISABLED = 32;\n\n // Recording was not started because user setting disabled automatic recording.\n public const int FLAG_RECORDING_REASON_USER_AUTOMATIC_DISABLED = 64;\n\n // Recording was not started because team setting disabled automatic recording.\n public const int FLAG_RECORDING_REASON_TEAM_AUTOMATIC_DISABLED = 128;\n\n // Recording was not started because user has overriden default.\n public const int FLAG_RECORDING_REASON_PREFERENCE_OVERRIDE = 256;\n\n // Recording was not started because they don't want internal, and this meeting was not scheduled/imported in time.\n public const int FLAG_RECORDING_REASON_USER_INTERNAL_DISABLED_UNSCHEDULED = 512;\n\n // Recording was not started because their team setting does excludes the meeting type.\n public const int FLAG_RECORDING_REASON_UNSUPPORTED_TYPE = 1024;\n\n // Recording was not started because the external provider disabled it (or recording is missing etc).\n public const int FLAG_RECORDING_REASON_EXTERNALLY_DISABLED = 2048;\n\n // Recording was stopped externally (\"exit-meeting\" Pusher event)\n public const int FLAG_RECORDING_REASON_STOPPED_EXTERNALLY = 384;\n\n // Recording couldn't be started due to Zoom hosting conflict error\n public const int FLAG_RECORDING_REASON_HOSTING_CONFLICT = 448;\n\n // meeting.failed event with reason code BOT_DENIED_FROM_LOBBY\n public const int FLAG_RECORDING_REASON_MEETING_BOT_DENIED_FROM_LOBBY = 4096;\n\n // meeting.failed event with reason code LOBBY_TIMEOUT\n public const int FLAG_RECORDING_REASON_MEETING_BOT_LOBBY_TIMEOUT = 8192;\n\n // meeting.failed event with reason code BOT_KICKED\n public const int FLAG_RECORDING_REASON_MEETING_BOT_KICKED = 16384;\n\n // meeting.failed event with reason code UNKNOWN\n public const int FLAG_RECORDING_REASON_MEETING_BOT_UNKNOWN = 32768;\n\n public const int FLAG_RECORDING_REASON_CONSENT_DENIED = 65536;\n\n // Invalid meeting (e.g. URL is invalid, or the meeting is not found)\n public const int FLAG_RECORDING_REASON_MEETING_BOT_INVALID = 131072;\n\n // The host stopped the recording.\n public const int FLAG_RECORDING_REASON_USER_STOPPED = 262144;\n\n // Recording was not started because an alternative vendor disabled it (or overrode it).\n public const int FLAG_RECORDING_REASON_VENDOR_OVERRIDE = 1048576;\n\n // Login required meeting.failed code\n public const int FLAG_RECORDING_REASON_LOGIN_REQUIRED = 524288;\n\n // Password for meeting was not provided - meeting.failed code\n public const int FLAG_RECORDING_REASON_MEETING_PASSWORD_NOT_PROVIDED = 2097152;\n\n // meeting.failed - when the meeting is locked\n public const int FLAG_RECORDING_REASON_MEETING_IS_LOCKED = 4194304;\n\n // max recording duration reached\n public const int FLAG_RECORDING_REASON_MAX_DURATION_REACHED = 8388608;\n\n // recording size is too small\n public const int FLAG_RECORDING_REASON_EMPTY_RECORDING = 16777216;\n\n // meeting.failed - when bot is redirected to sign in page multiple times\n public const int FLAG_RECORDING_REASON_MAX_RESTART_COUNT_IS_REACHED = 33554432;\n\n // meeting.failed event with reason code CONNECTION_LOST\n public const int FLAG_RECORDING_REASON_MEETING_BOT_CONNECTION_LOST = 67108864;\n\n // recording is corrupted.\n public const int FLAG_RECORDING_REASON_MEDIA_FILE_UNSUPPORTED_MIME_TYPE = 134217728;\n\n // meeting ended in lobby\n public const int FLAG_RECORDING_REASON_MEETING_ENDED_IN_LOBBY = 268435456;\n\n // meeting not started\n public const int FLAG_RECORDING_REASON_REASON_MEETING_NOT_STARTED = 536870912;\n\n // unfinished zoom custom disclaimer\n public const int FLAG_RECORDING_REASON_FEATURE_RULE_NOT_FOUND_ERROR = 1073741824;\n\n // recording download failed - server error\n public const int FLAG_RECORDING_REASON_SERVER_ERROR = 2147483648;\n\n // recording download failed - client code 404\n public const int FLAG_RECORDING_REASON_NOT_FOUND = 2147483649;\n\n // recording download failed - client code 401, 403\n public const int FLAG_RECORDING_REASON_ACCESS_DENIED = 2147483650;\n\n // recording download failed - client code 429\n public const int FLAG_RECORDING_REASON_TOO_MANY_REQUESTS = 2147483651;\n\n // recording download failed - unknown client error\n public const int FLAG_RECORDING_REASON_CLIENT_ERROR = 2147483652;\n\n // recording download failed - unknown error\n public const int FLAG_RECORDING_REASON_UNKNOWN_ERROR = 2147483653;\n\n // It has been setup ahead of time through calendar\n public const string STATUS_SCHEDULED = 'scheduled';\n\n // It is awaiting audio.\n public const string STATUS_PENDING = 'pending';\n\n // Participant(s) dialed in, awaiting organizer.\n public const string STATUS_RINGING = 'ringing';\n\n // Call is in progress.\n public const string STATUS_IN_PROGRESS = 'in-progress';\n\n // It has ended.\n public const string STATUS_COMPLETED = 'completed';\n\n // Cancelled prior to starting.\n public const string STATUS_CANCELLED = 'canceled';\n\n public const string STATUS_DUPLICATED = 'duplicated'; // duplicated conference\n\n public const string STATUS_STARTING_SOON = 'starting-soon';\n\n public const string STATUS_BOT_CREATE_SENT = 'bot-create-sent';\n\n public const string STATUS_BOT_INSTANCE_WORKER_ASSIGNED = 'worker-assigned';\n\n public const string STATUS_BOT_INSTANCE_STARTED = 'bot-started';\n\n // When bot instance is waiting in lobby\n public const string STATUS_BOT_INSTANCE_WAITING_LOBBY = 'bot-waiting';\n\n public const string STATUS_BUSY = 'busy';\n public const string STATUS_NO_ANSWER = 'no-answer';\n public const string STATUS_FAILED = 'failed'; // Used by SMS too\n\n // SMS related\n public const string STATUS_ACCEPTED = 'accepted';\n public const string STATUS_QUEUED = 'queued';\n public const string STATUS_SENDING = 'sending';\n public const string STATUS_SENT = 'sent';\n public const string STATUS_DELIVERED = 'delivered';\n public const string STATUS_UNDELIVERED = 'undelivered';\n public const string STATUS_RECEIVING = 'receiving';\n public const string STATUS_RECEIVED = 'received';\n public const string STATUS_RESENT = 'resent';\n\n public const array SMS_STATUSES = [\n Activity::STATUS_RECEIVED,\n Activity::STATUS_SENT,\n Activity::STATUS_DELIVERED,\n ];\n\n public const array SOFT_PHONE_CONFERENCE_STATUSES = [\n Activity::STATUS_IN_PROGRESS,\n Activity::STATUS_COMPLETED,\n ];\n\n // @todo refactor prefix from `TYPE_` to `CHANNEL_`\n public const string TYPE_SOFTPHONE = 'softphone';\n public const string TYPE_SOFTPHONE_INBOUND = 'softphone-inbound';\n public const string TYPE_CONFERENCE = 'conference';\n public const string TYPE_SMS_INBOUND = 'sms-inbound';\n public const string TYPE_SMS_OUTBOUND = 'sms-outbound';\n public const string TYPE_EMAIL_INBOUND = 'email-inbound';\n public const string TYPE_EMAIL_OUTBOUND = 'email-outbound';\n\n public const array CHANNELS = [\n self::TYPE_SOFTPHONE,\n self::TYPE_SOFTPHONE_INBOUND,\n self::TYPE_CONFERENCE,\n self::TYPE_SMS_INBOUND,\n self::TYPE_SMS_OUTBOUND,\n self::TYPE_EMAIL_INBOUND,\n self::TYPE_EMAIL_OUTBOUND,\n ];\n\n public const array PLAYABLE_CHANNELS = [\n self::TYPE_SOFTPHONE,\n self::TYPE_SOFTPHONE_INBOUND,\n self::TYPE_CONFERENCE,\n ];\n\n // Recording States\n public const string RECORDING_OFF = 'off'; // Default state\n public const string RECORDING_IN_PROGRESS = 'in-progress';\n public const string RECORDING_PAUSED = 'paused';\n public const string RECORDING_STOPPED = 'stopped'; // To never be resumed.\n public const string RECORDING_RECORDED = 'recorded'; // At least some portion of it was recorded.\n public const string RECORDING_FAILED = 'failed'; // Recording was attempted but failed for some reason.\n\n // Live Stream States\n public const int ON_AIR_DEFAULT = 0;\n public const int ON_AIR_READY = 1;\n public const int ON_AIR_PREPARING = 2;\n public const int ON_AIR_STREAMING = 3;\n public const int ON_AIR_FINISHED = 4;\n public const int ON_AIR_NOT_STREAMED = 5;\n public const int ON_AIR_ERROR = -1;\n\n public const string SOURCE_GONG = 'gong';\n public const string SOURCE_CHORUS = 'chorus';\n public const string SOURCE_OUTLOOK = 'outlook';\n public const string SOURCE_GOOGLE = 'google';\n\n // Activity Providers\n public const string PROVIDER_TWILIO = 'twilio'; // XXX: This is run via the Jiminny Provider.\n public const string PROVIDER_OUTREACH = 'outreach';\n public const string PROVIDER_ZOOM_BOT = 'zoom-bot';\n public const string PROVIDER_SALESLOFT = 'salesloft';\n public const string PROVIDER_GOOGLE = 'google';\n public const string PROVIDER_AIRCALL = 'aircall';\n public const string PROVIDER_JUSTCALL = 'justcall';\n public const string PROVIDER_GOOGLE_MEET = 'google-meet';\n public const string PROVIDER_GONG = 'gong';\n public const string PROVIDER_HUBSPOT = 'hubspot';\n public const string PROVIDER_CLOSE = 'close';\n public const string PROVIDER_TEAMS = 'ms-teams';\n public const string PROVIDER_SALESFORCE = 'salesforce';\n public const string PROVIDER_GROOVE = 'groove';\n public const string PROVIDER_XANT = 'xant';\n public const string PROVIDER_OFFICE = 'office';\n public const string PROVIDER_NATTERBOX = 'natterbox';\n public const string PROVIDER_RINGCENTRAL = 'ringcentral';\n public const string PROVIDER_RINGCENTRAL_VIDEO = 'ringcentral-video';\n public const string PROVIDER_GOTOMEETING = 'go-to-meeting';\n public const string PROVIDER_DEMODESK = 'demo-desk';\n public const string PROVIDER_DIALPAD = 'dialpad';\n public const string PROVIDER_ZOOM_PHONE = 'zoom-phone';\n public const string PROVIDER_CLOUDCALL = 'cloudcall';\n public const string PROVIDER_CLOUDCALL_US = 'cloudcall-us';\n public const string PROVIDER_EIGHT_BY_EIGHT = 'eight-by-eight'; // \"8x8\" UK\n public const string PROVIDER_EIGHT_BY_EIGHT_CA = 'eight-by-eight-ca'; // \"8x8\" Canada\n public const string PROVIDER_EIGHT_BY_EIGHT_AP = 'eight-by-eight-ap'; // \"8x8\" Australia\n public const string PROVIDER_EIGHT_BY_EIGHT_US_EAST = 'eight-by-eight-use'; // \"8x8\" US East\n public const string PROVIDER_EIGHT_BY_EIGHT_US_WEST = 'eight-by-eight-usw'; // \"8x8\" US West\n public const string PROVIDER_CONNECT_AND_SELL = 'connect-and-sell';\n public const string PROVIDER_CLOUD_TALK = 'cloud-talk';\n public const string PROVIDER_AMAZON_CONNECT = 'amazon-connect';\n public const string PROVIDER_VONAGE = 'vonage';\n public const string PROVIDER_MIGRATOR = 'migrator';\n public const string PROVIDER_UPLOADER = 'uploader';\n public const string PROVIDER_TALKDESK = 'talkdesk';\n public const string PROVIDER_TWILIO_FLEX = 'twilio-flex';\n public const string PROVIDER_TWILIO_FLEX_DIRECT = 'twilio-flex-direct';\n public const string PROVIDER_TWILIO_VIDEO = 'twilio-video';\n public const string PROVIDER_AVAYA = 'avaya';\n public const string PROVIDER_TELUS = 'telus';\n public const string PROVIDER_FIVE_NINE = 'five-nine';\n public const string PROVIDER_APOLLO = 'apollo';\n public const string PROVIDER_ORUM = 'orum';\n public const string PROVIDER_BLOOBIRDS = 'bloobirds';\n\n /**\n * @const API_PROVIDERS\n * A list of integrations that import calls via API instead of webhooks\n */\n public const array API_PROVIDERS = [\n self::PROVIDER_OUTREACH,\n self::PROVIDER_SALESLOFT,\n self::PROVIDER_HUBSPOT,\n self::PROVIDER_GROOVE,\n self::PROVIDER_XANT,\n self::PROVIDER_NATTERBOX,\n self::PROVIDER_CLOUDCALL,\n self::PROVIDER_CLOUDCALL_US,\n self::PROVIDER_EIGHT_BY_EIGHT,\n self::PROVIDER_EIGHT_BY_EIGHT_CA,\n self::PROVIDER_EIGHT_BY_EIGHT_AP,\n self::PROVIDER_EIGHT_BY_EIGHT_US_EAST,\n self::PROVIDER_EIGHT_BY_EIGHT_US_WEST,\n self::PROVIDER_CONNECT_AND_SELL,\n self::PROVIDER_CLOUD_TALK,\n self::PROVIDER_AMAZON_CONNECT,\n self::PROVIDER_VONAGE,\n self::PROVIDER_TALKDESK,\n self::PROVIDER_TWILIO_VIDEO,\n self::PROVIDER_TWILIO_FLEX,\n self::PROVIDER_TWILIO_FLEX_DIRECT,\n self::PROVIDER_FIVE_NINE,\n self::PROVIDER_APOLLO,\n self::PROVIDER_ORUM,\n self::PROVIDER_BLOOBIRDS,\n self::PROVIDER_RINGCENTRAL,\n self::PROVIDER_AVAYA,\n self::PROVIDER_TELUS,\n ];\n\n public const array FINITE_STATES = [\n self::TYPE_SOFTPHONE => [\n self::STATUS_COMPLETED,\n self::STATUS_FAILED,\n self::STATUS_NO_ANSWER,\n self::STATUS_BUSY,\n ],\n self::TYPE_SOFTPHONE_INBOUND => [\n self::STATUS_COMPLETED,\n self::STATUS_FAILED,\n self::STATUS_NO_ANSWER,\n self::STATUS_BUSY,\n ],\n self::TYPE_CONFERENCE => self::FINITE_STATES_CONFERENCE,\n ];\n\n public const array FINITE_STATES_CONFERENCE = [\n self::STATUS_COMPLETED,\n self::STATUS_FAILED,\n self::STATUS_CANCELLED,\n ];\n\n public const array MEETING_BOT_JOIN_ATTEMPTED = [\n self::STATUS_BOT_INSTANCE_WAITING_LOBBY,\n self::STATUS_BOT_INSTANCE_STARTED,\n ];\n\n public static array $enumStatuses = [\n self::STATUS_SCHEDULED,\n self::STATUS_PENDING,\n self::STATUS_RINGING,\n self::STATUS_IN_PROGRESS,\n self::STATUS_COMPLETED,\n self::STATUS_CANCELLED,\n self::STATUS_BUSY,\n self::STATUS_NO_ANSWER,\n self::STATUS_FAILED,\n self::STATUS_ACCEPTED,\n self::STATUS_QUEUED,\n self::STATUS_SENDING,\n self::STATUS_SENT,\n self::STATUS_RESENT,\n self::STATUS_DELIVERED,\n self::STATUS_UNDELIVERED,\n self::STATUS_RECEIVING,\n self::STATUS_RECEIVED,\n self::STATUS_BOT_INSTANCE_WAITING_LOBBY,\n self::STATUS_STARTING_SOON,\n self::STATUS_BOT_INSTANCE_WORKER_ASSIGNED,\n self::STATUS_BOT_INSTANCE_STARTED,\n self::STATUS_DUPLICATED,\n ];\n\n public static array $enumProviders = [\n self::PROVIDER_TWILIO,\n self::PROVIDER_OUTREACH,\n self::PROVIDER_ZOOM_BOT,\n self::PROVIDER_SALESLOFT,\n self::PROVIDER_AIRCALL,\n self::PROVIDER_JUSTCALL,\n self::PROVIDER_GOOGLE_MEET,\n self::PROVIDER_GONG,\n self::PROVIDER_HUBSPOT,\n self::PROVIDER_CLOSE,\n self::PROVIDER_TEAMS,\n self::PROVIDER_SALESFORCE,\n self::PROVIDER_GROOVE,\n self::PROVIDER_XANT,\n self::PROVIDER_GOOGLE,\n self::PROVIDER_OFFICE,\n self::PROVIDER_NATTERBOX,\n self::PROVIDER_RINGCENTRAL,\n self::PROVIDER_RINGCENTRAL_VIDEO,\n self::PROVIDER_GOTOMEETING,\n self::PROVIDER_DEMODESK,\n self::PROVIDER_DIALPAD,\n self::PROVIDER_ZOOM_PHONE,\n self::PROVIDER_CLOUDCALL,\n self::PROVIDER_CLOUDCALL_US,\n self::PROVIDER_EIGHT_BY_EIGHT,\n self::PROVIDER_EIGHT_BY_EIGHT_CA,\n self::PROVIDER_EIGHT_BY_EIGHT_AP,\n self::PROVIDER_EIGHT_BY_EIGHT_US_EAST,\n self::PROVIDER_EIGHT_BY_EIGHT_US_WEST,\n self::PROVIDER_CONNECT_AND_SELL,\n self::PROVIDER_CLOUD_TALK,\n self::PROVIDER_AMAZON_CONNECT,\n self::PROVIDER_VONAGE,\n self::PROVIDER_TALKDESK,\n self::PROVIDER_TWILIO_FLEX,\n self::PROVIDER_TWILIO_FLEX_DIRECT,\n self::PROVIDER_TWILIO_VIDEO,\n self::PROVIDER_AVAYA,\n self::PROVIDER_TELUS,\n self::PROVIDER_FIVE_NINE,\n self::PROVIDER_APOLLO,\n self::PROVIDER_ORUM,\n self::PROVIDER_BLOOBIRDS,\n ];\n\n public static $enumRecordingStates = [\n self::RECORDING_OFF, // Default state\n self::RECORDING_IN_PROGRESS,\n self::RECORDING_PAUSED,\n self::RECORDING_STOPPED,\n self::RECORDING_RECORDED,\n self::RECORDING_FAILED,\n ];\n\n // @Important:\n // This collection is not used anywhere, and is fully duplicated by the Channels const.\n // Validate if it is referred somehow via the enum trait, and if not, remove it entirely.\n // An even better strategy will be to move all those constants to a dedicated class\n protected array $enumTypes = [\n self::TYPE_SOFTPHONE,\n self::TYPE_SOFTPHONE_INBOUND,\n self::TYPE_CONFERENCE,\n self::TYPE_SMS_INBOUND,\n self::TYPE_SMS_OUTBOUND,\n self::TYPE_EMAIL_INBOUND,\n self::TYPE_EMAIL_OUTBOUND,\n ];\n\n protected static $enumFailedStatuses = [\n self::STATUS_NO_ANSWER,\n self::STATUS_FAILED,\n self::STATUS_BUSY,\n self::STATUS_CANCELLED,\n ];\n\n protected $table = 'activities';\n\n protected $fillable = [\n // Type of activity.\n 'type', // @todo refactor to `channel`\n // The activity type.\n 'playbook_category_id',\n // User who hosts the activity.\n 'user_id',\n // Related Lead record (if applicable)\n 'lead_id',\n // Related Account record (if applicable)\n 'account_id',\n // Related Contact record (if applicable)\n 'contact_id',\n // Related Opportunity record (if applicable)\n 'opportunity_id',\n // Stage of activity.\n 'stage_id',\n // Value of opportunity.\n 'value',\n // If the activity relates to a CRM task.\n 'crm_provider_id',\n // If the activity was created through an external device.\n 'device_id',\n // the activity's language code\n 'language',\n // transcription id\n 'transcription_id',\n // Duration of the call, with microseconds precision.\n 'duration',\n // One of enumStatuses above.\n 'status',\n // Have we reminded them to log the call?\n 'log_reminder_sent_at',\n // If activity is private or inter-org, flagged here.\n 'is_internal',\n // Managers and above can mark a call as private, to exclude it from other team members\n 'is_private',\n 'is_processed',\n // Boolean for this activity being instant invite handled.\n 'is_instant_invite',\n // If activity is in recording state, flagged here.\n 'recording_state',\n // If activity recording is overidden from default.\n 'recording_preference',\n // if recording did (not) happen, why that is\n 'recording_reason_code',\n // Average score, updated during\n 'average_score',\n // Summary that the organizer has taken after the call.\n 'summary',\n // Subject of the activity, usually taken from calendar event.\n 'title',\n // Description of the activity, usually taken from calendar event.\n 'description',\n // Start time, usually taken from calendar event.\n 'scheduled_start_time',\n // End time, usually taken from calendar event.\n 'scheduled_end_time',\n // When the call actually started.\n 'actual_start_time',\n // When the call actually ended.\n 'actual_end_time',\n // SMS: Message reference\n 'telephony_provider_id',\n // SMS: Participant who sent message\n 'from_participant_id',\n // SMS: Participant who should receive the message\n 'to_participant_id',\n // When an external guest joins an organizers meeting room and the organizer is not present,\n // send them an SMS notification that someone has joined.\n 'organizer_notified_at',\n // where was the activity imported from\n 'source',\n // The id in the source system (e.g. the bot id in Recall.ai)\n 'external_id',\n // The provider, by default it is twilio.\n 'provider',\n // Meeting location url\n 'location',\n // The snapshot for displaying a poster image.\n 'poster_path',\n 'crm_configuration_id',\n // If there is an automated message that the conversation is being recorded\n 'has_recording_prompt',\n // If the activity is being live-streamed\n 'on_air',\n 'calendar_event_id',\n ];\n\n protected $appends = [\n 'id_string',\n 'organizer',\n ];\n\n protected $hidden = [\n 'uuid',\n ];\n\n protected $visible = [\n 'id_string',\n 'type',\n 'duration',\n 'average_score',\n 'status',\n 'log_reminder_sent_at',\n 'title',\n 'description',\n 'is_internal',\n 'scheduled_start_time',\n 'scheduled_end_time',\n 'actual_start_time',\n 'actual_end_time',\n 'user',\n 'category',\n 'account',\n 'contact',\n 'opportunity',\n 'lead',\n 'stage',\n 'stats',\n 'participants',\n 'playlists',\n 'tracks',\n 'comments',\n 'plays',\n 'coachingFeedbacks',\n 'shares',\n 'favorites',\n 'language',\n 'transcription',\n 'is_private',\n 'is_instant_invite',\n 'on_air',\n 'calendar_event_id',\n ];\n\n protected function casts(): array\n {\n return [\n 'scheduled_start_time' => 'datetime',\n 'scheduled_end_time' => 'datetime',\n 'actual_start_time' => 'datetime',\n 'actual_end_time' => 'datetime',\n 'organizer_notified_at' => 'datetime',\n 'log_reminder_sent_at' => 'datetime',\n 'is_internal' => 'boolean',\n 'duration' => 'integer',\n 'average_score' => 'decimal:2',\n 'is_private' => 'boolean',\n 'is_processed' => 'boolean',\n 'is_instant_invite' => 'boolean',\n 'value' => 'decimal:2',\n 'recording_preference' => 'boolean',\n 'recording_reason_code' => 'integer',\n 'has_recording_prompt' => 'boolean',\n 'on_air' => 'integer',\n ];\n }\n\n protected static function boot()\n {\n parent::boot();\n\n static::updated(static function (Activity $activity) {\n // If activity is about to start (pending, ringing, in-progress) or event is scheduled in less than 1 week\n if (in_array($activity->status, [Activity::STATUS_PENDING, Activity::STATUS_RINGING, Activity::STATUS_IN_PROGRESS], true) ||\n ($activity->scheduled_start_time && (int) $activity->scheduled_start_time->diffInWeeks(new Carbon(), true) < 1)) {\n if ($activity->isDirty('status')) {\n event(new StatusUpdated($activity));\n }\n\n if ($activity->isDirty('stage_id')) {\n event(new StageUpdated($activity));\n }\n\n if ($activity->isDirty(['lead_id', 'account_id', 'contact_id'])) {\n event(new ProspectUpdated($activity));\n }\n\n if ($activity->isDirty('opportunity_id')) {\n event(new ActivityUpdated($activity, 'activity.opportunity-updated', Auth::user()));\n }\n\n if ($activity->isDirty('title')) {\n event(new TitleUpdated($activity));\n }\n }\n\n if ($activity->isDirty('playbook_category_id')) {\n event(new ActivityTypeUpdated($activity));\n }\n });\n\n static::deleted(static function (Activity $activity) {\n // Hard delete associated playlistActivities\n $activity->playlistActivities()->delete();\n });\n }\n\n public function getOrganizerAttribute(): ?Participant\n {\n $participant = $this->participants()->where('user_id', $this->user_id)->first();\n\n if (! $participant instanceof Participant && $participant !== null) {\n throw new RuntimeException(sprintf('$participant must be an instance of \"%s\" or null', Participant::class));\n }\n\n return $participant;\n }\n\n public function getFormattedValueAttribute()\n {\n $currencyCode = 'USD';\n if ($this->opportunity) {\n $currencyCode = $this->opportunity->getCurrencyCode();\n }\n\n $formatter = new CurrencyFormatter();\n $formatter->setTextAttribute(NumberFormatter::CURRENCY_CODE, $currencyCode);\n $formatter->setAttribute(NumberFormatter::MAX_FRACTION_DIGITS, 0);\n\n return $formatter->format($this->value, $currencyCode);\n }\n\n public function getProspectNameAttribute(): ?string\n {\n $prospectName = null;\n\n if ($this->lead_id) {\n $prospectName = $this->lead->name;\n } elseif ($this->contact_id) {\n $prospectName = $this->contact->name;\n } elseif ($this->account_id) {\n $prospectName = $this->account->name;\n }\n\n return $prospectName;\n }\n\n public function getProspectName(): ?string\n {\n /** @var string|null */\n return $this->getAttribute('prospect_name');\n }\n\n /**\n * Get activity title depending on prospect or title\n */\n public function getActivityTitleAttribute(): ?string\n {\n $activityTitle = null;\n if ($this->prospect && $this->prospect->getName()) {\n if ($this->account_id) {\n $activityTitle = $this->account->name;\n } elseif ($this->lead_id) {\n $activityTitle = $this->lead->company;\n } elseif ($this->contact_id) {\n $activityTitle = $this->contact->account ? $this->contact->account->name : $this->contact->name;\n }\n } elseif ($this->title) {\n $activityTitle = $this->title;\n }\n\n return $activityTitle;\n }\n\n public function wasRecentlyCreated(): bool\n {\n return $this->wasRecentlyCreated;\n }\n\n public function getProspectTypeAttribute()\n {\n $prospectType = null;\n\n if ($this->lead_id) {\n $prospectType = 'Lead';\n } elseif ($this->contact_id) {\n $prospectType = 'Contact';\n } elseif ($this->account_id) {\n $prospectType = 'Account';\n }\n\n return $prospectType;\n }\n\n /**\n * Return the best match for prospect. Results are in the following order of priority:\n * 1. Lead\n * 2. Contact\n * 3. Account\n * 4. NULL\n */\n public function getProspectAttribute(): ?ProspectInterface\n {\n if ($this->hasLead()) {\n return $this->getLead();\n }\n\n if ($this->hasContact()) {\n return $this->getContact();\n }\n\n if ($this->hasAccount()) {\n return $this->getAccount();\n }\n\n return null;\n }\n\n public function getTitleAttribute($value): ?string\n {\n return \\getActivityTitleAttribute(\n $this->user->name,\n $this->getType(),\n $value,\n $this->prospect->name ?? null,\n $this->from->national_phone_number ?? null\n );\n }\n\n public function getTitle(): ?string\n {\n return $this->getAttribute('title');\n }\n\n public function getSummary(): ?string\n {\n return $this->getAttribute('summary');\n }\n\n public function isInternal(): bool\n {\n return $this->getAttribute('is_internal');\n }\n\n public function getIsPrivate(): bool\n {\n return $this->getAttribute('is_private');\n }\n\n public function getDescription(): ?string\n {\n return $this->getAttribute('description');\n }\n\n public function hasTitle(): bool\n {\n return $this->getOriginal('title') !== null;\n }\n\n public function getPlayCountAttribute()\n {\n return $this->getPlaysCountAttribute();\n }\n\n public function getPlaysCountAttribute()\n {\n if (! isset($this->attributes['plays_count'])) {\n $this->loadCount('plays');\n }\n\n return $this->attributes['plays_count'];\n }\n\n public function getCommentCountAttribute()\n {\n return $this->getCommentsCountAttribute();\n }\n\n public function getCommentsCountAttribute()\n {\n if (! isset($this->attributes['comments_count'])) {\n $this->loadCount('comments');\n }\n\n return $this->attributes['comments_count'];\n }\n\n public function getVisibleCommentsCountAttribute()\n {\n if (! isset($this->attributes['visible_comments_count'])) {\n $activityCommentsService = app(ActivityCommentService::class);\n $user = Auth::user() instanceof User ? Auth::user() : null;\n $this->attributes['visible_comments_count'] = $activityCommentsService\n ->getVisibleCommentsCount($this, $user);\n }\n\n return $this->attributes['visible_comments_count'];\n }\n\n public function getShareCountAttribute()\n {\n return $this->getSharesCountAttribute();\n }\n\n public function getSharesCountAttribute()\n {\n if (! isset($this->attributes['shares_count'])) {\n $this->loadCount('shares');\n }\n\n return $this->attributes['shares_count'];\n }\n\n\n /**\n * Get the count of favorites playlists this activity appears in\n */\n public function getFavoriteCountAttribute(): int\n {\n return $this->getFavoritesCountAttribute();\n }\n\n public function getFavoritesCountAttribute()\n {\n if (! isset($this->attributes['favorites_count'])) {\n $this->loadCount('favorites');\n }\n\n return $this->attributes['favorites_count'];\n }\n\n public function getActiveParticipantsCountAttribute()\n {\n if (! isset($this->attributes['active_participants_count'])) {\n $this->loadCount('activeParticipants');\n }\n\n return $this->attributes['active_participants_count'];\n }\n\n public function getTracksWithTelephonyCountAttribute()\n {\n if (! isset($this->attributes['tracks_with_telephony_count'])) {\n $this->loadCount('tracksWithTelephony');\n }\n\n return $this->attributes['tracks_with_telephony_count'];\n }\n\n /**\n * @TEMP\n * $this->loadCount('tracksWithTelephony') throws null pointer exception\n */\n public function countTracksWithTelephony(): int\n {\n return $this->tracks()->whereNotNull('telephony_provider_id')->count();\n }\n\n public function getDuration(): float\n {\n return $this->getAttribute('duration');\n }\n\n public function getDurationForHumansAttribute()\n {\n return Carbon::now()->subSeconds($this->duration)->diffForHumans(now(), true);\n }\n\n public function getDurationForHumansShortAttribute(): string\n {\n return Carbon::now()->subSeconds($this->duration)->diffForHumans(now(), true, true);\n }\n\n public function hasRecordingPreference(): bool\n {\n return $this->getAttribute('recording_preference') !== null;\n }\n\n public function getRecordingPreference()\n {\n return $this->getAttribute('recording_preference');\n }\n\n /** @return BelongsTo<User, self> */\n public function user(): BelongsTo\n {\n return $this->belongsTo(User::class)->with('group');\n }\n\n public function device()\n {\n return $this->belongsTo(Device::class);\n }\n\n public function category()\n {\n return $this->belongsTo(PlaybookCategory::class, 'playbook_category_id');\n }\n\n public function getCategory(): ?PlaybookCategory\n {\n return $this->getAttribute('category');\n }\n\n public function getPlaybookCategoryId(): ?int\n {\n return $this->getAttribute('playbook_category_id');\n }\n\n public function hasStats(): bool\n {\n return $this->getAttribute('stats') !== null;\n }\n\n public function getStats(): ?Stats\n {\n return $this->getAttribute('stats');\n }\n\n public function stats(): HasOne\n {\n return $this->hasOne(Stats::class);\n }\n\n public function participantStats(): Eloquent\\Relations\\HasManyThrough\n {\n return $this->hasManyThrough(\n Models\\Participant\\ParticipantStats::class,\n Participant::class,\n 'activity_id',\n 'participant_id'\n );\n }\n\n public function getParticipantStats(): Eloquent\\Collection\n {\n return $this->getAttribute('participantStats');\n }\n\n public function account()\n {\n return $this->belongsTo(Account::class);\n }\n\n public function contact()\n {\n return $this->belongsTo(Contact::class)->with(['account']);\n }\n\n public function lead()\n {\n return $this->belongsTo(Lead::class)->with(['stage', 'recordType']);\n }\n\n /**\n * @return BelongsTo<Opportunity, self>\n */\n public function opportunity(): BelongsTo\n {\n /** @var BelongsTo<Opportunity, self> */\n return $this->belongsTo(Opportunity::class);\n }\n\n public function stage()\n {\n return $this->belongsTo(Stage::class);\n }\n\n /**\n * @return HasMany<Session>\n */\n public function sessions(): HasMany\n {\n return $this->hasMany(Session::class);\n }\n\n /**\n * @return HasMany|ParticipantSpeech[]|Eloquent\\Collection\n */\n public function participantSpeeches()\n {\n return $this->hasMany(ParticipantSpeech::class);\n }\n\n public function getParticipantSpeeches(): Eloquent\\Collection\n {\n return $this->getAttribute('participantSpeeches');\n }\n\n /**\n * @return HasMany|Log[]|Eloquent\\Collection\n */\n public function logs()\n {\n return $this->hasMany(Log::class);\n }\n\n /**\n * @return HasMany|Moment[]|Eloquent\\Collection\n */\n public function moments()\n {\n return $this->hasMany(Moment::class);\n }\n\n /**\n * @return HasMany|Note[]|Eloquent\\Collection\n */\n public function notes()\n {\n return $this->hasMany(Note::class);\n }\n\n /**\n * @return Eloquent\\Collection|Note[]\n */\n public function getNotes(): Eloquent\\Collection\n {\n return $this->getAttribute('notes');\n }\n\n /**\n * @return HasMany|Message[]|Eloquent\\Collection\n */\n public function messages()\n {\n return $this->hasMany(Message::class);\n }\n\n public function coachingMessages(): HasMany\n {\n return $this->hasMany(Message::class)\n ->where('is_private', 1);\n }\n\n public function getCoachingMessages(): Eloquent\\Collection\n {\n return $this->getAttribute('coachingMessages');\n }\n\n public function participants(): HasMany\n {\n return $this->hasMany(Participant::class);\n }\n\n public function getSnapshots(): Eloquent\\Collection\n {\n return $this->getAttribute('snapshots');\n }\n\n /** @return HasMany<Track> */\n public function tracks(): HasMany\n {\n return $this->hasMany(Track::class);\n }\n\n public function tracksWithTelephony(): HasMany\n {\n return $this->hasMany(Track::class)->whereNotNull('telephony_provider_id');\n }\n\n public function getTracksWithTelephony(): Eloquent\\Collection\n {\n return $this->getAttribute('tracksWithTelephony');\n }\n\n /** @return Collection|Track[] */\n public function getTracks(): Eloquent\\Collection\n {\n return $this->getAttribute('tracks');\n }\n\n public function masterTrack(): HasOne\n {\n return $this->hasOne(Track::class)->where('is_master', 1)\n ->whereIn('format', [Track::FORMAT_WAV, Track::FORMAT_M3U8])\n ->latest();\n }\n\n public function getMasterTrack(): ?Track\n {\n /** @var Track|null */\n return $this->getAttribute('masterTrack');\n }\n\n public function transcription(): Eloquent\\Relations\\BelongsTo\n {\n return $this->belongsTo(Transcription::class, 'transcription_id');\n }\n\n public function findTranscriptionPromptSummaries(): Collection\n {\n $transcriptionId = $this->getTranscriptionId();\n if (is_null($transcriptionId)) {\n return new Collection();\n }\n\n return Models\\AiPrompt::query()\n ->where('transcription_id', $transcriptionId)\n ->get();\n }\n\n public function getTranscription(): Transcription\n {\n return $this->getAttribute('transcription');\n }\n\n public function hasTranscription(): bool\n {\n return $this->getAttribute('transcription') !== null;\n }\n\n public function setTranscriptionId(int $transcriptionId): Activity\n {\n $this->setAttribute('transcription_id', $transcriptionId);\n\n return $this;\n }\n\n public function unsetTranscriptionId(): self\n {\n $this->setAttribute('transcription_id', null);\n\n return $this;\n }\n\n public function getTranscriptionId(): ?int\n {\n return $this->getAttribute('transcription_id');\n }\n\n /** @deprecated */\n public function hasTranscriptionId(): bool\n {\n return $this->getAttribute('transcription_id') !== null;\n }\n\n public function coachRequests()\n {\n return $this->hasMany(CoachRequest::class);\n }\n\n public function availabilityNotifications()\n {\n return $this->hasMany(AvailabilityNotification::class);\n }\n\n public function processingStates()\n {\n return $this->hasMany(Models\\Activity\\ActivityProcessingState::class);\n }\n\n public function uploadSettings()\n {\n return $this->hasMany(ActivityUploadSetting::class);\n }\n\n public function comments()\n {\n return $this->hasMany(Comment::class);\n }\n\n public function getComments(): Eloquent\\Collection\n {\n return $this->getAttribute('comments');\n }\n\n public function visibleComments()\n {\n $rel = $this->hasMany(Comment::class);\n // Doesn't have auth()->user() in some tests, breaks the build\n if ($user = auth()->user()) {\n return $rel->visibleThreads($user->id);\n }\n\n return $rel;\n }\n\n public function snapshots(): HasMany\n {\n return $this->hasMany(Snapshot::class);\n }\n\n public function calendarEvent()\n {\n return $this->belongsTo(CalendarEvent::class);\n }\n\n public function getCalendarEvent(): ?CalendarEvent\n {\n return $this->getAttribute('calendarEvent');\n }\n\n public function latestCoachingFeedbacks(): HasMany\n {\n return $this->hasMany(CoachingFeedback::class)->latest();\n }\n\n public function playlists(): BelongsToMany\n {\n return $this->belongsToMany(Playlist::class, 'playlist_activities')\n ->withPivot('id', 'uuid', 'user_id', 'start_time', 'end_time')\n ->using(PlaylistActivity::class)\n ->withTimestamps();\n }\n\n public function coachingFeedbacks(): HasMany\n {\n return $this->hasMany(CoachingFeedback::class);\n }\n\n /**\n * @return Eloquent\\Collection|CoachingFeedback[]\n */\n public function getCoachingFeedback(?int $visibility = null): Eloquent\\Collection\n {\n $feedbacks = $this->coachingFeedbacks();\n if ($visibility !== null) {\n $feedbacks = $feedbacks->where('visibility', $visibility);\n }\n\n return $feedbacks->get();\n }\n\n /** @return Eloquent\\Collection<int, PlaylistActivity> */\n public function favoritedBy(User $user): Eloquent\\Collection\n {\n return $this->favorites()->where('user_id', $user->getId())->get();\n }\n\n /**\n * Checks whether consumer has added this activity to their favorites playlist\n * In addition a default playlist gets created if not already present\n */\n public function wasFavoritedBy(User $user): bool\n {\n $playlist = $user->favoritePlaylist();\n\n return $playlist\n ->activities()\n ->where('activity_id', '=', $this->getId())\n ->exists();\n }\n\n /**\n * @return HasMany<PlaylistActivity>\n */\n public function playlistActivities(): HasMany\n {\n return $this->hasMany(PlaylistActivity::class);\n }\n\n /**\n * @return HasManyThrough<Playlist>\n */\n public function favoritePlaylists(): HasManyThrough\n {\n return $this->hasManyThrough(\n Playlist::class,\n PlaylistActivity::class,\n 'activity_id',\n 'id',\n 'id',\n 'playlist_id'\n )->where('is_default', 1);\n }\n\n /**\n * @return Eloquent\\Collection<int, Playlist>\n */\n public function getFavoritePlaylists(): Eloquent\\Collection\n {\n return $this->getAttribute('favoritePlaylists');\n }\n\n /**\n * Get activities from the default/favorite playlist\n *\n * @return Eloquent\\Builder|static\n */\n public function favorites()\n {\n return $this->playlistActivities()->whereHas('playlist', function ($query) {\n $query->where('is_default', 1);\n });\n }\n\n /**\n * @return Model|SubscriptionSet|null|object\n */\n public function subscribedBy(User $user)\n {\n if ($this->prospect === null) {\n return null;\n }\n\n return SubscriptionSet::select('activity_subscription_sets.*')\n ->where('user_id', $user->id)\n ->join('activity_subscriptions', function ($join) {\n $join\n ->on('subscription_set_id', '=', 'activity_subscription_sets.id');\n\n if ($this->account_id) {\n if ($this->opportunity_id) {\n $join\n ->where('followable_type', 'opportunity')\n ->where('followable_id', $this->opportunity_id);\n } else {\n $join\n ->where('followable_type', 'account')\n ->where('followable_id', $this->account_id);\n }\n } elseif ($this->contact_id) {\n $join\n ->where('followable_type', 'contact')\n ->where('followable_id', $this->contact_id);\n } elseif ($this->lead_id) {\n $join\n ->where('followable_type', 'lead')\n ->where('followable_id', $this->lead_id);\n }\n })\n ->first();\n }\n\n /**\n * @return array|Eloquent\\Builder[]|Eloquent\\Collection|SubscriptionSet[]\n */\n public function subscribers()\n {\n if ($this->prospect === null) {\n return [];\n }\n\n return SubscriptionSet::with(['subscriptions', 'user'])\n ->whereHas('subscriptions', function ($query) {\n if ($this->account_id) {\n if ($this->opportunity_id) {\n $query\n ->where('followable_type', 'opportunity')\n ->where('followable_id', $this->opportunity_id);\n } else {\n $query\n ->where('followable_type', 'account')\n ->where('followable_id', $this->account_id);\n }\n } elseif ($this->contact_id) {\n $query\n ->where('followable_type', 'contact')\n ->where('followable_id', $this->contact_id);\n } elseif ($this->lead_id) {\n $query\n ->where('followable_type', 'lead')\n ->where('followable_id', $this->lead_id);\n } else {\n // Nothing to join on?\n // refactor - use Jiminny specific exception\n throw new InvalidArgumentException('Cannot join on a specific customer filter.');\n }\n })\n ->whereHas('user', function ($query) {\n $query\n ->where('team_id', $this->user->team_id)\n ->where('status', User::STATUS_ACTIVE);\n })\n ->get();\n }\n\n /**\n * @return HasMany|Builder|Eloquent\\Collection|Play[]\n */\n public function plays()\n {\n return $this->hasMany(Play::class);\n }\n\n public function getPlays(): Eloquent\\Collection\n {\n return $this->getAttribute('plays');\n }\n\n public function playsBy(User $user)\n {\n /** @var Builder $builder */\n $builder = $this->plays()->where('user_id', $user->id);\n\n return $builder->get();\n }\n\n /**\n * Check if activity was played by a user\n */\n public function wasPlayedBy(User $user): bool\n {\n return $this->plays()->where('user_id', $user->id)->exists();\n }\n\n public function shares()\n {\n return $this->hasMany(Share::class);\n }\n\n /** @return BelongsTo<Participant, self> */\n public function from(): BelongsTo\n {\n return $this->belongsTo(Participant::class, 'from_participant_id');\n }\n\n /** @return BelongsTo<Participant, self> */\n public function to(): BelongsTo\n {\n return $this->belongsTo(Participant::class, 'to_participant_id');\n }\n\n /**\n * Get all of the connections through the participants.\n */\n public function connections()\n {\n return $this->hasManyThrough(\n Connection::class,\n Participant::class,\n 'activity_id',\n 'participant_id'\n );\n }\n\n public function getConnections(): Eloquent\\Collection\n {\n return $this->getAttribute('connections');\n }\n\n /**\n * Get all of the shares through the participants.\n */\n public function participantShares()\n {\n return $this->hasManyThrough(\n Participant\\Share::class,\n Participant::class,\n 'activity_id',\n 'participant_id'\n );\n }\n\n public function getParticipantShares(): Eloquent\\Collection\n {\n return $this->getAttribute('participantShares');\n }\n\n public function topicTriggers(): HasMany\n {\n return $this->hasMany(TopicTrigger::class);\n }\n\n public function activityScorecardRuleTriggers(): HasMany\n {\n return $this->hasMany(Models\\Scorecard\\ActivityScorecardRuleTrigger::class);\n }\n\n public function activityScorecardRules(): HasMany\n {\n return $this->hasMany(Models\\Scorecard\\ActivityScorecardRule::class);\n }\n\n public function questions(): HasMany\n {\n return $this->hasMany(Question::class);\n }\n\n /**\n * Get all the custom data attached to it.\n */\n public function data(): HasMany\n {\n return $this->hasMany(FieldData::class);\n }\n\n public function getData(): Eloquent\\Collection\n {\n /** @var Eloquent\\Collection */\n return $this->getAttribute('data');\n }\n\n #[Scope]\n protected function heldBetween($query, Carbon $start, Carbon $end)\n {\n // Sanity check.\n $from = min($start, $end);\n $until = max($start, $end);\n\n return $query\n ->where('actual_start_date', '>=', $from)\n ->where('actual_end_date', '<=', $until);\n }\n\n #[Scope]\n protected function scheduledBetween($query, Carbon $start, Carbon $end)\n {\n // Sanity check.\n $from = min($start, $end);\n $until = max($start, $end);\n\n return $query\n ->where('scheduled_start_date', '>=', $from)\n ->where('scheduled_end_date', '<=', $until);\n }\n\n /**\n * @param Builder<self> $query\n *\n * @return Builder<self>\n */\n #[Scope]\n protected function forTeam(Builder $query, int $teamId): Builder\n {\n /** @var Builder<self> */\n return $query->whereHas('user', static function (Builder $query) use ($teamId): void {\n $query->where('team_id', $teamId);\n });\n }\n\n /**\n * @param Builder<self> $query\n *\n * @return Builder<self>\n */\n #[Scope]\n protected function inOpenDeals(Builder $query): Builder\n {\n /** @var Builder<self> */\n return $query->whereHas(\n 'opportunity',\n static fn (Builder $query): Builder => $query\n ->where('is_closed', false)\n ->where('deleted_at', '=', null),\n );\n }\n\n /**\n * @param Builder<self> $query\n *\n * @return Builder<self>\n */\n #[Scope]\n protected function notInOpenDeals(Builder $query): Builder\n {\n /** @var Builder<self> */\n return $query->where(\n static fn (Builder $query): Builder => $query->whereNull('opportunity_id')\n ->orWhereHas(\n 'opportunity',\n static fn (Builder $query): Builder => $query->where('is_closed', true),\n )\n ->orWhereHas(\n 'opportunity',\n static fn (Builder $query): Builder => $query->withTrashed()->where('deleted_at', '!=', null),\n ),\n );\n }\n\n /**\n * Finds a participant and updates it with data. If participant doesn't exist creates a new participant from data.\n *\n * @param array $data participant data used to identify the participant and update it\n * @param bool $enterRoom true if participant is entering the room. false if we just want to update some participant data\n * @param Carbon|null $enterTime if $enterNow is true then this is the join time when the actual enter has occurred\n */\n public function updateOrCreateParticipant(\n array $data,\n bool $enterRoom = true,\n ?Carbon $enterTime = null,\n bool $nameMatching = false,\n ): Participant {\n $search = [];\n $participant = null;\n\n if (isset($data['user_id'])) {\n // Check if they already exist based on their ID.\n $search['user_id'] = $data['user_id'];\n } elseif (isset($data['provider_id'])) {\n $search['provider_id'] = $data['provider_id'];\n } elseif ($nameMatching && isset($data['name'])) {\n $search['name'] = $data['name'];\n }\n\n if (! empty($data['email'])) {\n $search['email'] = $data['email'];\n\n // If we have their email, this should be unique enough to lookup (e.g. calendar event based participant).\n unset($search['provider_id']);\n }\n\n // Search by phone number only in case nothing else is available to search by.\n if (array_key_exists('phone_number', $data) && empty($search)) {\n $search['phone_number'] = $data['phone_number'];\n }\n\n if (! empty($search)) {\n // Do a lookup now to see if we have a match on the provided data.\n $lookup = array_map(static function ($key, $value): array {\n return [$key, $value];\n }, array_keys($search), $search);\n\n $participant = $this->participants()->withTrashed()->where($lookup)->first();\n }\n\n // Do a partial match on the name and search in the team members.\n if (! $participant instanceof Participant && $nameMatching && ! empty($data['name'])) {\n $participantMatcher = app(MeetingBot\\Service\\ParticipantMatcher::class);\n\n if (! $participantMatcher instanceof MeetingBot\\Service\\ParticipantMatcher) {\n throw new LogicException('Expecting ParticipantMatcher service instance');\n }\n\n $participant = $participantMatcher->match($this, $data['name']);\n\n // If we've found good participant, avoid data overwrite in `$participant->fill($data)` below.\n if ($participant instanceof Models\\Participant && $participant->hasName()) {\n unset($data['name']); // Thoughts: should we unset also $data['user_id'] and $data['email'] ?\n }\n }\n\n if (! $participant instanceof Participant) {\n // If no match, create a new participant.\n if (empty($search)) {\n $participant = $this->participants()->create();\n } else {\n // If no match, create a new participant but avoid creating duplicates\n $participant = $this->participants()->withTrashed()->firstOrNew($search);\n }\n }\n\n // If we have just recycled a deleted participant\n if ($participant->trashed()) {\n $participant->deleted_at = null;\n }\n\n // Deal with the case when calendar syncs the event while it's in progress.\n // We should prevent change of the participant name, because speeches mapping will fail.\n if ($enterRoom === false\n && $this->isInProgress()\n && $participant->hasName()\n && isset($data['name'])\n && $data['name'] !== $participant->getName()\n ) {\n unset($data['name']);\n }\n\n // Upsert with new data.\n $participant->fill($data);\n\n if ($enterRoom) {\n if ($enterTime === null) {\n $enterTime = now();\n }\n\n // Participant enters room for the first time\n if ($participant->enter_time === null) {\n $participant->enter_time = $enterTime;\n }\n\n // If there is an exit time and it's prior to new enter_time\n if ($participant->exit_time && $participant->exit_time->lt($enterTime)) {\n // Participant has re-joined\n $participant->exit_time = null;\n }\n }\n\n $participant->save();\n\n return $participant;\n }\n\n /**\n * Updates participant CRM data\n *\n * @param array{\n * Lead|null,\n * Account|null,\n * Opportunity|null,\n * Contact|null,\n * Stage|null,\n * string|null\n *} $records\n * @param Participant $participant participant the CRM data is associated with\n */\n public function updateParticipantCrmData(array $records, Participant $participant): void\n {\n // Extract the records.\n [$lead, , , $contact] = $records;\n\n $resolver = $this->getUpdateCrmDataResolver();\n $strategy = $resolver->resolveForParticipant($lead, $contact);\n\n if ($strategy == UpdateCrmDataByStrategy::Lead) {\n if (! $participant->hasName()) {\n $participant->name = $lead->name;\n }\n\n if (! $participant->hasEmailAddress()) {\n $participant->email = $lead->email;\n }\n\n if (! $participant->hasPhoneNumber()) {\n $participant->phone_number = $lead->phone;\n }\n\n $participant->lead_id = $lead->id;\n $participant->save();\n } elseif ($strategy == UpdateCrmDataByStrategy::Contact) {\n if (! $participant->hasName()) {\n $participant->name = $contact->name;\n }\n\n if (! $participant->hasEmailAddress()) {\n $participant->email = $contact->email;\n }\n\n if (! $participant->hasPhoneNumber()) {\n $participant->phone_number = $contact->phone;\n }\n\n $participant->contact_id = $contact->id;\n $participant->save();\n }\n }\n\n /**\n * Updates activity CRM data\n *\n * @param array{\n * Lead|null,\n * Account|null,\n * Opportunity|null,\n * Contact|null,\n * Stage|null,\n * string|null\n *} $records\n */\n public function updateActivityCrmData(array $records): void\n {\n // Extract the records.\n [$lead, $account, $opportunity, $contact, $stage] = $records;\n\n $resolver = $this->getUpdateCrmDataResolver();\n $strategy = $resolver->resolveForActivity($lead, $contact, $account);\n\n if ($strategy == UpdateCrmDataByStrategy::Lead) {\n // Also update the parent activity if required, checking we don't create a mixed lead/account record.\n if ($this->account_id === null && $this->contact_id === null && $this->lead_id === null) {\n $this->lead_id = $lead->id;\n\n if ($this->stage_id === null && $stage) {\n $this->stage_id = $stage->id;\n }\n\n $this->save();\n }\n } elseif ($strategy == UpdateCrmDataByStrategy::Contact) {\n // Also update the parent activity if required, checking we don't create a mixed lead/account record.\n $this->lead_id = null;\n if ($this->stage && $this->stage->getType() === Stage::TYPE_LEAD) {\n $this->stage_id = null;\n }\n\n // Don't trust previous matched account_id as it might have been changed in the CRM\n if ($account && $account->id !== $this->account_id) {\n $this->account_id = $account->id;\n }\n\n if ($opportunity && $this->opportunity_id !== $opportunity->id) {\n $this->opportunity_id = $opportunity->id;\n // Reset stage_id when opportunity changes to avoid stale stage from previous opportunity match\n $this->stage_id = $stage?->id;\n }\n\n if ($this->stage_id === null && $stage) {\n $this->stage_id = $stage->id;\n }\n\n if ($opportunity && $this->value !== $opportunity->value) {\n $this->value = $opportunity->value;\n }\n\n // Always set contact_id when available, regardless of account_id status\n if ($this->contact_id === null && $contact) {\n $this->contact_id = $contact->id;\n }\n\n $this->save();\n } elseif ($strategy == UpdateCrmDataByStrategy::Account && $this->account_id === null) {\n // Also update the parent activity if required, checking we don't create a mixed lead/account record.\n $this->lead_id = null;\n if ($this->stage && $this->stage->getType() === Stage::TYPE_LEAD) {\n $this->stage_id = null;\n }\n\n // Update the account and opportunity on the activity record if possible.\n $this->account_id = $account->id;\n\n if ($this->stage_id === null && $stage) {\n $this->stage_id = $stage->id;\n }\n\n if ($this->opportunity_id === null && $opportunity) {\n $this->opportunity_id = $opportunity->id;\n $this->value = $opportunity->value;\n }\n\n $this->save();\n }\n }\n\n public function getActivityProspectData(): array\n {\n return [\n 'lead' => $this->lead_id,\n 'contact' => $this->contact_id,\n 'account' => $this->account_id,\n 'opportunity' => $this->opportunity_id,\n 'stage' => $this->stage_id,\n ];\n }\n\n public function isOrganizer(User $user): bool\n {\n return $this->user_id && $this->user_id === $user->id;\n }\n\n public function isJoinable(): bool\n {\n return \\in_array($this->status, [\n self::STATUS_SCHEDULED,\n self::STATUS_PENDING,\n self::STATUS_RINGING,\n self::STATUS_IN_PROGRESS,\n ], true);\n }\n\n public function isAttemptedForBotJoin(): bool\n {\n return in_array($this->getAttribute('status'), self::MEETING_BOT_JOIN_ATTEMPTED, true);\n }\n\n /**\n * Check if the activity can be saved to CRM (manual or autolog)\n */\n public function isLoggable(): bool\n {\n if ($this->getUser()->getTeam()->hasFeature(FeatureEnum::SIDEKICK_SETTINGS)) {\n $sidekickService = app(SidekickService::class);\n\n if (! $sidekickService->isSidekickEnabledForUser($this->getUser())) {\n return false;\n }\n }\n\n // If we don't know the activity type, don't try to log.\n if ($this->playbook_category_id === null) {\n return false;\n }\n\n if ($this->user->crm_required === false) {\n return false;\n }\n\n // Don't prompt for internal meetings.\n if ($this->is_internal) {\n return false;\n }\n\n // If we don't know who we are trying to log to, don't try to log.\n if ($this->prospect === null) {\n return false;\n }\n\n $validStatus = false;\n switch ($this->type) {\n case self::TYPE_SOFTPHONE:\n case self::TYPE_SOFTPHONE_INBOUND:\n $validStatus = true;\n\n break;\n case self::TYPE_CONFERENCE:\n $validStatus = in_array($this->status, [\n self::STATUS_BUSY,\n self::STATUS_NO_ANSWER,\n self::STATUS_COMPLETED,\n self::STATUS_CANCELLED,\n ], true);\n\n break;\n case self::TYPE_SMS_INBOUND:\n case self::TYPE_SMS_OUTBOUND:\n $validStatus = in_array($this->status, [\n self::STATUS_QUEUED,\n self::STATUS_SENT,\n self::STATUS_UNDELIVERED,\n self::STATUS_DELIVERED,\n self::STATUS_RECEIVED,\n ], true);\n\n break;\n }\n\n // Depending on the activity channel, we should not try to log.\n return $validStatus;\n }\n\n public function isScheduled(): bool\n {\n return $this->status === self::STATUS_SCHEDULED;\n }\n\n public function scheduledDuration(): int\n {\n if ($this->scheduled_start_time && $this->scheduled_end_time) {\n return $this->scheduled_end_time->timestamp - $this->scheduled_start_time->timestamp;\n }\n\n return 0;\n }\n\n public function isPending(): bool\n {\n return $this->status === self::STATUS_PENDING;\n }\n\n public function isCompleted(): bool\n {\n return $this->status === self::STATUS_COMPLETED;\n }\n\n public function isRinging(): bool\n {\n return $this->status === self::STATUS_RINGING;\n }\n\n public function isInProgress(): bool\n {\n return $this->status === self::STATUS_IN_PROGRESS;\n }\n\n public function isBusy(): bool\n {\n return $this->status === self::STATUS_BUSY;\n }\n\n public function isNoAnswer(): bool\n {\n return $this->status === self::STATUS_NO_ANSWER;\n }\n\n public function isFailed(): bool\n {\n return $this->status === self::STATUS_FAILED;\n }\n\n public function isCancelled(): bool\n {\n return $this->status === self::STATUS_CANCELLED;\n }\n\n public function hasEnded(int $gracePeriodMinutes = 15): bool\n {\n if ($this->isCompleted()) {\n return true;\n }\n\n if (($this->isFailed() || $this->isCancelled()) && $this->hasScheduledEndTime()) {\n return $this->getScheduledEndTime()->addMinutes($gracePeriodMinutes)->isPast();\n }\n\n return false;\n }\n\n public function hasStarted(): bool\n {\n return $this->hasActualStartTime();\n }\n\n public function isOngoing(): bool\n {\n return $this->hasActualStartTime() && ! $this->hasActualEndTime();\n }\n\n public function isTypeSmsInbound(): bool\n {\n return $this->getType() === self::TYPE_SMS_INBOUND;\n }\n\n public function isTypeSmsOutbound(): bool\n {\n return $this->getType() === self::TYPE_SMS_OUTBOUND;\n }\n\n public function isTypeSoftPhone(): bool\n {\n return $this->getType() === self::TYPE_SOFTPHONE;\n }\n\n public function isTypeSoftphoneInbound(): bool\n {\n return $this->getType() === self::TYPE_SOFTPHONE_INBOUND;\n }\n\n public function isTypeConference(): bool\n {\n return $this->getType() === self::TYPE_CONFERENCE;\n }\n\n /**\n * Get a conference elapsed time in seconds.\n *\n * @return int seconds count\n */\n public function secondsTimeElapsed(): int\n {\n if (empty($this->actual_start_time)) {\n return 0;\n }\n\n // Get number of seconds since conference actual start time\n return (int) abs(Carbon::now()->diffInRealSeconds($this->actual_start_time));\n }\n\n /**\n * Get a conference elapsed time formatted as \"1:30:20\" if more than 1 hour or \"30:20\" otherwise.\n */\n public function formattedTimeElapsed(): string\n {\n // Get number of seconds since conference actual start time.\n $elapsedSeconds = $this->secondsTimeElapsed();\n $elapsedTime = Carbon::createFromTimestampUTC($elapsedSeconds);\n\n // Format conference start time.\n return $elapsedTime->format($elapsedSeconds < 3600 ? 'i:s' : 'G:i:s');\n }\n\n public function wasScheduled(): bool\n {\n return $this->calendarEvent !== null || in_array($this->getSource(), [self::SOURCE_OUTLOOK, self::SOURCE_GOOGLE]);\n }\n\n public function isInstant(): bool\n {\n return ! $this->wasScheduled();\n }\n\n /**\n * GETTERS AND SETTERS FOLLOW BELOW\n */\n\n public function getUuid(): string\n {\n return $this->getAttribute('id_string');\n }\n\n public function getId(): int\n {\n return $this->getAttribute('id');\n }\n\n public function getFromParticipantId(): ?int\n {\n return $this->getAttribute('from_participant_id');\n }\n\n public function getFromParticipant(): ?Participant\n {\n return $this->getAttribute('from');\n }\n\n public function getToParticipantId(): ?int\n {\n return $this->getAttribute('to_participant_id');\n }\n\n public function getToParticipant(): ?Participant\n {\n return $this->getAttribute('to');\n }\n\n public function hasScheduledStartTime(): bool\n {\n return $this->getAttribute('scheduled_start_time') !== null;\n }\n\n public function getScheduledStartTime(): ?Carbon\n {\n return $this->getAttribute('scheduled_start_time');\n }\n\n public function setScheduledStartTime(DateTimeInterface $dateTime): self\n {\n $this->setAttribute('scheduled_start_time', $dateTime);\n\n return $this;\n }\n\n public function getScheduledEndTime(): ?DateTimeInterface\n {\n return $this->getAttribute('scheduled_end_time');\n }\n\n public function hasScheduledEndTime(): bool\n {\n return $this->getAttribute('scheduled_end_time') !== null;\n }\n\n public function setScheduledEndTime(DateTimeInterface $dateTime): self\n {\n $this->setAttribute('scheduled_end_time', $dateTime);\n\n return $this;\n }\n\n public function getActualStartTime(): ?Carbon\n {\n return $this->getAttribute('actual_start_time');\n }\n\n public function hasActualStartTime(): bool\n {\n return $this->getAttribute('actual_start_time') !== null;\n }\n\n public function getActualEndTime(): ?Carbon\n {\n return $this->getAttribute('actual_end_time');\n }\n\n public function hasActualEndTime(): bool\n {\n return $this->getAttribute('actual_end_time') !== null;\n }\n\n public function getType(): ?string\n {\n return $this->getAttribute('type');\n }\n\n public function getStatus(): string\n {\n return $this->getAttribute('status');\n }\n\n public function setStatus(string $status): self\n {\n $this->setAttribute('status', $status);\n\n return $this;\n }\n\n public function setActualStartTime(DateTimeInterface $dateTime): self\n {\n $this->setAttribute('actual_start_time', $dateTime);\n\n return $this;\n }\n\n public function setActualEndTime(DateTimeInterface $dateTime, bool $shouldUpdateDuration = true): self\n {\n $this->setAttribute('actual_end_time', $dateTime);\n\n if (! $shouldUpdateDuration) {\n return $this;\n }\n\n return $this->updateDuration();\n }\n\n public function updateDuration(): self\n {\n if (! $this->hasActualStartTime() || ! $this->hasActualEndTime()) {\n return $this;\n }\n\n return $this->setDuration(\n (int) abs($this->getActualStartTime()->diffInRealSeconds($this->getActualEndTime()))\n );\n }\n\n public function setDuration(int $duration): self\n {\n $this->setAttribute('duration', $duration);\n\n return $this;\n }\n\n public function getRecordingState(): string\n {\n return $this->getAttribute('recording_state');\n }\n\n public function isRecordingState(string $recordingState): bool\n {\n return $this->getRecordingState() === $recordingState;\n }\n\n public function setRecordingState(string $recordingState): self\n {\n $this->setAttribute('recording_state', $recordingState);\n\n return $this;\n }\n\n public function hasActivityType(): bool\n {\n return $this->getAttribute('category') !== null;\n }\n\n public function getActivityType(): ?PlaybookCategory\n {\n return $this->getAttribute('category');\n }\n\n public function setActivityType(int $playbookCategoryId): self\n {\n $this->setAttribute('playbook_category_id', $playbookCategoryId);\n\n return $this;\n }\n\n public function hasStage(): bool\n {\n return $this->getAttribute('stage') !== null;\n }\n\n public function getStage(): ?Stage\n {\n return $this->getAttribute('stage');\n }\n\n public function getStageId(): ?int\n {\n return $this->getAttribute('stage_id');\n }\n\n public function setStageId(?int $stageId): void\n {\n $this->setAttribute('stage_id', $stageId);\n }\n\n public function hasOpportunity(): bool\n {\n return $this->getAttribute('opportunity') !== null;\n }\n\n public function getOpportunity(): ?Opportunity\n {\n return $this->getAttribute('opportunity');\n }\n\n public function getOpportunityId(): ?int\n {\n return $this->getAttribute('opportunity_id');\n }\n\n public function setOpportunityId(?int $opportunityId): void\n {\n $this->setAttribute('opportunity_id', $opportunityId);\n }\n\n public function hasContact(): bool\n {\n return $this->getAttribute('contact') !== null;\n }\n\n public function getContact(): ?Contact\n {\n return $this->getAttribute('contact');\n }\n\n public function getContactId(): ?int\n {\n return $this->getAttribute('contact_id');\n }\n\n public function setContactId(?int $contactId): void\n {\n $this->setAttribute('contact_id', $contactId);\n }\n\n public function hasLead(): bool\n {\n return $this->getAttribute('lead') !== null;\n }\n\n public function getLead(): ?Lead\n {\n return $this->getAttribute('lead');\n }\n\n public function getLeadId(): ?int\n {\n return $this->getAttribute('lead_id');\n }\n\n public function setLeadId(?int $leadId): void\n {\n $this->setAttribute('lead_id', $leadId);\n }\n\n public function hasAccount(): bool\n {\n return $this->getAttribute('account') !== null;\n }\n\n public function getAccount(): ?Account\n {\n return $this->getAttribute('account');\n }\n\n public function getAccountId(): ?int\n {\n return $this->getAttribute('account_id');\n }\n\n public function setAccountId(?int $accountId): void\n {\n $this->setAttribute('account_id', $accountId);\n }\n\n /**\n * This method exists to avoid confusion using ->participants() or ->participants. Use the getter instead.\n *\n * @return Collection<int, Participant>|Participant[]\n */\n public function getParticipants(): Collection\n {\n return $this->participants;\n }\n\n /**\n * @deprecated use ParticipantRepository::findParticipantRoomOwner() instead\n */\n public function findParticipantRoomOwner(): ?Participant\n {\n $roomOwnerId = $this->getUserId();\n\n return $this->getParticipants()\n ->filter(static fn (Participant $participant): bool => $participant->isSameUserId($roomOwnerId))\n ->first();\n }\n\n public function hasCrmProviderId(): bool\n {\n return $this->getAttribute('crm_provider_id') !== null;\n }\n\n public function getCrmProviderId(): ?string\n {\n return $this->getAttribute('crm_provider_id');\n }\n\n public function setCrmProviderId(?string $crmProviderId): void\n {\n $this->setAttribute('crm_provider_id', $crmProviderId);\n }\n\n public function getUserId(): ?int\n {\n return $this->getAttribute('user_id');\n }\n\n public function hasUser(): bool\n {\n return $this->user()->exists();\n }\n\n public function getUser(): User\n {\n return $this->getAttribute('user');\n }\n\n public function getCreatedAt(): Carbon\n {\n return $this->getAttribute('created_at');\n }\n\n public function isInFiniteState(): bool\n {\n return $this->isFiniteState($this->getStatus());\n }\n\n public function isFiniteState(string $status): bool\n {\n $finiteStates = self::FINITE_STATES[$this->getType()] ?? [];\n\n return in_array($status, $finiteStates, true);\n }\n\n public function getParticipant(Authenticatable $user): Participant\n {\n return $this->findParticipant($user);\n }\n\n public function findParticipant(Authenticatable $user): ?Participant\n {\n if ($user instanceof User) {\n /** @var User $user */\n return $this->participants()->where('user_id', '=', $user->getId())->first();\n }\n\n throw new LogicException(sprintf('Unsupported Authenticatable implementation %s', get_class($user)));\n }\n\n public function hasLanguageCode(): bool\n {\n return $this->getAttribute('language') !== null;\n }\n\n public function getLanguageCode(): ?string\n {\n /** @var string|null */\n return $this->getAttribute('language');\n }\n\n public function getLanguageCodeHyphenated(): string\n {\n return str_replace('_', '-', $this->getLanguageCode() ?? '');\n }\n\n public function getLanguageCodeLocale(): string\n {\n [ $language ] = explode('_', $this->getLanguageCode() ?? '');\n\n return $language;\n }\n\n public function setLanguageCode(string $value): self\n {\n return $this->setAttribute('language', $value);\n }\n\n public function hasSource(): bool\n {\n return $this->getAttribute('source') !== null;\n }\n\n public function setSource(?string $source): self\n {\n return $this->setAttribute('source', $source);\n }\n\n public function isSource(string $source): bool\n {\n return $this->getAttribute('source') === $source;\n }\n\n public function getSource(): ?string\n {\n return $this->getAttribute('source');\n }\n\n public function isSourceGong(): bool\n {\n return $this->isSource(self::SOURCE_GONG);\n }\n\n public function getExternalId(): ?string\n {\n return $this->getAttribute('external_id');\n }\n\n public function setExternalId(?string $externalId): self\n {\n return $this->setAttribute('external_id', $externalId);\n }\n\n public function hasExternalId(): bool\n {\n return $this->getAttribute('external_id') !== null;\n }\n\n public function getProvider(): string\n {\n return $this->getAttribute('provider');\n }\n\n public function hasTelephonyProviderId(): bool\n {\n return $this->getAttribute('telephony_provider_id') !== null;\n }\n\n public function getTelephonyProviderId(): ?string\n {\n return $this->getAttribute('telephony_provider_id');\n }\n\n public function setTelephonyProviderId(?string $telephonyProviderId): self\n {\n return $this->setAttribute('telephony_provider_id', $telephonyProviderId);\n }\n\n public function getLocation(): ?string\n {\n return $this->getAttribute('location');\n }\n\n public function setLocation(?string $location): self\n {\n return $this->setAttribute('location', $location);\n }\n\n public function isDeleted(): bool\n {\n return $this->getAttribute('deleted_at') !== null;\n }\n\n /**\n * Check if activity recording is on and activity status is not one of the failed statuses.\n */\n public function canReviewActivity(): bool\n {\n $failedStatuses = self::$enumFailedStatuses;\n\n return (! in_array($this->recording_state, [self::RECORDING_OFF, self::RECORDING_STOPPED], true) &&\n ! in_array($this->status, $failedStatuses, true));\n }\n\n public function hasReasonCodeBotKicked(): bool\n {\n return $this->getFlag('recording_reason_code', self::FLAG_RECORDING_REASON_MEETING_BOT_KICKED);\n }\n\n public function hasReasonCodeNotCompliant(): bool\n {\n return $this->getFlag('recording_reason_code', self::FLAG_RECORDING_REASON_CONSENT_DENIED);\n }\n\n public function hasTopicTriggers(): bool\n {\n return $this->topicTriggers()->count() !== 0;\n }\n\n public function getTopicTriggers(): Collection\n {\n return $this->topicTriggers;\n }\n\n public function getTopicTriggersSorted(): Collection\n {\n $this->loadMissing([\n 'topicTriggers.participant',\n 'topicTriggers.playbackThemeTopicTrigger',\n 'topicTriggers.playbackThemeTopicTrigger',\n 'topicTriggers.playbackThemeTopicTrigger.playbackThemeTopic',\n 'topicTriggers.playbackThemeTopicTrigger.playbackThemeTopic.playbackTheme',\n ]);\n\n return $this->topicTriggers\n ->sortBy([\n 'playbackThemeTopicTrigger.playbackThemeTopic.playbackTheme.sort',\n 'playbackThemeTopicTrigger.playbackThemeTopic.sort',\n 'playbackThemeTopicTrigger.sort',\n ]);\n }\n\n public function hasQuestions(): bool\n {\n return $this->questions()->exists();\n }\n\n public function getQuestions(): Collection\n {\n return $this->questions;\n }\n\n public function hasValue(): bool\n {\n return $this->getAttribute('value') !== null;\n }\n\n public function getValue(): ?float\n {\n return $this->getAttribute('value');\n }\n\n public function setValue(?float $value): void\n {\n $this->setAttribute('value', $value);\n }\n\n public function transitionTo(string $newState, callable $callback, ?int $timeout = null): self\n {\n $newState = $this->getWorkflowStateFor(\n $this->getType(),\n $newState\n );\n\n return $this->traitTransitionTo($newState, $callback, $timeout);\n }\n\n public function getWorkflowState(): string\n {\n return $this->getWorkflowStateFor(\n $this->getType(),\n $this->getStatus()\n );\n }\n\n public function getActivityProviderDisplayName(): string\n {\n return \\Cache::remember('activity_provider_display_name-' . $this->getProvider(), 60 * 60 * 24, function () {\n $activityProviderRegistry = app()->make(ActivityProviderRegistry::class);\n\n try {\n return $activityProviderRegistry->get($this->getProvider())->getDisplayName();\n } catch (Exception $exception) {\n return ucfirst($this->getProvider());\n }\n });\n }\n\n private function getWorkflowStateFor(string $activityChannel, string $activityStatus): string\n {\n return sprintf(\n '%s::%s',\n $activityChannel,\n $activityStatus\n );\n }\n\n public function getWorkflow(): array\n {\n $map = [\n self::TYPE_SOFTPHONE => [\n self::STATUS_SCHEDULED => [\n self::STATUS_PENDING,\n self::STATUS_IN_PROGRESS,\n self::STATUS_FAILED,\n self::STATUS_BUSY,\n ],\n self::STATUS_PENDING => [\n self::STATUS_IN_PROGRESS,\n self::STATUS_FAILED,\n self::STATUS_BUSY,\n ],\n self::STATUS_RINGING => [\n self::STATUS_CANCELLED,\n self::STATUS_FAILED,\n self::STATUS_IN_PROGRESS,\n self::STATUS_BUSY,\n ],\n self::STATUS_IN_PROGRESS => [\n self::STATUS_COMPLETED,\n ],\n ],\n self::TYPE_SOFTPHONE_INBOUND => [\n self::STATUS_RINGING => [\n self::STATUS_IN_PROGRESS,\n self::STATUS_NO_ANSWER,\n self::STATUS_CANCELLED,\n self::STATUS_FAILED,\n self::STATUS_BUSY,\n ],\n self::STATUS_IN_PROGRESS => [\n self::STATUS_COMPLETED,\n ],\n ],\n ];\n\n return collect($map)\n ->mapWithKeys(function (array $currentStates, string $activityChannel): array {\n return [\n $activityChannel => collect($currentStates)\n ->mapWithKeys(function (array $possibleStates, $currentState) use ($activityChannel): array {\n $transitionName = $this->getWorkflowStateFor($activityChannel, $currentState);\n\n return [\n $transitionName => array_map(function (string $newState) use ($activityChannel) {\n return $this->getWorkflowStateFor($activityChannel, $newState);\n }, $possibleStates),\n ];\n }),\n ];\n })\n ->reduce(static function (array $carry, Collection $item): array {\n return array_merge($carry, $item->all());\n }, []);\n }\n\n public function hasPosterPath(): bool\n {\n return $this->getAttribute('poster_path') !== null;\n }\n\n public function getPosterPath(): ?string\n {\n return $this->getAttribute('poster_path');\n }\n\n /**\n * Take into account all recording settings and determine if we need to record this activity or not.\n */\n public function shouldRecord(): bool\n {\n return $this->determineRecordingReasonCode() === null;\n }\n\n public function determineRecordingReasonCode(): ?int\n {\n // Conference specific decisions.\n if ($this->isTypeConference()) {\n // If they have manually overridden the recording setting to not record.\n if ($this->hasRecordingPreference() && $this->getRecordingPreference() === false) {\n return self::FLAG_RECORDING_REASON_PREFERENCE_OVERRIDE;\n }\n\n // If they have manually overridden the recording setting to record.\n if ($this->hasRecordingPreference() && $this->getRecordingPreference() === true) {\n return null;\n }\n\n // If their team has disabled recording meetings, don't record.\n if ($this->user->team->isConferenceRecordPreferenceDisabled()) {\n return self::FLAG_RECORDING_REASON_TEAM_AUTOMATIC_DISABLED;\n }\n\n // If the host has disabled recording meetings, don't record.\n if ($this->user->checkConferenceRecordPreference() === false) {\n return self::FLAG_RECORDING_REASON_USER_AUTOMATIC_DISABLED;\n }\n\n // If it was marked internal...\n if ($this->is_internal) {\n // and their team has disabled recording internal meetings, don't record.\n if (\n $this->user->team->isConferenceRecordPreferenceEnabled()\n && ! $this->user->team->isConferenceRecordInternalPreferenceEnabled()\n ) {\n return self::FLAG_RECORDING_REASON_TEAM_INTERNAL_DISABLED;\n }\n\n // and the host has disabled recording internal meetings, don't record.\n if ($this->user->checkConferenceRecordInternalPreference() === false) {\n return self::FLAG_RECORDING_REASON_USER_INTERNAL_DISABLED;\n }\n }\n\n // If it was not scheduled and they disabled internal meetings, we cannot determine if it was internal.\n if ($this->wasScheduled() === false && $this->user->checkConferenceRecordInternalPreference() === false) {\n return self::FLAG_RECORDING_REASON_USER_INTERNAL_DISABLED_UNSCHEDULED;\n }\n }\n\n return null;\n }\n\n public function getRecordingReasonCode(): int\n {\n return $this->getAttribute('recording_reason_code');\n }\n\n public function setRecordingReasonCode(int $recordingReasonCode): self\n {\n $this->setAttribute('recording_reason_code', $recordingReasonCode);\n\n return $this;\n }\n\n // Not used today.\n public function getRecordingReasonString(): ?string\n {\n if ($this->hasRecordingReasonCompliancePrompted()) {\n return Team::COMPLIANCE_MODE_RECORDING_PROMPT;\n }\n\n if ($this->hasRecordingReasonComplianceRestricted()) {\n return Team::COMPLIANCE_MODE_RECORDING_RESTRICT;\n }\n\n if ($this->hasRecordingReasonComplianceRestrictedToOneSideRecording()) {\n return Team::COMPLIANCE_MODE_RECORDING_RESTRICT_ONE_SIDE;\n }\n\n return null;\n }\n\n public function hasRecordingReasonComplianceRestricted(): bool\n {\n return $this->getFlag('recording_reason_code', self::FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT);\n }\n\n public function hasRecordingReasonCompliancePrompted(): bool\n {\n return $this->getFlag('recording_reason_code', self::FLAG_RECORDING_REASON_COMPLIANCE_PROMPT);\n }\n\n public function hasRecordingReasonComplianceRestrictedToOneSideRecording(): bool\n {\n return $this->getFlag('recording_reason_code', self::FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT_ONE_SIDE);\n }\n\n public function getAudioTrack(): ?Track\n {\n /** @var Track|null */\n return $this->tracks()\n ->where('type', '=', Track::TYPE_AUDIO)\n ->first();\n }\n\n public function activeParticipants(): HasMany\n {\n return $this->hasMany(Participant::class)->active();\n }\n\n public function getActiveParticipants(): Eloquent\\Collection\n {\n return $this->getAttribute('activeParticipants');\n }\n\n public function crm(): Eloquent\\Relations\\BelongsTo\n {\n return $this->belongsTo(Configuration::class, 'crm_configuration_id');\n }\n\n public function activitySummaryLogs(): HasMany\n {\n return $this->hasMany(ActivitySummaryLog::class);\n }\n\n public function getCrm(): ?Configuration\n {\n return $this->getAttribute('crm');\n }\n\n public function hasCrmConfiguration(): bool\n {\n return $this->getAttribute('crm') !== null;\n }\n\n public function isProcessed(): ?bool\n {\n return $this->getAttribute('is_processed');\n }\n\n public function hasRecordingPrompt(): bool\n {\n return $this->getAttribute('has_recording_prompt') === true;\n }\n\n public function isOnAir(): bool\n {\n return $this->getAttribute('on_air') === self::ON_AIR_READY || $this->getAttribute('on_air') === self::ON_AIR_STREAMING;\n }\n\n public function setOnAir(int $onAir): self\n {\n $this->setAttribute('on_air', $onAir);\n\n return $this;\n }\n\n public function getOnAir(): ?int\n {\n return $this->getAttribute('on_air');\n }\n\n public function setTitleFromCallData(Call $call): void\n {\n $direction = $call->isOutbound() ? 'to' : 'from';\n\n $party = $this->prospect_name\n ?? $call->getContactName()\n ?? $call->getOtherPartyPhoneNumber()\n ;\n\n $this->update(['title' => sprintf('Call %s %s', $direction, $party)]);\n }\n\n /**\n * @param array{}|array{channels:string|null, format:string|null, type:string|null, status:string|null} $audioParams\n */\n public function createAudioTrack(\n string $telephonyProviderId,\n string $recordingUrl,\n array $audioParams = []\n ): Track {\n return $this->tracks()->updateOrCreate([\n 'telephony_provider_id' => $telephonyProviderId,\n ], [\n 'type' => $audioParams['type'] ?? Track::TYPE_AUDIO,\n 'status' => $audioParams['status'] ?? Track::STATUS_PENDING,\n 'format' => $audioParams['format'] ?? Track::FORMAT_WAV,\n 'provider_content_url' => $recordingUrl,\n 'start_time' => $this->actual_start_time,\n 'end_time' => $this->actual_end_time,\n ]);\n }\n\n public function createTrack(string $telephonyProviderId, array $params): Track\n {\n return $this->tracks()->updateOrCreate(\n [\n 'telephony_provider_id' => $telephonyProviderId,\n ],\n $params\n );\n }\n\n public function createOrganiserParticipant(Call $call): Participant\n {\n $user = $this->getUser();\n\n return $this->updateOrCreateParticipant([\n 'is_ghost' => 0,\n 'name' => $user->name,\n 'email' => $user->email,\n 'phone_number' => phone_e164(null, $call->getUserPhoneNumber()),\n 'enter_time' => $this->actual_start_time,\n 'exit_time' => $this->actual_end_time,\n 'user_id' => $user->id,\n ], false);\n }\n\n public function createProspectParticipant(Call $call): Participant\n {\n // not null 'name' is mandatory here to create a separate participant with 'nameMatching'\n // in case of the same phone_number with the Organiser\n $useNameMatching = $call->getUserPhoneNumber() === $call->getOtherPartyPhoneNumber();\n $defaultName = $useNameMatching ? '' : null;\n\n return $this->updateOrCreateParticipant(data: [\n 'is_ghost' => 0,\n 'name' => $this->prospect->name ?? $defaultName,\n 'email' => $this->prospect->email ?? null,\n 'phone_number' => phone_e164(null, $call->getOtherPartyPhoneNumber()),\n 'enter_time' => $this->actual_start_time,\n 'exit_time' => $this->actual_end_time,\n 'contact_id' => $this->contact_id ?? null,\n 'lead_id' => $this->lead_id ?? null,\n ], enterRoom: false, nameMatching: $useNameMatching);\n }\n\n public function updateParticipants(Participant $organiserParticipant, Participant $prospectParticipant): void\n {\n $this->update([\n 'from_participant_id' => $this->isTypeSoftPhone() ? $organiserParticipant->id : $prospectParticipant->id,\n 'to_participant_id' => $this->isTypeSoftPhone() ? $prospectParticipant->id : $organiserParticipant->id,\n ]);\n }\n\n public function hasProspect(): bool\n {\n return $this->getProspectAttribute() !== null;\n }\n\n public function isPrivate(): bool\n {\n return $this->getAttribute('is_private');\n }\n\n /** Create a new factory instance for the model. */\n protected static function newFactory(): Factory\n {\n return ActivityFactory::new();\n }\n\n public function getUpdatedAt(): Carbon\n {\n return $this->getAttribute('updated_at');\n }\n\n public function getActivitySummaryLogs(): Eloquent\\Collection\n {\n return $this->getAttribute('activitySummaryLogs');\n }\n\n public function hasProspectActivitySummaryLog(): bool\n {\n return $this->getActivitySummaryLogs()->contains(\n 'relation_type',\n ActivitySummaryLog::RELATION_OBJECT_TYPE_PROSPECT\n );\n }\n\n public function getTeam(): Team\n {\n return $this->getUser()->getTeam();\n }\n\n private function getUpdateCrmDataResolver(): UpdateCrmDataResolverInterface\n {\n $factory = app(UpdateCrmDataResolverFactory::class);\n\n return $factory->create($this);\n }\n\n public function getMeetingTrackProviderId(string $type): string\n {\n $label = match ($type) {\n Track::TYPE_VIDEO => 'v',\n Track::TYPE_AUDIO => 'a',\n default => throw new InvalidArgumentJiminnyException('Invalid track type'),\n };\n\n $startTimestamp = $this->getScheduledStartTime()?->getTimestamp();\n $teamId = $this->getTeam()->getId();\n\n return $this->getTelephonyProviderId() . ':' . $label . ':' . $startTimestamp . ':' . $teamId;\n }\n\n /**\n * Get all consent records associated with this activity\n *\n * @return \\Illuminate\\Database\\Eloquent\\Relations\\HasMany\n */\n public function participantConsents(): HasMany\n {\n return $this->hasMany(Participant\\Consent::class);\n }\n\n public function isDiallerCall(): bool\n {\n if ($this->getProvider() === Activity::PROVIDER_UPLOADER) {\n return false;\n }\n\n if (! in_array($this->getType(), [self::TYPE_SOFTPHONE, self::TYPE_SOFTPHONE_INBOUND])) {\n return false;\n }\n\n return $this->getProvider() !== self::PROVIDER_TWILIO;\n }\n\n public function getActivityDateWithFallback(): Carbon\n {\n if ($this->getActualStartTime() !== null) {\n return $this->getActualStartTime();\n }\n\n if ($this->getScheduledStartTime() !== null) {\n return $this->getScheduledStartTime();\n }\n\n return $this->getCreatedAt();\n }\n\n public function getCrmType(): ?string\n {\n // Treat uploader activities as conferences\n if ($this->getProvider() === Activity::PROVIDER_UPLOADER) {\n return Activity::TYPE_CONFERENCE;\n }\n\n return $this->getType();\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Execute","depth":4,"bounds":{"left":0.37799203,"top":0.09896249,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Explain Plan","depth":4,"bounds":{"left":0.38663563,"top":0.09896249,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Browse Query History","depth":4,"bounds":{"left":0.39760637,"top":0.09896249,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"View Parameters","depth":4,"bounds":{"left":0.40625,"top":0.09896249,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Open Query Execution Settings…","depth":4,"bounds":{"left":0.41489363,"top":0.09896249,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"In-Editor Results","depth":4,"bounds":{"left":0.42586437,"top":0.09896249,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Tx: Auto","depth":4,"bounds":{"left":0.4368351,"top":0.09896249,"width":0.024268618,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
3066729483647041324
|
8179532969745786684
|
visual_change
|
accessibility
|
NULL
|
Project: faVsco.js, menu
#12121 on JY-20963-fix-im Project: faVsco.js, menu
#12121 on JY-20963-fix-import-on-deleted-entity, menu
Start Listening for PHP Debug Connections
ServiceTest
Run 'ServiceTest'
Debug 'ServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
Analyzing…
Previous Highlighted Error
Next Highlighted Error
<?php
namespace Jiminny\Models;
use Carbon\Carbon;
use Database\Factories\ActivityFactory;
use DateTimeInterface;
use Exception;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Database\Eloquent;
use Illuminate\Database\Eloquent\Attributes\Scope;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Auth;
use InvalidArgumentException;
use Jiminny\Component\ElasticSearch;
use Jiminny\Component\MeetingBot;
use Jiminny\Component\Model\BitwiseFlagTrait;
use Jiminny\Component\PlaybackPage\Comments\Services\ActivityCommentService;
use Jiminny\Component\Sidekick\SidekickService;
use Jiminny\Component\Uuid\UuidAwareInterface;
use Jiminny\Component\Workflow;
use Jiminny\Contracts;
use Jiminny\Contracts\Crm\ProspectInterface;
use Jiminny\DTO\ImportCall\Call;
use Jiminny\Events\Activities\ActivityTypeUpdated;
use Jiminny\Events\Activities\ActivityUpdated;
use Jiminny\Events\Activities\ProspectUpdated;
use Jiminny\Events\Activities\StageUpdated;
use Jiminny\Events\Activities\StatusUpdated;
use Jiminny\Events\Activities\TitleUpdated;
use Jiminny\Exceptions\InvalidArgumentException as InvalidArgumentJiminnyException;
use Jiminny\Exceptions\LogicException;
use Jiminny\Exceptions\RuntimeException;
use Jiminny\Models;
use Jiminny\Models\Activity\ActivitySummaryLog;
use Jiminny\Models\Activity\ActivityUploadSetting;
use Jiminny\Models\Activity\AvailabilityNotification;
use Jiminny\Models\Activity\CoachRequest;
use Jiminny\Models\Activity\Comment;
use Jiminny\Models\Activity\Log;
use Jiminny\Models\Activity\Message;
use Jiminny\Models\Activity\Moment;
use Jiminny\Models\Activity\Note;
use Jiminny\Models\Activity\ParticipantSpeech;
use Jiminny\Models\Activity\Play;
use Jiminny\Models\Activity\Question;
use Jiminny\Models\Activity\Share;
use Jiminny\Models\Activity\Snapshot;
use Jiminny\Models\Activity\Stats;
use Jiminny\Models\Activity\SubscriptionSet;
use Jiminny\Models\Activity\TopicTrigger;
use Jiminny\Models\Activity\Transcription;
use Jiminny\Models\Calendar\CalendarEvent;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Models\Crm\FieldData;
use Jiminny\Models\ElasticSearch\ActivityElasticSearchTrait;
use Jiminny\Models\Feature\FeatureEnum;
use Jiminny\Models\Participant\Connection;
use Jiminny\Models\Playlist\Activity as PlaylistActivity;
use Jiminny\Services\Activity\ActivityProviderRegistry;
use Jiminny\Services\Activity\Import\DataResolvers\UpdateCrmDataByStrategy;
use Jiminny\Services\Activity\Import\DataResolvers\UpdateCrmDataResolverFactory;
use Jiminny\Services\Activity\Import\DataResolvers\UpdateCrmDataResolverInterface;
use Jiminny\Traits\Enums;
use Jiminny\Traits\RequiresUUID;
use Jiminny\Utils\CurrencyFormatter;
use NumberFormatter;
use function in_array;
/**
* Jiminny\Models\Activity
*
* @property null|int $auto_score filled from ES hydrator, not in DB!
* @property-read Account|null $account
* @property-read CalendarEvent|null $calendarEvent
* @property-read Contact|null $contact
* @property-read Lead|null $lead
* @property-read Opportunity|null $opportunity
* @property-read Stage|null $stage
* @property int $id
* @property mixed|null $uuid
* @property string|null $source
* @property string|null $external_id
* @property string $provider
* @property string|null $location
* @property string|null $telephony_provider_id
* @property int|null $from_participant_id
* @property int|null $to_participant_id
* @property int|null $device_id
* @property string|null $type
* @property int|null $playbook_category_id
* @property int $user_id
* @property int|null $lead_id
* @property int|null $account_id
* @property int|null $contact_id
* @property int|null $opportunity_id
* @property int|null $stage_id
* @property string|null $value
* @property int|null $crm_configuration_id
* @property string|null $crm_provider_id
* @property string|null $language
* @property int|null $transcription_id
* @property int $duration
* @property string $status
* @property int|null $on_air
* @property int|null $calendar_event_id
* @property string $recording_state
* @property bool|null $recording_preference
* @property int $recording_reason_code
* @property int $summary_reminder_sent
* @property \Illuminate\Support\Carbon|null $log_reminder_sent_at
* @property \Illuminate\Support\Carbon|null $organizer_notified_at
* @property bool|null $has_recording_prompt
* @property bool $is_internal
* @property int $is_locked
* @property int $is_recording
* @property bool|null $is_processed
* @property bool $is_private
* @property bool $is_instant_invite
* @property string|null $poster_path
* @property string|null $summary
* @property string|null $title
* @property string|null $description
* @property \Illuminate\Support\Carbon|null $scheduled_start_time
* @property \Illuminate\Support\Carbon|null $scheduled_end_time
* @property \Illuminate\Support\Carbon|null $actual_start_time
* @property \Illuminate\Support\Carbon|null $actual_end_time
* @property int|null $uploaded_by
* @property \Illuminate\Support\Carbon|null $deleted_at
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property string|null $average_score
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Participant> $activeParticipants
* @property-read int|null $active_participants_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Scorecard\ActivityScorecardRuleTrigger> $activityScorecardRuleTriggers
* @property-read int|null $activity_scorecard_rule_triggers_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Scorecard\ActivityScorecardRule> $activityScorecardRules
* @property-read int|null $activity_scorecard_rules_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, AvailabilityNotification> $availabilityNotifications
* @property-read int|null $availability_notifications_count
* @property-read \Jiminny\Models\PlaybookCategory|null $category
* @property-read \Illuminate\Database\Eloquent\Collection<int, CoachRequest> $coachRequests
* @property-read int|null $coach_requests_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\CoachingFeedback> $coachingFeedbacks
* @property-read int|null $coaching_feedbacks_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, Message> $coachingMessages
* @property-read int|null $coaching_messages_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, Comment> $comments
* @property-read int|null $comments_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, Connection> $connections
* @property-read int|null $connections_count
* @property-read Configuration|null $crm
* @property-read \Illuminate\Database\Eloquent\Collection<int, FieldData> $data
* @property-read int|null $data_count
* @property-read \Jiminny\Models\Device|null $device
* @property-read \Kalnoy\Nestedset\Collection<int, \Jiminny\Models\Playlist> $favoritePlaylists
* @property-read int|null $favorite_playlists_count
* @property-read \Jiminny\Models\Participant|null $from
* @property-read string|null $activity_title
* @property-read mixed $comment_count
* @property-read mixed $duration_for_humans
* @property-read string $duration_for_humans_short
* @property-read int $favorite_count
* @property-read mixed $favorites_count
* @property-read mixed $formatted_value
* @property-read string $id_string
* @property-read \Jiminny\Models\Participant|null $organizer
* @property-read mixed $play_count
* @property-read int|null $plays_count
* @property-read ?ProspectInterface $prospect
* @property-read string|null $prospect_name
* @property-read mixed $prospect_type
* @property-read mixed $share_count
* @property-read int|null $shares_count
* @property-read int|null $tracks_with_telephony_count
* @property-read int|null $visible_comments_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\CoachingFeedback> $latestCoachingFeedbacks
* @property-read int|null $latest_coaching_feedbacks_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, Log> $logs
* @property-read int|null $logs_count
* @property-read \Jiminny\Models\Track|null $masterTrack
* @property-read \Illuminate\Database\Eloquent\Collection<int, Message> $messages
* @property-read int|null $messages_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, Moment> $moments
* @property-read int|null $moments_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, Note> $notes
* @property-read int|null $notes_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Participant\Share> $participantShares
* @property-read int|null $participant_shares_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, ParticipantSpeech> $participantSpeeches
* @property-read int|null $participant_speeches_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Participant\ParticipantStats> $participantStats
* @property-read int|null $participant_stats_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Participant> $participants
* @property-read int|null $participants_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, PlaylistActivity> $playlistActivities
* @property-read int|null $playlist_activities_count
* @property-read \Kalnoy\Nestedset\Collection<int, \Jiminny\Models\Playlist> $playlists
* @property-read int|null $playlists_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, Play> $plays
* @property-read \Illuminate\Database\Eloquent\Collection<int, Question> $questions
* @property-read int|null $questions_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Session> $sessions
* @property-read int|null $sessions_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, Share> $shares
* @property-read \Illuminate\Database\Eloquent\Collection<int, Snapshot> $snapshots
* @property-read int|null $snapshots_count
* @property-read Stats|null $stats
* @property-read \Jiminny\Models\Participant|null $to
* @property-read \Illuminate\Database\Eloquent\Collection<int, TopicTrigger> $topicTriggers
* @property-read int|null $topic_triggers_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Track> $tracks
* @property-read int|null $tracks_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Jiminny\Models\Track> $tracksWithTelephony
* @property-read Transcription|null $transcription
* @property-read \Jiminny\Models\User $user
* @property-read \Illuminate\Database\Eloquent\Collection<int, Comment> $visibleComments
*
* @method static \Illuminate\Database\Eloquent\Collection<int, static> all($columns = ['*'])
* @method static \Jiminny\Component\Eloquent\Builder|Activity chunkByIdDesc($count, callable $callback, $column = null, $alias = null)
* @method static \Database\Factories\ActivityFactory factory(...$parameters)
* @method static \Illuminate\Database\Eloquent\Collection<int, static> get($columns = ['*'])
* @method static \Jiminny\Component\Eloquent\Builder|Activity heldBetween(\Carbon\Carbon $start, \Carbon\Carbon $end)
* @method static \Jiminny\Component\Eloquent\Builder|Activity idOrUuId($idOrUuid, bool $first = true)
* @method static \Jiminny\Component\Eloquent\Builder|Activity newModelQuery()
* @method static \Jiminny\Component\Eloquent\Builder|Activity newQuery()
* @method static Builder|Activity onlyTrashed()
* @method static \Jiminny\Component\Eloquent\Builder|Activity query()
* @method static \Jiminny\Component\Eloquent\Builder|Activity scheduledBetween(\Carbon\Carbon $start, \Carbon\Carbon $end)
* @method static \Jiminny\Component\Eloquent\Builder|Activity inOpenDeals()
* @method static \Jiminny\Component\Eloquent\Builder|Activity notInOpenDeals()
* @method static \Jiminny\Component\Eloquent\Builder|Activity forTeam(int $teamId)
* @method static \Jiminny\Component\Eloquent\Builder|Activity search(callable $searchQuery, $key = null, $sortByResults = true)
* @method static \Jiminny\Component\Eloquent\Builder|Activity uuid(string $uuid, bool $first = true)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereAccountId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereActualEndTime($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereActualStartTime($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereAverageScore($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereCalendarEventId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereContactId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereCreatedAt($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereCrmConfigurationId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereCrmProviderId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereDeletedAt($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereDescription($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereDeviceId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereDuration($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereFromParticipantId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereHasRecordingPrompt($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereIsInstantInvite($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereIsInternal($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereIsLocked($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereIsPrivate($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereIsProcessed($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereIsRecording($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereLanguage($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereLeadId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereLocation($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereLogReminderSentAt($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereOnAir($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereOpportunityId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereOrganizerNotifiedAt($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity wherePlaybookCategoryId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity wherePosterPath($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereProvider($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereRecordingPreference($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereRecordingReasonCode($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereRecordingState($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereScheduledEndTime($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereScheduledStartTime($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereSource($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereExternalId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereStageId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereStatus($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereSummary($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereSummaryReminderSent($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereTelephonyProviderId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereTitle($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereToParticipantId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereTranscriptionId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereType($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereUpdatedAt($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereUploadedBy($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereUserId($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereUuid($value)
* @method static \Jiminny\Component\Eloquent\Builder|Activity whereValue($value)
* @method static Builder|Activity withTrashed()
* @method static Builder|Activity withoutTrashed()
*
* @mixin \Eloquent
*/
class Activity extends Model implements
ElasticSearch\Contract\Searchable,
Workflow\Workflow\WorkflowAwareInterface,
Models\Contracts\ActivityContract,
Contracts\Model\ActivityInterface,
UuidAwareInterface
{
use HasFactory;
use Enums;
use SoftDeletes;
use RequiresUUID;
use BitwiseFlagTrait;
use ElasticSearch\Model\Searchable;
use ActivityElasticSearchTrait;
use Workflow\Workflow\WorkflowAware {
transitionTo as traitTransitionTo;
}
public const int FLAG_RECORDING_REASON_DEFAULT = 0;
// Recording Prompted but never started
public const int FLAG_RECORDING_REASON_COMPLIANCE_PROMPT = 1;
public const int FLAG_RECORDING_REASON_COMPLIANCE_RESUMED = 2;
public const int FLAG_RECORDING_REASON_NO_AUDIO = 3;
// Recording Disabled by Organization
public const int FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT = 4;
// Recording was restricted to one-side recordings only
public const int FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT_ONE_SIDE = 8;
// Recording was not started because it was internal and team setting disabled that.
public const int FLAG_RECORDING_REASON_TEAM_INTERNAL_DISABLED = 16;
// Recording was not started because it was internal and user setting disabled that.
public const int FLAG_RECORDING_REASON_USER_INTERNAL_DISABLED = 32;
// Recording was not started because user setting disabled automatic recording.
public const int FLAG_RECORDING_REASON_USER_AUTOMATIC_DISABLED = 64;
// Recording was not started because team setting disabled automatic recording.
public const int FLAG_RECORDING_REASON_TEAM_AUTOMATIC_DISABLED = 128;
// Recording was not started because user has overriden default.
public const int FLAG_RECORDING_REASON_PREFERENCE_OVERRIDE = 256;
// Recording was not started because they don't want internal, and this meeting was not scheduled/imported in time.
public const int FLAG_RECORDING_REASON_USER_INTERNAL_DISABLED_UNSCHEDULED = 512;
// Recording was not started because their team setting does excludes the meeting type.
public const int FLAG_RECORDING_REASON_UNSUPPORTED_TYPE = 1024;
// Recording was not started because the external provider disabled it (or recording is missing etc).
public const int FLAG_RECORDING_REASON_EXTERNALLY_DISABLED = 2048;
// Recording was stopped externally ("exit-meeting" Pusher event)
public const int FLAG_RECORDING_REASON_STOPPED_EXTERNALLY = 384;
// Recording couldn't be started due to Zoom hosting conflict error
public const int FLAG_RECORDING_REASON_HOSTING_CONFLICT = 448;
// meeting.failed event with reason code BOT_DENIED_FROM_LOBBY
public const int FLAG_RECORDING_REASON_MEETING_BOT_DENIED_FROM_LOBBY = 4096;
// meeting.failed event with reason code LOBBY_TIMEOUT
public const int FLAG_RECORDING_REASON_MEETING_BOT_LOBBY_TIMEOUT = 8192;
// meeting.failed event with reason code BOT_KICKED
public const int FLAG_RECORDING_REASON_MEETING_BOT_KICKED = 16384;
// meeting.failed event with reason code UNKNOWN
public const int FLAG_RECORDING_REASON_MEETING_BOT_UNKNOWN = 32768;
public const int FLAG_RECORDING_REASON_CONSENT_DENIED = 65536;
// Invalid meeting (e.g. URL is invalid, or the meeting is not found)
public const int FLAG_RECORDING_REASON_MEETING_BOT_INVALID = 131072;
// The host stopped the recording.
public const int FLAG_RECORDING_REASON_USER_STOPPED = 262144;
// Recording was not started because an alternative vendor disabled it (or overrode it).
public const int FLAG_RECORDING_REASON_VENDOR_OVERRIDE = 1048576;
// Login required meeting.failed code
public const int FLAG_RECORDING_REASON_LOGIN_REQUIRED = 524288;
// Password for meeting was not provided - meeting.failed code
public const int FLAG_RECORDING_REASON_MEETING_PASSWORD_NOT_PROVIDED = 2097152;
// meeting.failed - when the meeting is locked
public const int FLAG_RECORDING_REASON_MEETING_IS_LOCKED = 4194304;
// max recording duration reached
public const int FLAG_RECORDING_REASON_MAX_DURATION_REACHED = 8388608;
// recording size is too small
public const int FLAG_RECORDING_REASON_EMPTY_RECORDING = 16777216;
// meeting.failed - when bot is redirected to sign in page multiple times
public const int FLAG_RECORDING_REASON_MAX_RESTART_COUNT_IS_REACHED = 33554432;
// meeting.failed event with reason code CONNECTION_LOST
public const int FLAG_RECORDING_REASON_MEETING_BOT_CONNECTION_LOST = 67108864;
// recording is corrupted.
public const int FLAG_RECORDING_REASON_MEDIA_FILE_UNSUPPORTED_MIME_TYPE = 134217728;
// meeting ended in lobby
public const int FLAG_RECORDING_REASON_MEETING_ENDED_IN_LOBBY = 268435456;
// meeting not started
public const int FLAG_RECORDING_REASON_REASON_MEETING_NOT_STARTED = 536870912;
// unfinished zoom custom disclaimer
public const int FLAG_RECORDING_REASON_FEATURE_RULE_NOT_FOUND_ERROR = 1073741824;
// recording download failed - server error
public const int FLAG_RECORDING_REASON_SERVER_ERROR = 2147483648;
// recording download failed - client code 404
public const int FLAG_RECORDING_REASON_NOT_FOUND = 2147483649;
// recording download failed - client code 401, 403
public const int FLAG_RECORDING_REASON_ACCESS_DENIED = 2147483650;
// recording download failed - client code 429
public const int FLAG_RECORDING_REASON_TOO_MANY_REQUESTS = 2147483651;
// recording download failed - unknown client error
public const int FLAG_RECORDING_REASON_CLIENT_ERROR = 2147483652;
// recording download failed - unknown error
public const int FLAG_RECORDING_REASON_UNKNOWN_ERROR = 2147483653;
// It has been setup ahead of time through calendar
public const string STATUS_SCHEDULED = 'scheduled';
// It is awaiting audio.
public const string STATUS_PENDING = 'pending';
// Participant(s) dialed in, awaiting organizer.
public const string STATUS_RINGING = 'ringing';
// Call is in progress.
public const string STATUS_IN_PROGRESS = 'in-progress';
// It has ended.
public const string STATUS_COMPLETED = 'completed';
// Cancelled prior to starting.
public const string STATUS_CANCELLED = 'canceled';
public const string STATUS_DUPLICATED = 'duplicated'; // duplicated conference
public const string STATUS_STARTING_SOON = 'starting-soon';
public const string STATUS_BOT_CREATE_SENT = 'bot-create-sent';
public const string STATUS_BOT_INSTANCE_WORKER_ASSIGNED = 'worker-assigned';
public const string STATUS_BOT_INSTANCE_STARTED = 'bot-started';
// When bot instance is waiting in lobby
public const string STATUS_BOT_INSTANCE_WAITING_LOBBY = 'bot-waiting';
public const string STATUS_BUSY = 'busy';
public const string STATUS_NO_ANSWER = 'no-answer';
public const string STATUS_FAILED = 'failed'; // Used by SMS too
// SMS related
public const string STATUS_ACCEPTED = 'accepted';
public const string STATUS_QUEUED = 'queued';
public const string STATUS_SENDING = 'sending';
public const string STATUS_SENT = 'sent';
public const string STATUS_DELIVERED = 'delivered';
public const string STATUS_UNDELIVERED = 'undelivered';
public const string STATUS_RECEIVING = 'receiving';
public const string STATUS_RECEIVED = 'received';
public const string STATUS_RESENT = 'resent';
public const array SMS_STATUSES = [
Activity::STATUS_RECEIVED,
Activity::STATUS_SENT,
Activity::STATUS_DELIVERED,
];
public const array SOFT_PHONE_CONFERENCE_STATUSES = [
Activity::STATUS_IN_PROGRESS,
Activity::STATUS_COMPLETED,
];
// @todo refactor prefix from `TYPE_` to `CHANNEL_`
public const string TYPE_SOFTPHONE = 'softphone';
public const string TYPE_SOFTPHONE_INBOUND = 'softphone-inbound';
public const string TYPE_CONFERENCE = 'conference';
public const string TYPE_SMS_INBOUND = 'sms-inbound';
public const string TYPE_SMS_OUTBOUND = 'sms-outbound';
public const string TYPE_EMAIL_INBOUND = 'email-inbound';
public const string TYPE_EMAIL_OUTBOUND = 'email-outbound';
public const array CHANNELS = [
self::TYPE_SOFTPHONE,
self::TYPE_SOFTPHONE_INBOUND,
self::TYPE_CONFERENCE,
self::TYPE_SMS_INBOUND,
self::TYPE_SMS_OUTBOUND,
self::TYPE_EMAIL_INBOUND,
self::TYPE_EMAIL_OUTBOUND,
];
public const array PLAYABLE_CHANNELS = [
self::TYPE_SOFTPHONE,
self::TYPE_SOFTPHONE_INBOUND,
self::TYPE_CONFERENCE,
];
// Recording States
public const string RECORDING_OFF = 'off'; // Default state
public const string RECORDING_IN_PROGRESS = 'in-progress';
public const string RECORDING_PAUSED = 'paused';
public const string RECORDING_STOPPED = 'stopped'; // To never be resumed.
public const string RECORDING_RECORDED = 'recorded'; // At least some portion of it was recorded.
public const string RECORDING_FAILED = 'failed'; // Recording was attempted but failed for some reason.
// Live Stream States
public const int ON_AIR_DEFAULT = 0;
public const int ON_AIR_READY = 1;
public const int ON_AIR_PREPARING = 2;
public const int ON_AIR_STREAMING = 3;
public const int ON_AIR_FINISHED = 4;
public const int ON_AIR_NOT_STREAMED = 5;
public const int ON_AIR_ERROR = -1;
public const string SOURCE_GONG = 'gong';
public const string SOURCE_CHORUS = 'chorus';
public const string SOURCE_OUTLOOK = 'outlook';
public const string SOURCE_GOOGLE = 'google';
// Activity Providers
public const string PROVIDER_TWILIO = 'twilio'; // XXX: This is run via the Jiminny Provider.
public const string PROVIDER_OUTREACH = 'outreach';
public const string PROVIDER_ZOOM_BOT = 'zoom-bot';
public const string PROVIDER_SALESLOFT = 'salesloft';
public const string PROVIDER_GOOGLE = 'google';
public const string PROVIDER_AIRCALL = 'aircall';
public const string PROVIDER_JUSTCALL = 'justcall';
public const string PROVIDER_GOOGLE_MEET = 'google-meet';
public const string PROVIDER_GONG = 'gong';
public const string PROVIDER_HUBSPOT = 'hubspot';
public const string PROVIDER_CLOSE = 'close';
public const string PROVIDER_TEAMS = 'ms-teams';
public const string PROVIDER_SALESFORCE = 'salesforce';
public const string PROVIDER_GROOVE = 'groove';
public const string PROVIDER_XANT = 'xant';
public const string PROVIDER_OFFICE = 'office';
public const string PROVIDER_NATTERBOX = 'natterbox';
public const string PROVIDER_RINGCENTRAL = 'ringcentral';
public const string PROVIDER_RINGCENTRAL_VIDEO = 'ringcentral-video';
public const string PROVIDER_GOTOMEETING = 'go-to-meeting';
public const string PROVIDER_DEMODESK = 'demo-desk';
public const string PROVIDER_DIALPAD = 'dialpad';
public const string PROVIDER_ZOOM_PHONE = 'zoom-phone';
public const string PROVIDER_CLOUDCALL = 'cloudcall';
public const string PROVIDER_CLOUDCALL_US = 'cloudcall-us';
public const string PROVIDER_EIGHT_BY_EIGHT = 'eight-by-eight'; // "8x8" UK
public const string PROVIDER_EIGHT_BY_EIGHT_CA = 'eight-by-eight-ca'; // "8x8" Canada
public const string PROVIDER_EIGHT_BY_EIGHT_AP = 'eight-by-eight-ap'; // "8x8" Australia
public const string PROVIDER_EIGHT_BY_EIGHT_US_EAST = 'eight-by-eight-use'; // "8x8" US East
public const string PROVIDER_EIGHT_BY_EIGHT_US_WEST = 'eight-by-eight-usw'; // "8x8" US West
public const string PROVIDER_CONNECT_AND_SELL = 'connect-and-sell';
public const string PROVIDER_CLOUD_TALK = 'cloud-talk';
public const string PROVIDER_AMAZON_CONNECT = 'amazon-connect';
public const string PROVIDER_VONAGE = 'vonage';
public const string PROVIDER_MIGRATOR = 'migrator';
public const string PROVIDER_UPLOADER = 'uploader';
public const string PROVIDER_TALKDESK = 'talkdesk';
public const string PROVIDER_TWILIO_FLEX = 'twilio-flex';
public const string PROVIDER_TWILIO_FLEX_DIRECT = 'twilio-flex-direct';
public const string PROVIDER_TWILIO_VIDEO = 'twilio-video';
public const string PROVIDER_AVAYA = 'avaya';
public const string PROVIDER_TELUS = 'telus';
public const string PROVIDER_FIVE_NINE = 'five-nine';
public const string PROVIDER_APOLLO = 'apollo';
public const string PROVIDER_ORUM = 'orum';
public const string PROVIDER_BLOOBIRDS = 'bloobirds';
/**
* @const API_PROVIDERS
* A list of integrations that import calls via API instead of webhooks
*/
public const array API_PROVIDERS = [
self::PROVIDER_OUTREACH,
self::PROVIDER_SALESLOFT,
self::PROVIDER_HUBSPOT,
self::PROVIDER_GROOVE,
self::PROVIDER_XANT,
self::PROVIDER_NATTERBOX,
self::PROVIDER_CLOUDCALL,
self::PROVIDER_CLOUDCALL_US,
self::PROVIDER_EIGHT_BY_EIGHT,
self::PROVIDER_EIGHT_BY_EIGHT_CA,
self::PROVIDER_EIGHT_BY_EIGHT_AP,
self::PROVIDER_EIGHT_BY_EIGHT_US_EAST,
self::PROVIDER_EIGHT_BY_EIGHT_US_WEST,
self::PROVIDER_CONNECT_AND_SELL,
self::PROVIDER_CLOUD_TALK,
self::PROVIDER_AMAZON_CONNECT,
self::PROVIDER_VONAGE,
self::PROVIDER_TALKDESK,
self::PROVIDER_TWILIO_VIDEO,
self::PROVIDER_TWILIO_FLEX,
self::PROVIDER_TWILIO_FLEX_DIRECT,
self::PROVIDER_FIVE_NINE,
self::PROVIDER_APOLLO,
self::PROVIDER_ORUM,
self::PROVIDER_BLOOBIRDS,
self::PROVIDER_RINGCENTRAL,
self::PROVIDER_AVAYA,
self::PROVIDER_TELUS,
];
public const array FINITE_STATES = [
self::TYPE_SOFTPHONE => [
self::STATUS_COMPLETED,
self::STATUS_FAILED,
self::STATUS_NO_ANSWER,
self::STATUS_BUSY,
],
self::TYPE_SOFTPHONE_INBOUND => [
self::STATUS_COMPLETED,
self::STATUS_FAILED,
self::STATUS_NO_ANSWER,
self::STATUS_BUSY,
],
self::TYPE_CONFERENCE => self::FINITE_STATES_CONFERENCE,
];
public const array FINITE_STATES_CONFERENCE = [
self::STATUS_COMPLETED,
self::STATUS_FAILED,
self::STATUS_CANCELLED,
];
public const array MEETING_BOT_JOIN_ATTEMPTED = [
self::STATUS_BOT_INSTANCE_WAITING_LOBBY,
self::STATUS_BOT_INSTANCE_STARTED,
];
public static array $enumStatuses = [
self::STATUS_SCHEDULED,
self::STATUS_PENDING,
self::STATUS_RINGING,
self::STATUS_IN_PROGRESS,
self::STATUS_COMPLETED,
self::STATUS_CANCELLED,
self::STATUS_BUSY,
self::STATUS_NO_ANSWER,
self::STATUS_FAILED,
self::STATUS_ACCEPTED,
self::STATUS_QUEUED,
self::STATUS_SENDING,
self::STATUS_SENT,
self::STATUS_RESENT,
self::STATUS_DELIVERED,
self::STATUS_UNDELIVERED,
self::STATUS_RECEIVING,
self::STATUS_RECEIVED,
self::STATUS_BOT_INSTANCE_WAITING_LOBBY,
self::STATUS_STARTING_SOON,
self::STATUS_BOT_INSTANCE_WORKER_ASSIGNED,
self::STATUS_BOT_INSTANCE_STARTED,
self::STATUS_DUPLICATED,
];
public static array $enumProviders = [
self::PROVIDER_TWILIO,
self::PROVIDER_OUTREACH,
self::PROVIDER_ZOOM_BOT,
self::PROVIDER_SALESLOFT,
self::PROVIDER_AIRCALL,
self::PROVIDER_JUSTCALL,
self::PROVIDER_GOOGLE_MEET,
self::PROVIDER_GONG,
self::PROVIDER_HUBSPOT,
self::PROVIDER_CLOSE,
self::PROVIDER_TEAMS,
self::PROVIDER_SALESFORCE,
self::PROVIDER_GROOVE,
self::PROVIDER_XANT,
self::PROVIDER_GOOGLE,
self::PROVIDER_OFFICE,
self::PROVIDER_NATTERBOX,
self::PROVIDER_RINGCENTRAL,
self::PROVIDER_RINGCENTRAL_VIDEO,
self::PROVIDER_GOTOMEETING,
self::PROVIDER_DEMODESK,
self::PROVIDER_DIALPAD,
self::PROVIDER_ZOOM_PHONE,
self::PROVIDER_CLOUDCALL,
self::PROVIDER_CLOUDCALL_US,
self::PROVIDER_EIGHT_BY_EIGHT,
self::PROVIDER_EIGHT_BY_EIGHT_CA,
self::PROVIDER_EIGHT_BY_EIGHT_AP,
self::PROVIDER_EIGHT_BY_EIGHT_US_EAST,
self::PROVIDER_EIGHT_BY_EIGHT_US_WEST,
self::PROVIDER_CONNECT_AND_SELL,
self::PROVIDER_CLOUD_TALK,
self::PROVIDER_AMAZON_CONNECT,
self::PROVIDER_VONAGE,
self::PROVIDER_TALKDESK,
self::PROVIDER_TWILIO_FLEX,
self::PROVIDER_TWILIO_FLEX_DIRECT,
self::PROVIDER_TWILIO_VIDEO,
self::PROVIDER_AVAYA,
self::PROVIDER_TELUS,
self::PROVIDER_FIVE_NINE,
self::PROVIDER_APOLLO,
self::PROVIDER_ORUM,
self::PROVIDER_BLOOBIRDS,
];
public static $enumRecordingStates = [
self::RECORDING_OFF, // Default state
self::RECORDING_IN_PROGRESS,
self::RECORDING_PAUSED,
self::RECORDING_STOPPED,
self::RECORDING_RECORDED,
self::RECORDING_FAILED,
];
// @Important:
// This collection is not used anywhere, and is fully duplicated by the Channels const.
// Validate if it is referred somehow via the enum trait, and if not, remove it entirely.
// An even better strategy will be to move all those constants to a dedicated class
protected array $enumTypes = [
self::TYPE_SOFTPHONE,
self::TYPE_SOFTPHONE_INBOUND,
self::TYPE_CONFERENCE,
self::TYPE_SMS_INBOUND,
self::TYPE_SMS_OUTBOUND,
self::TYPE_EMAIL_INBOUND,
self::TYPE_EMAIL_OUTBOUND,
];
protected static $enumFailedStatuses = [
self::STATUS_NO_ANSWER,
self::STATUS_FAILED,
self::STATUS_BUSY,
self::STATUS_CANCELLED,
];
protected $table = 'activities';
protected $fillable = [
// Type of activity.
'type', // @todo refactor to `channel`
// The activity type.
'playbook_category_id',
// User who hosts the activity.
'user_id',
// Related Lead record (if applicable)
'lead_id',
// Related Account record (if applicable)
'account_id',
// Related Contact record (if applicable)
'contact_id',
// Related Opportunity record (if applicable)
'opportunity_id',
// Stage of activity.
'stage_id',
// Value of opportunity.
'value',
// If the activity relates to a CRM task.
'crm_provider_id',
// If the activity was created through an external device.
'device_id',
// the activity's language code
'language',
// transcription id
'transcription_id',
// Duration of the call, with microseconds precision.
'duration',
// One of enumStatuses above.
'status',
// Have we reminded them to log the call?
'log_reminder_sent_at',
// If activity is private or inter-org, flagged here.
'is_internal',
// Managers and above can mark a call as private, to exclude it from other team members
'is_private',
'is_processed',
// Boolean for this activity being instant invite handled.
'is_instant_invite',
// If activity is in recording state, flagged here.
'recording_state',
// If activity recording is overidden from default.
'recording_preference',
// if recording did (not) happen, why that is
'recording_reason_code',
// Average score, updated during
'average_score',
// Summary that the organizer has taken after the call.
'summary',
// Subject of the activity, usually taken from calendar event.
'title',
// Description of the activity, usually taken from calendar event.
'description',
// Start time, usually taken from calendar event.
'scheduled_start_time',
// End time, usually taken from calendar event.
'scheduled_end_time',
// When the call actually started.
'actual_start_time',
// When the call actually ended.
'actual_end_time',
// SMS: Message reference
'telephony_provider_id',
// SMS: Participant who sent message
'from_participant_id',
// SMS: Participant who should receive the message
'to_participant_id',
// When an external guest joins an organizers meeting room and the organizer is not present,
// send them an SMS notification that someone has joined.
'organizer_notified_at',
// where was the activity imported from
'source',
// The id in the source system (e.g. the bot id in Recall.ai)
'external_id',
// The provider, by default it is twilio.
'provider',
// Meeting location url
'location',
// The snapshot for displaying a poster image.
'poster_path',
'crm_configuration_id',
// If there is an automated message that the conversation is being recorded
'has_recording_prompt',
// If the activity is being live-streamed
'on_air',
'calendar_event_id',
];
protected $appends = [
'id_string',
'organizer',
];
protected $hidden = [
'uuid',
];
protected $visible = [
'id_string',
'type',
'duration',
'average_score',
'status',
'log_reminder_sent_at',
'title',
'description',
'is_internal',
'scheduled_start_time',
'scheduled_end_time',
'actual_start_time',
'actual_end_time',
'user',
'category',
'account',
'contact',
'opportunity',
'lead',
'stage',
'stats',
'participants',
'playlists',
'tracks',
'comments',
'plays',
'coachingFeedbacks',
'shares',
'favorites',
'language',
'transcription',
'is_private',
'is_instant_invite',
'on_air',
'calendar_event_id',
];
protected function casts(): array
{
return [
'scheduled_start_time' => 'datetime',
'scheduled_end_time' => 'datetime',
'actual_start_time' => 'datetime',
'actual_end_time' => 'datetime',
'organizer_notified_at' => 'datetime',
'log_reminder_sent_at' => 'datetime',
'is_internal' => 'boolean',
'duration' => 'integer',
'average_score' => 'decimal:2',
'is_private' => 'boolean',
'is_processed' => 'boolean',
'is_instant_invite' => 'boolean',
'value' => 'decimal:2',
'recording_preference' => 'boolean',
'recording_reason_code' => 'integer',
'has_recording_prompt' => 'boolean',
'on_air' => 'integer',
];
}
protected static function boot()
{
parent::boot();
static::updated(static function (Activity $activity) {
// If activity is about to start (pending, ringing, in-progress) or event is scheduled in less than 1 week
if (in_array($activity->status, [Activity::STATUS_PENDING, Activity::STATUS_RINGING, Activity::STATUS_IN_PROGRESS], true) ||
($activity->scheduled_start_time && (int) $activity->scheduled_start_time->diffInWeeks(new Carbon(), true) < 1)) {
if ($activity->isDirty('status')) {
event(new StatusUpdated($activity));
}
if ($activity->isDirty('stage_id')) {
event(new StageUpdated($activity));
}
if ($activity->isDirty(['lead_id', 'account_id', 'contact_id'])) {
event(new ProspectUpdated($activity));
}
if ($activity->isDirty('opportunity_id')) {
event(new ActivityUpdated($activity, 'activity.opportunity-updated', Auth::user()));
}
if ($activity->isDirty('title')) {
event(new TitleUpdated($activity));
}
}
if ($activity->isDirty('playbook_category_id')) {
event(new ActivityTypeUpdated($activity));
}
});
static::deleted(static function (Activity $activity) {
// Hard delete associated playlistActivities
$activity->playlistActivities()->delete();
});
}
public function getOrganizerAttribute(): ?Participant
{
$participant = $this->participants()->where('user_id', $this->user_id)->first();
if (! $participant instanceof Participant && $participant !== null) {
throw new RuntimeException(sprintf('$participant must be an instance of "%s" or null', Participant::class));
}
return $participant;
}
public function getFormattedValueAttribute()
{
$currencyCode = 'USD';
if ($this->opportunity) {
$currencyCode = $this->opportunity->getCurrencyCode();
}
$formatter = new CurrencyFormatter();
$formatter->setTextAttribute(NumberFormatter::CURRENCY_CODE, $currencyCode);
$formatter->setAttribute(NumberFormatter::MAX_FRACTION_DIGITS, 0);
return $formatter->format($this->value, $currencyCode);
}
public function getProspectNameAttribute(): ?string
{
$prospectName = null;
if ($this->lead_id) {
$prospectName = $this->lead->name;
} elseif ($this->contact_id) {
$prospectName = $this->contact->name;
} elseif ($this->account_id) {
$prospectName = $this->account->name;
}
return $prospectName;
}
public function getProspectName(): ?string
{
/** @var string|null */
return $this->getAttribute('prospect_name');
}
/**
* Get activity title depending on prospect or title
*/
public function getActivityTitleAttribute(): ?string
{
$activityTitle = null;
if ($this->prospect && $this->prospect->getName()) {
if ($this->account_id) {
$activityTitle = $this->account->name;
} elseif ($this->lead_id) {
$activityTitle = $this->lead->company;
} elseif ($this->contact_id) {
$activityTitle = $this->contact->account ? $this->contact->account->name : $this->contact->name;
}
} elseif ($this->title) {
$activityTitle = $this->title;
}
return $activityTitle;
}
public function wasRecentlyCreated(): bool
{
return $this->wasRecentlyCreated;
}
public function getProspectTypeAttribute()
{
$prospectType = null;
if ($this->lead_id) {
$prospectType = 'Lead';
} elseif ($this->contact_id) {
$prospectType = 'Contact';
} elseif ($this->account_id) {
$prospectType = 'Account';
}
return $prospectType;
}
/**
* Return the best match for prospect. Results are in the following order of priority:
* 1. Lead
* 2. Contact
* 3. Account
* 4. NULL
*/
public function getProspectAttribute(): ?ProspectInterface
{
if ($this->hasLead()) {
return $this->getLead();
}
if ($this->hasContact()) {
return $this->getContact();
}
if ($this->hasAccount()) {
return $this->getAccount();
}
return null;
}
public function getTitleAttribute($value): ?string
{
return \getActivityTitleAttribute(
$this->user->name,
$this->getType(),
$value,
$this->prospect->name ?? null,
$this->from->national_phone_number ?? null
);
}
public function getTitle(): ?string
{
return $this->getAttribute('title');
}
public function getSummary(): ?string
{
return $this->getAttribute('summary');
}
public function isInternal(): bool
{
return $this->getAttribute('is_internal');
}
public function getIsPrivate(): bool
{
return $this->getAttribute('is_private');
}
public function getDescription(): ?string
{
return $this->getAttribute('description');
}
public function hasTitle(): bool
{
return $this->getOriginal('title') !== null;
}
public function getPlayCountAttribute()
{
return $this->getPlaysCountAttribute();
}
public function getPlaysCountAttribute()
{
if (! isset($this->attributes['plays_count'])) {
$this->loadCount('plays');
}
return $this->attributes['plays_count'];
}
public function getCommentCountAttribute()
{
return $this->getCommentsCountAttribute();
}
public function getCommentsCountAttribute()
{
if (! isset($this->attributes['comments_count'])) {
$this->loadCount('comments');
}
return $this->attributes['comments_count'];
}
public function getVisibleCommentsCountAttribute()
{
if (! isset($this->attributes['visible_comments_count'])) {
$activityCommentsService = app(ActivityCommentService::class);
$user = Auth::user() instanceof User ? Auth::user() : null;
$this->attributes['visible_comments_count'] = $activityCommentsService
->getVisibleCommentsCount($this, $user);
}
return $this->attributes['visible_comments_count'];
}
public function getShareCountAttribute()
{
return $this->getSharesCountAttribute();
}
public function getSharesCountAttribute()
{
if (! isset($this->attributes['shares_count'])) {
$this->loadCount('shares');
}
return $this->attributes['shares_count'];
}
/**
* Get the count of favorites playlists this activity appears in
*/
public function getFavoriteCountAttribute(): int
{
return $this->getFavoritesCountAttribute();
}
public function getFavoritesCountAttribute()
{
if (! isset($this->attributes['favorites_count'])) {
$this->loadCount('favorites');
}
return $this->attributes['favorites_count'];
}
public function getActiveParticipantsCountAttribute()
{
if (! isset($this->attributes['active_participants_count'])) {
$this->loadCount('activeParticipants');
}
return $this->attributes['active_participants_count'];
}
public function getTracksWithTelephonyCountAttribute()
{
if (! isset($this->attributes['tracks_with_telephony_count'])) {
$this->loadCount('tracksWithTelephony');
}
return $this->attributes['tracks_with_telephony_count'];
}
/**
* @TEMP
* $this->loadCount('tracksWithTelephony') throws null pointer exception
*/
public function countTracksWithTelephony(): int
{
return $this->tracks()->whereNotNull('telephony_provider_id')->count();
}
public function getDuration(): float
{
return $this->getAttribute('duration');
}
public function getDurationForHumansAttribute()
{
return Carbon::now()->subSeconds($this->duration)->diffForHumans(now(), true);
}
public function getDurationForHumansShortAttribute(): string
{
return Carbon::now()->subSeconds($this->duration)->diffForHumans(now(), true, true);
}
public function hasRecordingPreference(): bool
{
ret...
|
86374
|
NULL
|
NULL
|
NULL
|
|
86375
|
2962
|
0
|
2026-05-28T14:05:47.697645+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-28/1779 /Users/lukas/.screenpipe/data/data/2026-05-28/1779977147697_m1.jpg...
|
PhpStorm
|
faVsco.js – CrmActivityService.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
#12121 on JY-20963-fix-im Project: faVsco.js, menu
#12121 on JY-20963-fix-import-on-deleted-entity, menu
Start Listening for PHP Debug Connections
ServiceTest
Run 'ServiceTest'
Debug 'ServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"#12121 on JY-20963-fix-import-on-deleted-entity, menu","depth":5,"on_screen":true,"help_text":"Pull request #12121 exists for current branch JY-20963-fix-import-on-deleted-entity, but pull request details loading failed","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"ServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'ServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'ServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
5582423643801155883
|
-8994321436789994556
|
click
|
hybrid
|
NULL
|
Project: faVsco.js, menu
#12121 on JY-20963-fix-im Project: faVsco.js, menu
#12121 on JY-20963-fix-import-on-deleted-entity, menu
Start Listening for PHP Debug Connections
ServiceTest
Run 'ServiceTest'
Debug 'ServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
IAlIAlActivity MonitorFileEditViewWindowHelpActivity MonitorAll ProcessesProcess Name% CPUPhpStormkernel_taskVirtual Machine Service for DockerreplaydscreenpipeWindowServerlaunchservicesdFirefoxCP Isolated Web ContentiTerm2coreaudiodSlack HelperlaunchdSlack Helper (Renderer)Activity MonitorWispr Flow Helper (Renderer)language_server_macos_armFirefoxFirefoxFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentSlackFirefoxCP Isolated Web ContentWispr FlowWispr FlowFirefoxCP Isolated Web ContentDockerBitwardenNotion Helper (Renderer)235,8196,391,062,330,122,914,59,75,65,24,84,24,24,13,83,13,02,72,52,52,12,12,11,81,81,61,61,6CPU Time2:30:04,9522:44:46,431:50:34,535:41:18,193:34:22,167:49:42,691:00:00,0333:30,651:03:23,7556:56,3214:20,2518:34,0457:19,516:42,293:42,317:18,0614:01,091:46:41,8115:10,7923:19,0613:12,863:31,322:34,121:07,1340:30,831:24,592:02,7523:55,28System:User:Idle:Threads % C8• Thu 28 May 17:05:47CPUMemoryEnergyDiskNetworkIdle Wake-UpsKind% GPU AppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleApple43,09%53,51%3,40%7,90,00,00,00,031,70,00,00,30,03,10,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,0CPU LOADThreads:Processes:SearchGPU Time3:1PID (ah)Userlukasrootlukaslukaslukas_windowserverrootlukaslukas_coreaudiod...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
86374
|
2963
|
0
|
2026-05-28T14:05:47.598789+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-28/1779 /Users/lukas/.screenpipe/data/data/2026-05-28/1779977147598_m2.jpg...
|
PhpStorm
|
faVsco.js – CrmActivityService.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
rapstomViewCoocWindowFV faVsco.|s ~#12121 on JY-20 rapstomViewCoocWindowFV faVsco.|s ~#12121 on JY-20963-fx-l>mutkeWebhookc Batchsynceo ecror.orrcRatchSuncRedeCanrce.o© Clent.phoc eoseddeaistarcosermieuDealFieldsService.pho© DecorateActivity.phgoreaochinidons.oneorieiolyecconvere.on(© HubspotClientinterface.pl© HubspotTokenManager.ph© PayloadBuilder.phoc) Remotcermocrec.wan .oulResponseNormalize.phoc) service.ono© SyncFieidAction.phoc Suncrelateohcwiywanx© WebhookSvncBatchProceuntearation4ooListeners•lMeradataMotaten→p nedrivesalestoes• FaldeMi Onnortun tMatchenOpportunitySyncStrategyProspectSearchStrategy• Ih SenniceTrairdeeant ohd© DecorateActivity.phpDeleteObiectsTrait.phpe Faldnafoitione mho© PayloadBuilder.pho(©) Profile.pho© QueryBuilder.php© QueryHandler.pho© Querviterator.ohc© QueryResuits.pho© Service.php© SyncBatchRedisService.otla Traitsg Basecllent.phpCBaseService.choc countrvcoderesolver ohdCrmActivitvProviderintegrateClermciv tsarioe.ooo= custom.logSF [minny@localhost)Service.phpwalcnaedviycimDalo.ongo CimaeumyociceoroeC Team.phd(© JiminnyDebugCommand.phpA console (EUTA console [STAGING"closs cinaczrzc suryeee9232&32898clermeontautatoncett docce109CTermobiactshass veroh(@ nelauttDrosnantSearchStrateA2УSAyouoere Tuncczon uobdteulUardScraceoy = oerclasssorospec.searchstraceay=700if (SrenoteScarch) €tryScrnServico = Sthis->teanCraResolver->resolveForTean(Stean) :} catch (SocialAccountTokenInvalidException)Sthisoslogger-swarnina(+fCondotivitvSenvicel CPM token expired, fallling back789activity_id' => Sactivity->getidO,team_1d => Stean-›getido.Srecords = Sthis->updatePartichpantsCrnData(tean: Stean.activity: Sactivity.participants: Sparticipantscraservace. soinserwaceПЕЕЕТОТEВЕ!if ( empty(Srecords)) 4Sactivity->updateActivityCrnData(Srecords)* soaren collece oncPare cinanio Soare choant*ethrows Exceptton* Sreturn erroustLeadinuteAccount/null,Monontuns tulnittContact louth.ade louhestesnalnuth*lenneuta.private function updateParticipantsCraData0Tean SteanimiamyAND a.created at > DATE SUB(NOWO, INTERVAL 38 DAY)GROUP BY u.id, u.enail, u.name, u.softphone nunbenORDER BY sms count DESC045 A1 A41 Y 66 A 1select * from teans where 1d = 1select * fron rolesSELECTCONCAT(u.1d, CASE WHEN u.1d = t.owner id THEN • (ouner)' ELSE •• END) AS user idt.oanerid FROM social accounts s.JOIN users u on u.id = sa.sociable_idJOIN teans t 1n<->1: on t.id = u.tean_idTHERE u,tean sid = 1117 and saroroviden = "hubspot*.SELECT * FROM activities WHERE uuid_to_bin('8024fffb-2df7-4017-91f4-d9f896850248') = uuid; # 79933459 YESSELECT + FPOM activitios THERE unid to binf+[CREDIT_CARD]-9276-464d22-8185e*) = uusde # 80186192 NGSELECT * FROM crn_configurations WHERE id = 1053:SSLECT + FP0M teansTHERE SOTIEIYselect * fromThere fdl- 39249sellect * rono awhooks here oi shalawbook cateoonses where 5d= 43783%select * fron playbook_categories where playbook_id = 5473select * fron crn_field_values where crm_field_id = 659242SELECT * FROM crn_field_data fo• JOIN cra_fields f ON fd.crn_field_id = f.jd# JOIN activities a ON fd.activity id = a.10WHERE actávity_$d = 79933459PAlRAtA ChAe WN ANAUS ThARA AMMAIAA 10004-06011select * fron activities where userid IN (7168, 18688) and created at > *2826-85-22' order by id descaselect * fron users where tean_id = 1 and id IN (18688. 13934, 7160)=select * fron activities where user id = 7169 order by id desc Linit 10:select * fron usens where nane Like 'ySubrax": # 31954. 1117III 1activity sparches where usen 1d= 31954sparch &ilters where actiuity seanch ia TN (9998), BROR)):ServiceTestTO0У L7Thu 28 May 17:05:47+0.Cecsdales OrcnnworeebeionhDOCERIaLLES PCoCITORCiROn TACERLD DOCLAIALYTsactv ty-suocatel"opportun tyd nul"stane id a nulia:zb. Trigger renatch (this is KaY.sdispatcher-odispatch(new DetachActivity0bject(Sactivity, Crm0bject::OPPORTUNETY)):step 3: kematchAcuvityoncrmobtecibetach Listener Run:tondw/Users/luxas/1Bus: : chain(ltach.pho:70-odnew natchacuivicyroatalacavicylo: sacavity-sgeczol, resotesearch: tatse)lStepnew CheckAndRetrReeoteMatch(actvityld: Sactvity-soetdO), creobfecte Screbbfect). II Steo 9SIAn h.MOtAhAMWNCWANNIN TAN GYAMINS// @/Users/lukas/1iminny/app/app/Jobs/Crn/MatchActivityCrmData.phpawye hoyroosorves hooydethsesacelvvoKhKNRwwwtawonecontacacwirw.cthisesrcordsearthStep 5: CrmActivityService:updatcCrmData() Calledpublic function updatecrabata (Activity Sactivity, bool SremoteSearch = false): voigsrecords = sthis-supdateParticipantsCrnData(team: Steam, activity: sactivity, ...)1f (: capty(Srecords)) 4Sactiwty-ouodareAcivwemoataSrecords woites FXtolokSteo 6: update Partic oantscrmbatall Finds Deleted Ooportunity// @/Users/lukas/jiminny/app/app/Services/Crm/CrmActivityService.php: 109-189prevare vunccion uposterande pontserdotol..e ortavKeeThies*4 space...
|
NULL
|
4456068514041059925
|
NULL
|
click
|
ocr
|
NULL
|
rapstomViewCoocWindowFV faVsco.|s ~#12121 on JY-20 rapstomViewCoocWindowFV faVsco.|s ~#12121 on JY-20963-fx-l>mutkeWebhookc Batchsynceo ecror.orrcRatchSuncRedeCanrce.o© Clent.phoc eoseddeaistarcosermieuDealFieldsService.pho© DecorateActivity.phgoreaochinidons.oneorieiolyecconvere.on(© HubspotClientinterface.pl© HubspotTokenManager.ph© PayloadBuilder.phoc) Remotcermocrec.wan .oulResponseNormalize.phoc) service.ono© SyncFieidAction.phoc Suncrelateohcwiywanx© WebhookSvncBatchProceuntearation4ooListeners•lMeradataMotaten→p nedrivesalestoes• FaldeMi Onnortun tMatchenOpportunitySyncStrategyProspectSearchStrategy• Ih SenniceTrairdeeant ohd© DecorateActivity.phpDeleteObiectsTrait.phpe Faldnafoitione mho© PayloadBuilder.pho(©) Profile.pho© QueryBuilder.php© QueryHandler.pho© Querviterator.ohc© QueryResuits.pho© Service.php© SyncBatchRedisService.otla Traitsg Basecllent.phpCBaseService.choc countrvcoderesolver ohdCrmActivitvProviderintegrateClermciv tsarioe.ooo= custom.logSF [minny@localhost)Service.phpwalcnaedviycimDalo.ongo CimaeumyociceoroeC Team.phd(© JiminnyDebugCommand.phpA console (EUTA console [STAGING"closs cinaczrzc suryeee9232&32898clermeontautatoncett docce109CTermobiactshass veroh(@ nelauttDrosnantSearchStrateA2УSAyouoere Tuncczon uobdteulUardScraceoy = oerclasssorospec.searchstraceay=700if (SrenoteScarch) €tryScrnServico = Sthis->teanCraResolver->resolveForTean(Stean) :} catch (SocialAccountTokenInvalidException)Sthisoslogger-swarnina(+fCondotivitvSenvicel CPM token expired, fallling back789activity_id' => Sactivity->getidO,team_1d => Stean-›getido.Srecords = Sthis->updatePartichpantsCrnData(tean: Stean.activity: Sactivity.participants: Sparticipantscraservace. soinserwaceПЕЕЕТОТEВЕ!if ( empty(Srecords)) 4Sactivity->updateActivityCrnData(Srecords)* soaren collece oncPare cinanio Soare choant*ethrows Exceptton* Sreturn erroustLeadinuteAccount/null,Monontuns tulnittContact louth.ade louhestesnalnuth*lenneuta.private function updateParticipantsCraData0Tean SteanimiamyAND a.created at > DATE SUB(NOWO, INTERVAL 38 DAY)GROUP BY u.id, u.enail, u.name, u.softphone nunbenORDER BY sms count DESC045 A1 A41 Y 66 A 1select * from teans where 1d = 1select * fron rolesSELECTCONCAT(u.1d, CASE WHEN u.1d = t.owner id THEN • (ouner)' ELSE •• END) AS user idt.oanerid FROM social accounts s.JOIN users u on u.id = sa.sociable_idJOIN teans t 1n<->1: on t.id = u.tean_idTHERE u,tean sid = 1117 and saroroviden = "hubspot*.SELECT * FROM activities WHERE uuid_to_bin('8024fffb-2df7-4017-91f4-d9f896850248') = uuid; # 79933459 YESSELECT + FPOM activitios THERE unid to binf+[CREDIT_CARD]-9276-464d22-8185e*) = uusde # 80186192 NGSELECT * FROM crn_configurations WHERE id = 1053:SSLECT + FP0M teansTHERE SOTIEIYselect * fromThere fdl- 39249sellect * rono awhooks here oi shalawbook cateoonses where 5d= 43783%select * fron playbook_categories where playbook_id = 5473select * fron crn_field_values where crm_field_id = 659242SELECT * FROM crn_field_data fo• JOIN cra_fields f ON fd.crn_field_id = f.jd# JOIN activities a ON fd.activity id = a.10WHERE actávity_$d = 79933459PAlRAtA ChAe WN ANAUS ThARA AMMAIAA 10004-06011select * fron activities where userid IN (7168, 18688) and created at > *2826-85-22' order by id descaselect * fron users where tean_id = 1 and id IN (18688. 13934, 7160)=select * fron activities where user id = 7169 order by id desc Linit 10:select * fron usens where nane Like 'ySubrax": # 31954. 1117III 1activity sparches where usen 1d= 31954sparch &ilters where actiuity seanch ia TN (9998), BROR)):ServiceTestTO0У L7Thu 28 May 17:05:47+0.Cecsdales OrcnnworeebeionhDOCERIaLLES PCoCITORCiROn TACERLD DOCLAIALYTsactv ty-suocatel"opportun tyd nul"stane id a nulia:zb. Trigger renatch (this is KaY.sdispatcher-odispatch(new DetachActivity0bject(Sactivity, Crm0bject::OPPORTUNETY)):step 3: kematchAcuvityoncrmobtecibetach Listener Run:tondw/Users/luxas/1Bus: : chain(ltach.pho:70-odnew natchacuivicyroatalacavicylo: sacavity-sgeczol, resotesearch: tatse)lStepnew CheckAndRetrReeoteMatch(actvityld: Sactvity-soetdO), creobfecte Screbbfect). II Steo 9SIAn h.MOtAhAMWNCWANNIN TAN GYAMINS// @/Users/lukas/1iminny/app/app/Jobs/Crn/MatchActivityCrmData.phpawye hoyroosorves hooydethsesacelvvoKhKNRwwwtawonecontacacwirw.cthisesrcordsearthStep 5: CrmActivityService:updatcCrmData() Calledpublic function updatecrabata (Activity Sactivity, bool SremoteSearch = false): voigsrecords = sthis-supdateParticipantsCrnData(team: Steam, activity: sactivity, ...)1f (: capty(Srecords)) 4Sactiwty-ouodareAcivwemoataSrecords woites FXtolokSteo 6: update Partic oantscrmbatall Finds Deleted Ooportunity// @/Users/lukas/jiminny/app/app/Services/Crm/CrmActivityService.php: 109-189prevare vunccion uposterande pontserdotol..e ortavKeeThies*4 space...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
86373
|
NULL
|
0
|
2026-05-28T14:05:39.962909+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-28/1779 /Users/lukas/.screenpipe/data/data/2026-05-28/1779977139962_m1.jpg...
|
PhpStorm
|
faVsco.js – CrmActivityService.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
#12121 on JY-20963-fix-im Project: faVsco.js, menu
#12121 on JY-20963-fix-import-on-deleted-entity, menu
Start Listening for PHP Debug Connections
ServiceTest
Run 'ServiceTest'
Debug 'ServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
22
5
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Crm;
use Illuminate\Support\Collection;
use Jiminny\Contracts\Repositories\TeamRepository;
use Jiminny\Contracts\Services\Crm\ServiceInterface;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Models\Account;
use Jiminny\Models\Activity;
use Jiminny\Models\Contact;
use Jiminny\Models\Lead;
use Jiminny\Models\Opportunity;
use Jiminny\Models\Participant;
use Jiminny\Models\Stage;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Services\ResolveTeamCrmConnection;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Psr\Log\LoggerInterface;
use Exception;
use Throwable;
class CrmActivityService
{
public function __construct(
private readonly TeamRepository $teamRepository,
private readonly CachedCrmServiceDecorator $decorator,
private readonly EmailHelper $emailHelper,
private readonly ResolveTeamCrmConnection $teamCrmResolver,
private readonly LoggerInterface $logger,
) {
}
/**
* Updates CRM data for an activity and its participants.
*
* NOTE: This method performs multiple database writes and should be called
* within a transaction by the caller to ensure atomicity.
*
* @param Activity $activity
* @param bool $remoteSearch
*
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception
*/
public function updateCrmData(
Activity $activity,
bool $remoteSearch = false,
): void {
$crmService = null;
$participants = $activity->getParticipants();
$team = $activity->getTeam();
$prospectSearchStrategy = ProspectSearchStrategyFactory::match($team);
if ($prospectSearchStrategy->ignoreCrmMatchData()) {
$this->logger->info('[CrmActivityService] Ignoring crm data because of prospect strategy', [
'activity_id' => $activity->getId(),
'strategy' => get_class($prospectSearchStrategy),
]);
return;
}
if ($remoteSearch) {
try {
$crmService = $this->teamCrmResolver->resolveForTeam($team);
} catch (SocialAccountTokenInvalidException) {
$this->logger->warning('[CrmActivityService] CRM token expired, falling back to local search', [
'activity_id' => $activity->getId(),
'team_id' => $team->getId(),
]);
}
}
$records = $this->updateParticipantsCrmData(
team: $team,
activity: $activity,
participants: $participants,
crmService: $crmService,
);
if (! empty($records)) {
$activity->updateActivityCrmData($records);
}
$activity->refresh();
}
/**
* @param Collection<Participant> $participants
*
* @throws Exception
*
* @return array{
* Lead|null,
* Account|null,
* Opportunity|null,
* Contact|null,
* Stage|null,
* string|null
*}|array{}
*/
private function updateParticipantsCrmData(
Team $team,
Activity $activity,
Collection $participants,
?ServiceInterface $crmService = null,
): array {
$matchedRecords = [];
$matchedDomainRecords = [];
$this->validateCrmConfiguration($activity);
$this->decorator->setConfiguration($activity->getCrm());
$this->decorator->setCrmService($crmService);
foreach ($participants as $participant) {
if ($this->shouldSkipParticipant($participant)) {
continue;
}
if (! $this->shouldPerformLookup($participant, $team)) {
$this->logger->info('[CrmActivityService] Email domain belongs to the team, skipping crm lookup', [
'activity_id' => $activity->getId(),
'team_id' => $team->getId(),
'email' => $participant->getEmailAddress(),
]);
$this->attachUserIfExists($participant, $team);
continue;
}
$records = $this->findCrmRecords($participant, $activity);
if (! empty($records)) {
$matchedRecords[] = $records;
} else {
$records = $this->findCrmDomainRecords(
crmService: $crmService,
participant: $participant,
activity: $activity,
);
if (! empty($records)) {
$matchedDomainRecords[] = $records;
}
}
if (empty($records)) {
continue;
}
try {
$activity->updateParticipantCrmData($records, $participant);
} catch (Throwable $ex) {
$this->logger->error('[CrmActivityService] Failed to update participant CRM data', [
'activity_id' => $activity->getId(),
'participant_id' => $participant->getId(),
'exception' => $ex->getMessage(),
]);
continue;
}
}
$bestMatch = $this->getBestMatch(
matchedRecords : $matchedRecords,
matchedDomainRecords: $matchedDomainRecords,
);
$this->logger->info('[CrmActivityService] CRM matching completed', [
'activity_id' => $activity->getId(),
'participants_processed' => $participants->count(),
'exact_matches' => count($matchedRecords),
'domain_matches' => count($matchedDomainRecords),
'best_match_found' => ! empty($bestMatch),
]);
return $bestMatch;
}
private function shouldPerformLookup(Participant $participant, Team $team): bool
{
if ($participant->hasEmailAddress()) {
return $this->emailHelper->shouldPerformLookup($team, $participant->getEmailAddress());
}
return true;
}
private function validateCrmConfiguration(Activity $activity): void
{
if ($activity->getCrm() === null) {
throw new InvalidArgumentException('Cannot find CRM configuration');
}
}
private function getBestMatch(?array $matchedRecords, ?array $matchedDomainRecords): array
{
return RecordSelector::pickBestFromLists($matchedRecords, $matchedDomainRecords);
}
private function findCrmRecords(Participant $participant, Activity $activity): ?array
{
$records = null;
if ($participant->hasEmailAddress()) {
$records = $this->decorator->matchExactlyByEmail(
email: $participant->getEmailAddress(),
userId: $activity->getUser()->getId()
);
}
if (empty($records) && $participant->getPhoneNumber() !== null) {
$records = $this->decorator->matchByPhone(
phone: $participant->getPhoneNumber(),
userId: $activity->getUser()->getId(),
);
}
if (empty($records) && $participant->getName() !== null) {
$records = $this->decorator->matchByName(
name: $participant->getName(),
userId: $activity->getUser()->getId(),
);
}
return $records;
}
private function shouldSkipParticipant(Participant $participant): bool
{
return $participant->hasUser();
}
private function attachUserIfExists(Participant $participant, Team $team): void
{
if ($participant->hasEmailAddress() === false) {
return;
}
$user = $this->teamRepository->findActiveTeamMemberByEmail($team, $participant->getEmailAddress());
if ($user instanceof User) {
$participant->user_id = $user->getId();
$participant->save();
}
}
private function findCrmDomainRecords(
?ServiceInterface $crmService,
Participant $participant,
Activity $activity,
): array {
if ($participant->hasEmailAddress()) {
$this->decorator->setConfiguration($activity->getCrm());
$this->decorator->setCrmService($crmService);
$records = $this->decorator->matchByDomain(
email: $participant->getEmailAddress(),
userId: $activity->getUser()->getId()
);
if (! empty($records)) {
return $records;
}
}
return [];
}
}...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"#12121 on JY-20963-fix-import-on-deleted-entity, menu","depth":5,"on_screen":true,"help_text":"Pull request #12121 exists for current branch JY-20963-fix-import-on-deleted-entity, but pull request details loading failed","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"ServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'ServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'ServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"22","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"5","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Crm;\n\nuse Illuminate\\Support\\Collection;\nuse Jiminny\\Contracts\\Repositories\\TeamRepository;\nuse Jiminny\\Contracts\\Services\\Crm\\ServiceInterface;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Exceptions\\SocialAccountTokenInvalidException;\nuse Jiminny\\Models\\Account;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Contact;\nuse Jiminny\\Models\\Lead;\nuse Jiminny\\Models\\Opportunity;\nuse Jiminny\\Models\\Participant;\nuse Jiminny\\Models\\Stage;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\ResolveTeamCrmConnection;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Psr\\Log\\LoggerInterface;\nuse Exception;\nuse Throwable;\n\nclass CrmActivityService\n{\n public function __construct(\n private readonly TeamRepository $teamRepository,\n private readonly CachedCrmServiceDecorator $decorator,\n private readonly EmailHelper $emailHelper,\n private readonly ResolveTeamCrmConnection $teamCrmResolver,\n private readonly LoggerInterface $logger,\n ) {\n }\n\n /**\n * Updates CRM data for an activity and its participants.\n *\n * NOTE: This method performs multiple database writes and should be called\n * within a transaction by the caller to ensure atomicity.\n *\n * @param Activity $activity\n * @param bool $remoteSearch\n *\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception\n */\n public function updateCrmData(\n Activity $activity,\n bool $remoteSearch = false,\n ): void {\n $crmService = null;\n $participants = $activity->getParticipants();\n $team = $activity->getTeam();\n\n $prospectSearchStrategy = ProspectSearchStrategyFactory::match($team);\n if ($prospectSearchStrategy->ignoreCrmMatchData()) {\n $this->logger->info('[CrmActivityService] Ignoring crm data because of prospect strategy', [\n 'activity_id' => $activity->getId(),\n 'strategy' => get_class($prospectSearchStrategy),\n ]);\n\n return;\n }\n\n if ($remoteSearch) {\n try {\n $crmService = $this->teamCrmResolver->resolveForTeam($team);\n } catch (SocialAccountTokenInvalidException) {\n $this->logger->warning('[CrmActivityService] CRM token expired, falling back to local search', [\n 'activity_id' => $activity->getId(),\n 'team_id' => $team->getId(),\n ]);\n }\n }\n\n $records = $this->updateParticipantsCrmData(\n team: $team,\n activity: $activity,\n participants: $participants,\n crmService: $crmService,\n );\n\n if (! empty($records)) {\n $activity->updateActivityCrmData($records);\n }\n\n $activity->refresh();\n }\n\n /**\n * @param Collection<Participant> $participants\n *\n * @throws Exception\n *\n * @return array{\n * Lead|null,\n * Account|null,\n * Opportunity|null,\n * Contact|null,\n * Stage|null,\n * string|null\n *}|array{}\n */\n private function updateParticipantsCrmData(\n Team $team,\n Activity $activity,\n Collection $participants,\n ?ServiceInterface $crmService = null,\n ): array {\n $matchedRecords = [];\n $matchedDomainRecords = [];\n\n $this->validateCrmConfiguration($activity);\n $this->decorator->setConfiguration($activity->getCrm());\n $this->decorator->setCrmService($crmService);\n\n foreach ($participants as $participant) {\n if ($this->shouldSkipParticipant($participant)) {\n continue;\n }\n\n if (! $this->shouldPerformLookup($participant, $team)) {\n $this->logger->info('[CrmActivityService] Email domain belongs to the team, skipping crm lookup', [\n 'activity_id' => $activity->getId(),\n 'team_id' => $team->getId(),\n 'email' => $participant->getEmailAddress(),\n ]);\n\n $this->attachUserIfExists($participant, $team);\n\n continue;\n }\n\n $records = $this->findCrmRecords($participant, $activity);\n\n if (! empty($records)) {\n $matchedRecords[] = $records;\n } else {\n $records = $this->findCrmDomainRecords(\n crmService: $crmService,\n participant: $participant,\n activity: $activity,\n );\n if (! empty($records)) {\n $matchedDomainRecords[] = $records;\n }\n }\n\n if (empty($records)) {\n continue;\n }\n\n try {\n $activity->updateParticipantCrmData($records, $participant);\n } catch (Throwable $ex) {\n $this->logger->error('[CrmActivityService] Failed to update participant CRM data', [\n 'activity_id' => $activity->getId(),\n 'participant_id' => $participant->getId(),\n 'exception' => $ex->getMessage(),\n ]);\n\n continue;\n }\n }\n\n $bestMatch = $this->getBestMatch(\n matchedRecords : $matchedRecords,\n matchedDomainRecords: $matchedDomainRecords,\n );\n\n $this->logger->info('[CrmActivityService] CRM matching completed', [\n 'activity_id' => $activity->getId(),\n 'participants_processed' => $participants->count(),\n 'exact_matches' => count($matchedRecords),\n 'domain_matches' => count($matchedDomainRecords),\n 'best_match_found' => ! empty($bestMatch),\n ]);\n\n return $bestMatch;\n }\n\n private function shouldPerformLookup(Participant $participant, Team $team): bool\n {\n if ($participant->hasEmailAddress()) {\n return $this->emailHelper->shouldPerformLookup($team, $participant->getEmailAddress());\n }\n\n return true;\n }\n\n private function validateCrmConfiguration(Activity $activity): void\n {\n if ($activity->getCrm() === null) {\n throw new InvalidArgumentException('Cannot find CRM configuration');\n }\n }\n\n private function getBestMatch(?array $matchedRecords, ?array $matchedDomainRecords): array\n {\n return RecordSelector::pickBestFromLists($matchedRecords, $matchedDomainRecords);\n }\n\n private function findCrmRecords(Participant $participant, Activity $activity): ?array\n {\n $records = null;\n\n if ($participant->hasEmailAddress()) {\n $records = $this->decorator->matchExactlyByEmail(\n email: $participant->getEmailAddress(),\n userId: $activity->getUser()->getId()\n );\n }\n\n if (empty($records) && $participant->getPhoneNumber() !== null) {\n $records = $this->decorator->matchByPhone(\n phone: $participant->getPhoneNumber(),\n userId: $activity->getUser()->getId(),\n );\n }\n\n if (empty($records) && $participant->getName() !== null) {\n $records = $this->decorator->matchByName(\n name: $participant->getName(),\n userId: $activity->getUser()->getId(),\n );\n }\n\n return $records;\n }\n\n private function shouldSkipParticipant(Participant $participant): bool\n {\n return $participant->hasUser();\n }\n\n private function attachUserIfExists(Participant $participant, Team $team): void\n {\n if ($participant->hasEmailAddress() === false) {\n return;\n }\n\n $user = $this->teamRepository->findActiveTeamMemberByEmail($team, $participant->getEmailAddress());\n\n if ($user instanceof User) {\n $participant->user_id = $user->getId();\n $participant->save();\n }\n }\n\n private function findCrmDomainRecords(\n ?ServiceInterface $crmService,\n Participant $participant,\n Activity $activity,\n ): array {\n if ($participant->hasEmailAddress()) {\n $this->decorator->setConfiguration($activity->getCrm());\n $this->decorator->setCrmService($crmService);\n\n $records = $this->decorator->matchByDomain(\n email: $participant->getEmailAddress(),\n userId: $activity->getUser()->getId()\n );\n if (! empty($records)) {\n return $records;\n }\n }\n\n return [];\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Crm;\n\nuse Illuminate\\Support\\Collection;\nuse Jiminny\\Contracts\\Repositories\\TeamRepository;\nuse Jiminny\\Contracts\\Services\\Crm\\ServiceInterface;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Exceptions\\SocialAccountTokenInvalidException;\nuse Jiminny\\Models\\Account;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Contact;\nuse Jiminny\\Models\\Lead;\nuse Jiminny\\Models\\Opportunity;\nuse Jiminny\\Models\\Participant;\nuse Jiminny\\Models\\Stage;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\ResolveTeamCrmConnection;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\nuse Psr\\Log\\LoggerInterface;\nuse Exception;\nuse Throwable;\n\nclass CrmActivityService\n{\n public function __construct(\n private readonly TeamRepository $teamRepository,\n private readonly CachedCrmServiceDecorator $decorator,\n private readonly EmailHelper $emailHelper,\n private readonly ResolveTeamCrmConnection $teamCrmResolver,\n private readonly LoggerInterface $logger,\n ) {\n }\n\n /**\n * Updates CRM data for an activity and its participants.\n *\n * NOTE: This method performs multiple database writes and should be called\n * within a transaction by the caller to ensure atomicity.\n *\n * @param Activity $activity\n * @param bool $remoteSearch\n *\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws Exception\n */\n public function updateCrmData(\n Activity $activity,\n bool $remoteSearch = false,\n ): void {\n $crmService = null;\n $participants = $activity->getParticipants();\n $team = $activity->getTeam();\n\n $prospectSearchStrategy = ProspectSearchStrategyFactory::match($team);\n if ($prospectSearchStrategy->ignoreCrmMatchData()) {\n $this->logger->info('[CrmActivityService] Ignoring crm data because of prospect strategy', [\n 'activity_id' => $activity->getId(),\n 'strategy' => get_class($prospectSearchStrategy),\n ]);\n\n return;\n }\n\n if ($remoteSearch) {\n try {\n $crmService = $this->teamCrmResolver->resolveForTeam($team);\n } catch (SocialAccountTokenInvalidException) {\n $this->logger->warning('[CrmActivityService] CRM token expired, falling back to local search', [\n 'activity_id' => $activity->getId(),\n 'team_id' => $team->getId(),\n ]);\n }\n }\n\n $records = $this->updateParticipantsCrmData(\n team: $team,\n activity: $activity,\n participants: $participants,\n crmService: $crmService,\n );\n\n if (! empty($records)) {\n $activity->updateActivityCrmData($records);\n }\n\n $activity->refresh();\n }\n\n /**\n * @param Collection<Participant> $participants\n *\n * @throws Exception\n *\n * @return array{\n * Lead|null,\n * Account|null,\n * Opportunity|null,\n * Contact|null,\n * Stage|null,\n * string|null\n *}|array{}\n */\n private function updateParticipantsCrmData(\n Team $team,\n Activity $activity,\n Collection $participants,\n ?ServiceInterface $crmService = null,\n ): array {\n $matchedRecords = [];\n $matchedDomainRecords = [];\n\n $this->validateCrmConfiguration($activity);\n $this->decorator->setConfiguration($activity->getCrm());\n $this->decorator->setCrmService($crmService);\n\n foreach ($participants as $participant) {\n if ($this->shouldSkipParticipant($participant)) {\n continue;\n }\n\n if (! $this->shouldPerformLookup($participant, $team)) {\n $this->logger->info('[CrmActivityService] Email domain belongs to the team, skipping crm lookup', [\n 'activity_id' => $activity->getId(),\n 'team_id' => $team->getId(),\n 'email' => $participant->getEmailAddress(),\n ]);\n\n $this->attachUserIfExists($participant, $team);\n\n continue;\n }\n\n $records = $this->findCrmRecords($participant, $activity);\n\n if (! empty($records)) {\n $matchedRecords[] = $records;\n } else {\n $records = $this->findCrmDomainRecords(\n crmService: $crmService,\n participant: $participant,\n activity: $activity,\n );\n if (! empty($records)) {\n $matchedDomainRecords[] = $records;\n }\n }\n\n if (empty($records)) {\n continue;\n }\n\n try {\n $activity->updateParticipantCrmData($records, $participant);\n } catch (Throwable $ex) {\n $this->logger->error('[CrmActivityService] Failed to update participant CRM data', [\n 'activity_id' => $activity->getId(),\n 'participant_id' => $participant->getId(),\n 'exception' => $ex->getMessage(),\n ]);\n\n continue;\n }\n }\n\n $bestMatch = $this->getBestMatch(\n matchedRecords : $matchedRecords,\n matchedDomainRecords: $matchedDomainRecords,\n );\n\n $this->logger->info('[CrmActivityService] CRM matching completed', [\n 'activity_id' => $activity->getId(),\n 'participants_processed' => $participants->count(),\n 'exact_matches' => count($matchedRecords),\n 'domain_matches' => count($matchedDomainRecords),\n 'best_match_found' => ! empty($bestMatch),\n ]);\n\n return $bestMatch;\n }\n\n private function shouldPerformLookup(Participant $participant, Team $team): bool\n {\n if ($participant->hasEmailAddress()) {\n return $this->emailHelper->shouldPerformLookup($team, $participant->getEmailAddress());\n }\n\n return true;\n }\n\n private function validateCrmConfiguration(Activity $activity): void\n {\n if ($activity->getCrm() === null) {\n throw new InvalidArgumentException('Cannot find CRM configuration');\n }\n }\n\n private function getBestMatch(?array $matchedRecords, ?array $matchedDomainRecords): array\n {\n return RecordSelector::pickBestFromLists($matchedRecords, $matchedDomainRecords);\n }\n\n private function findCrmRecords(Participant $participant, Activity $activity): ?array\n {\n $records = null;\n\n if ($participant->hasEmailAddress()) {\n $records = $this->decorator->matchExactlyByEmail(\n email: $participant->getEmailAddress(),\n userId: $activity->getUser()->getId()\n );\n }\n\n if (empty($records) && $participant->getPhoneNumber() !== null) {\n $records = $this->decorator->matchByPhone(\n phone: $participant->getPhoneNumber(),\n userId: $activity->getUser()->getId(),\n );\n }\n\n if (empty($records) && $participant->getName() !== null) {\n $records = $this->decorator->matchByName(\n name: $participant->getName(),\n userId: $activity->getUser()->getId(),\n );\n }\n\n return $records;\n }\n\n private function shouldSkipParticipant(Participant $participant): bool\n {\n return $participant->hasUser();\n }\n\n private function attachUserIfExists(Participant $participant, Team $team): void\n {\n if ($participant->hasEmailAddress() === false) {\n return;\n }\n\n $user = $this->teamRepository->findActiveTeamMemberByEmail($team, $participant->getEmailAddress());\n\n if ($user instanceof User) {\n $participant->user_id = $user->getId();\n $participant->save();\n }\n }\n\n private function findCrmDomainRecords(\n ?ServiceInterface $crmService,\n Participant $participant,\n Activity $activity,\n ): array {\n if ($participant->hasEmailAddress()) {\n $this->decorator->setConfiguration($activity->getCrm());\n $this->decorator->setCrmService($crmService);\n\n $records = $this->decorator->matchByDomain(\n email: $participant->getEmailAddress(),\n userId: $activity->getUser()->getId()\n );\n if (! empty($records)) {\n return $records;\n }\n }\n\n return [];\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false}]...
|
-5135967619275051346
|
-8454960550680315650
|
visual_change
|
accessibility
|
NULL
|
Project: faVsco.js, menu
#12121 on JY-20963-fix-im Project: faVsco.js, menu
#12121 on JY-20963-fix-import-on-deleted-entity, menu
Start Listening for PHP Debug Connections
ServiceTest
Run 'ServiceTest'
Debug 'ServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
22
5
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Crm;
use Illuminate\Support\Collection;
use Jiminny\Contracts\Repositories\TeamRepository;
use Jiminny\Contracts\Services\Crm\ServiceInterface;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Models\Account;
use Jiminny\Models\Activity;
use Jiminny\Models\Contact;
use Jiminny\Models\Lead;
use Jiminny\Models\Opportunity;
use Jiminny\Models\Participant;
use Jiminny\Models\Stage;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Services\ResolveTeamCrmConnection;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Psr\Log\LoggerInterface;
use Exception;
use Throwable;
class CrmActivityService
{
public function __construct(
private readonly TeamRepository $teamRepository,
private readonly CachedCrmServiceDecorator $decorator,
private readonly EmailHelper $emailHelper,
private readonly ResolveTeamCrmConnection $teamCrmResolver,
private readonly LoggerInterface $logger,
) {
}
/**
* Updates CRM data for an activity and its participants.
*
* NOTE: This method performs multiple database writes and should be called
* within a transaction by the caller to ensure atomicity.
*
* @param Activity $activity
* @param bool $remoteSearch
*
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception
*/
public function updateCrmData(
Activity $activity,
bool $remoteSearch = false,
): void {
$crmService = null;
$participants = $activity->getParticipants();
$team = $activity->getTeam();
$prospectSearchStrategy = ProspectSearchStrategyFactory::match($team);
if ($prospectSearchStrategy->ignoreCrmMatchData()) {
$this->logger->info('[CrmActivityService] Ignoring crm data because of prospect strategy', [
'activity_id' => $activity->getId(),
'strategy' => get_class($prospectSearchStrategy),
]);
return;
}
if ($remoteSearch) {
try {
$crmService = $this->teamCrmResolver->resolveForTeam($team);
} catch (SocialAccountTokenInvalidException) {
$this->logger->warning('[CrmActivityService] CRM token expired, falling back to local search', [
'activity_id' => $activity->getId(),
'team_id' => $team->getId(),
]);
}
}
$records = $this->updateParticipantsCrmData(
team: $team,
activity: $activity,
participants: $participants,
crmService: $crmService,
);
if (! empty($records)) {
$activity->updateActivityCrmData($records);
}
$activity->refresh();
}
/**
* @param Collection<Participant> $participants
*
* @throws Exception
*
* @return array{
* Lead|null,
* Account|null,
* Opportunity|null,
* Contact|null,
* Stage|null,
* string|null
*}|array{}
*/
private function updateParticipantsCrmData(
Team $team,
Activity $activity,
Collection $participants,
?ServiceInterface $crmService = null,
): array {
$matchedRecords = [];
$matchedDomainRecords = [];
$this->validateCrmConfiguration($activity);
$this->decorator->setConfiguration($activity->getCrm());
$this->decorator->setCrmService($crmService);
foreach ($participants as $participant) {
if ($this->shouldSkipParticipant($participant)) {
continue;
}
if (! $this->shouldPerformLookup($participant, $team)) {
$this->logger->info('[CrmActivityService] Email domain belongs to the team, skipping crm lookup', [
'activity_id' => $activity->getId(),
'team_id' => $team->getId(),
'email' => $participant->getEmailAddress(),
]);
$this->attachUserIfExists($participant, $team);
continue;
}
$records = $this->findCrmRecords($participant, $activity);
if (! empty($records)) {
$matchedRecords[] = $records;
} else {
$records = $this->findCrmDomainRecords(
crmService: $crmService,
participant: $participant,
activity: $activity,
);
if (! empty($records)) {
$matchedDomainRecords[] = $records;
}
}
if (empty($records)) {
continue;
}
try {
$activity->updateParticipantCrmData($records, $participant);
} catch (Throwable $ex) {
$this->logger->error('[CrmActivityService] Failed to update participant CRM data', [
'activity_id' => $activity->getId(),
'participant_id' => $participant->getId(),
'exception' => $ex->getMessage(),
]);
continue;
}
}
$bestMatch = $this->getBestMatch(
matchedRecords : $matchedRecords,
matchedDomainRecords: $matchedDomainRecords,
);
$this->logger->info('[CrmActivityService] CRM matching completed', [
'activity_id' => $activity->getId(),
'participants_processed' => $participants->count(),
'exact_matches' => count($matchedRecords),
'domain_matches' => count($matchedDomainRecords),
'best_match_found' => ! empty($bestMatch),
]);
return $bestMatch;
}
private function shouldPerformLookup(Participant $participant, Team $team): bool
{
if ($participant->hasEmailAddress()) {
return $this->emailHelper->shouldPerformLookup($team, $participant->getEmailAddress());
}
return true;
}
private function validateCrmConfiguration(Activity $activity): void
{
if ($activity->getCrm() === null) {
throw new InvalidArgumentException('Cannot find CRM configuration');
}
}
private function getBestMatch(?array $matchedRecords, ?array $matchedDomainRecords): array
{
return RecordSelector::pickBestFromLists($matchedRecords, $matchedDomainRecords);
}
private function findCrmRecords(Participant $participant, Activity $activity): ?array
{
$records = null;
if ($participant->hasEmailAddress()) {
$records = $this->decorator->matchExactlyByEmail(
email: $participant->getEmailAddress(),
userId: $activity->getUser()->getId()
);
}
if (empty($records) && $participant->getPhoneNumber() !== null) {
$records = $this->decorator->matchByPhone(
phone: $participant->getPhoneNumber(),
userId: $activity->getUser()->getId(),
);
}
if (empty($records) && $participant->getName() !== null) {
$records = $this->decorator->matchByName(
name: $participant->getName(),
userId: $activity->getUser()->getId(),
);
}
return $records;
}
private function shouldSkipParticipant(Participant $participant): bool
{
return $participant->hasUser();
}
private function attachUserIfExists(Participant $participant, Team $team): void
{
if ($participant->hasEmailAddress() === false) {
return;
}
$user = $this->teamRepository->findActiveTeamMemberByEmail($team, $participant->getEmailAddress());
if ($user instanceof User) {
$participant->user_id = $user->getId();
$participant->save();
}
}
private function findCrmDomainRecords(
?ServiceInterface $crmService,
Participant $participant,
Activity $activity,
): array {
if ($participant->hasEmailAddress()) {
$this->decorator->setConfiguration($activity->getCrm());
$this->decorator->setCrmService($crmService);
$records = $this->decorator->matchByDomain(
email: $participant->getEmailAddress(),
userId: $activity->getUser()->getId()
);
if (! empty($records)) {
return $records;
}
}
return [];
}
}...
|
86372
|
NULL
|
NULL
|
NULL
|
|
86372
|
2960
|
50
|
2026-05-28T14:05:33.580094+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-28/1779 /Users/lukas/.screenpipe/data/data/2026-05-28/1779977133580_m1.jpg...
|
PhpStorm
|
faVsco.js – CrmActivityService.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
#12121 on JY-20963-fix-im Project: faVsco.js, menu
#12121 on JY-20963-fix-import-on-deleted-entity, menu
Start Listening for PHP Debug Connections
ServiceTest
Run 'ServiceTest'
Debug 'ServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"#12121 on JY-20963-fix-import-on-deleted-entity, menu","depth":5,"on_screen":true,"help_text":"Pull request #12121 exists for current branch JY-20963-fix-import-on-deleted-entity, but pull request details loading failed","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"ServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'ServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'ServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
5582423643801155883
|
-8994321436789994556
|
visual_change
|
hybrid
|
NULL
|
Project: faVsco.js, menu
#12121 on JY-20963-fix-im Project: faVsco.js, menu
#12121 on JY-20963-fix-import-on-deleted-entity, menu
Start Listening for PHP Debug Connections
ServiceTest
Run 'ServiceTest'
Debug 'ServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
JAlActivity MonitorFileEditViewWindowHelpActivity MonitorAll ProcessesProcess Name% CPUkernel_taskPhpStormVirtual Machine Service for DockerscreenpipereplaydWindowServerNotion Helper (Renderer)FirefoxFirefoxCP Isolated Web ContentlaunchservicesdSlack Helper (Renderer)coreaudiodSlack HelperSlackiTerm2Activity MonitorFirefoxCP Isolated Web Contentlanguage_server_macos_armWispr Flow Helper (Renderer)FirefoxCP Isolated Web ContentNotion Calendar Helper (Renderer)Wispr FlowFirefoxCP Isolated Web ContentClaudelogdControl CentreFirefoxCP Isolated Web ContentKarabiner-Core-Service208,5158,376,165,041,334,318,512,310,19,46,35,45,14,74,54,13,83,63,22,62,52,32,21,71,71,51,41,3CPU Time22:44:26,542:29:41,791:50:25,113:34:18,165:41:11,797:49:39,3423:55,121:46:41,4933:29,6659:59,2257:19,0556:55,7614:19,7313:12,731:03:23,186:41,7915:10,517:17,713:41,9540:30,629:39,212:33,9023:18,8028:18,4215:25,9219:45,143:31,1219:48.11System:User:Idle:Threads % <78• Thu 28 May 17:05:33CPUMemoryEnergyDiskNetworkIdle Wake-UpsKind % GPUAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleApple50,13%49,28%0,59%0,012,80,00,00,046,50,00,00,00,00,00,04,20,00,40,00,00,00,00,00,00,00,00,00,00,00,00,0CPU LOADThreads:Processes:GPU Time0,000,000,000,000,040,000,000,000,000,050,000,000,000,000,000,000,000,000,000,000,040,000,00PIDlahlUserrootlukaslukaslukaslukas_windowserverlukaslukaslukasrootlukas_coreaudiodlukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukasrootlukaslukasrootSearch5 148544...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
86371
|
NULL
|
0
|
2026-05-28T14:05:31.101684+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-28/1779 /Users/lukas/.screenpipe/data/data/2026-05-28/1779977131101_m2.jpg...
|
PhpStorm
|
faVsco.js – CrmActivityService.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
oedstaONP TIERIOGOUSGECOTMIOINCO DOTSORONCATOunwod oedstaONP TIERIOGOUSGECOTMIOINCO DOTSORONCATOunwoddd oootod sourlde couh uro suleo Coohto outs((Spaosaas) Aoma i) ATOAADPCSUSO AOCAOOCATATDIKESNTEATADEдо пезеашазкатат аручеаснушя)/5000/00e/ooe/kowga35/5cm1/53950/0 /lANONSM SRUWONYRYRRO YOIS6 0915 11 (9991905395 1459590539 ОРОевк-ла,лярея ВридауимергевенедостнолРарuмареч машTuteцa:EsnaJInH M00311 0O0 00 D9 COUTANOXMANDNWSIGWONKS GONGnuceo DoUWCP ATUDNOORDDTORCATATDE• C+1e-C0:/1eMo7 nutJpioat111•(48688 25888) 12 P4 400e05 145ap40e duoym 500+695 400605 M4p9908-95662 - Py dosh dudyl soyesees neyapaoeeeenintdonsoxioLeU wOUssoyn spunosse bouy 4 790105BL 2y99 osop Py na Jopoo 89cL = PyJoSn DJoyu SoptyAgtoe 8oug + 700105- (691L 75621 88980) NI Py pue t= Pirueos auayn suash souy * 290185•SSOp Py AQ JOpJo 12C-S8-9282. < Ze Pazeaso pue (88981 8910) MI PEJOSE:soбessou"Aa5A7a06 HOBJ * 1031358S2952SSLllooa8ss soihnaroseiseaemelsmhossewiwsm uroroonn cretsooicheewenEО5А 65722666 # «РуПП В С187685896896Р-7716-2187-С9Р6-4939768810954 01 руnи зазнИ S0TENgOE W08S + 105155as stunoooe teyoos woas py Jouro tRRRERREREABARREVOADULUSTOAMOWnJud'aswuasSa10J BOJy + 109195T•TASаYI Swea MOS+ 2022E5CAVO IT OMNEE MAYOMME PENERTO EMGAYSAZULnal ejosuoodyopuemcosonaeanenobonwossno1 } Aesse :0AATATROES AaTATSOYonuestuss ouetuoturosnwose11ood •90r95990095 30009990801000999008efaspynoys doffoung osengoeJoori1PyOn • (AyAдовS мAрдow)Uoрeсnотguogaоgeлepяlen uoytoung влenyeс"*) Aesue :0OUsTNQe HoONETuTaeTANieSqUEOTOTaUeOS UOTa001100AATATROES AATATZOУSuernthoJJokw1nu buTuasInuaboasnu nonauonnnu harunauoddoene ponooep091nowve wnertJOPooxMoWUyuocrtwo cuocroeworsuocrowtor uodoorsBHs40e5t0050 deoeuewe.soecoueeoee woshieweeeeosiupepnosdniygpywieeNoowwoooswUNoTodudnus sasta 6snaopsessipor pteeounseoyoaspes ooya sinseyaano eоца зозезозло 0оцазоiрuен ло 0dyа seping ano eoya sepingpeoled oSudzueia ebesensupseasioedsola coKbeeлssuks/gunzoddo cNeerwenboouetCtotlo erddwuore.Baure<Soo1a40s8euSyoo4801ewAwwwodCionewceLOT90Touo uopyprgous oyo degeuonesuoosos eoyd sepingpeoled o"twwwwotooonhe)UOSTSASSATOInHa8 &dud suoniujaopieloPWWRCCEIRIGIPARRSSOAAYATwOmarOASUASUOE%lOATANAOATARAMNM SSPTSMOTENAS МАЛЯМШИASUdPARAWKAMANSYUSNOEuda1OnUOnnNpY-xJ-C960Z-AF UO LZIZL O• SгOOSAe AJMOOUIANIМОIСаTИ...
|
NULL
|
4809745643645372730
|
NULL
|
click
|
ocr
|
NULL
|
oedstaONP TIERIOGOUSGECOTMIOINCO DOTSORONCATOunwod oedstaONP TIERIOGOUSGECOTMIOINCO DOTSORONCATOunwoddd oootod sourlde couh uro suleo Coohto outs((Spaosaas) Aoma i) ATOAADPCSUSO AOCAOOCATATDIKESNTEATADEдо пезеашазкатат аручеаснушя)/5000/00e/ooe/kowga35/5cm1/53950/0 /lANONSM SRUWONYRYRRO YOIS6 0915 11 (9991905395 1459590539 ОРОевк-ла,лярея ВридауимергевенедостнолРарuмареч машTuteцa:EsnaJInH M00311 0O0 00 D9 COUTANOXMANDNWSIGWONKS GONGnuceo DoUWCP ATUDNOORDDTORCATATDE• C+1e-C0:/1eMo7 nutJpioat111•(48688 25888) 12 P4 400e05 145ap40e duoym 500+695 400605 M4p9908-95662 - Py dosh dudyl soyesees neyapaoeeeenintdonsoxioLeU wOUssoyn spunosse bouy 4 790105BL 2y99 osop Py na Jopoo 89cL = PyJoSn DJoyu SoptyAgtoe 8oug + 700105- (691L 75621 88980) NI Py pue t= Pirueos auayn suash souy * 290185•SSOp Py AQ JOpJo 12C-S8-9282. < Ze Pazeaso pue (88981 8910) MI PEJOSE:soбessou"Aa5A7a06 HOBJ * 1031358S2952SSLllooa8ss soihnaroseiseaemelsmhossewiwsm uroroonn cretsooicheewenEО5А 65722666 # «РуПП В С187685896896Р-7716-2187-С9Р6-4939768810954 01 руnи зазнИ S0TENgOE W08S + 105155as stunoooe teyoos woas py Jouro tRRRERREREABARREVOADULUSTOAMOWnJud'aswuasSa10J BOJy + 109195T•TASаYI Swea MOS+ 2022E5CAVO IT OMNEE MAYOMME PENERTO EMGAYSAZULnal ejosuoodyopuemcosonaeanenobonwossno1 } Aesse :0AATATROES AaTATSOYonuestuss ouetuoturosnwose11ood •90r95990095 30009990801000999008efaspynoys doffoung osengoeJoori1PyOn • (AyAдовS мAрдow)Uoрeсnотguogaоgeлepяlen uoytoung влenyeс"*) Aesue :0OUsTNQe HoONETuTaeTANieSqUEOTOTaUeOS UOTa001100AATATROES AATATZOУSuernthoJJokw1nu buTuasInuaboasnu nonauonnnu harunauoddoene ponooep091nowve wnertJOPooxMoWUyuocrtwo cuocroeworsuocrowtor uodoorsBHs40e5t0050 deoeuewe.soecoueeoee woshieweeeeosiupepnosdniygpywieeNoowwoooswUNoTodudnus sasta 6snaopsessipor pteeounseoyoaspes ooya sinseyaano eоца зозезозло 0оцазоiрuен ло 0dyа seping ano eoya sepingpeoled oSudzueia ebesensupseasioedsola coKbeeлssuks/gunzoddo cNeerwenboouetCtotlo erddwuore.Baure<Soo1a40s8euSyoo4801ewAwwwodCionewceLOT90Touo uopyprgous oyo degeuonesuoosos eoyd sepingpeoled o"twwwwotooonhe)UOSTSASSATOInHa8 &dud suoniujaopieloPWWRCCEIRIGIPARRSSOAAYATwOmarOASUASUOE%lOATANAOATARAMNM SSPTSMOTENAS МАЛЯМШИASUdPARAWKAMANSYUSNOEuda1OnUOnnNpY-xJ-C960Z-AF UO LZIZL O• SгOOSAe AJMOOUIANIМОIСаTИ...
|
86369
|
NULL
|
NULL
|
NULL
|
|
86370
|
2960
|
49
|
2026-05-28T14:05:30.579655+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-28/1779 /Users/lukas/.screenpipe/data/data/2026-05-28/1779977130579_m1.jpg...
|
PhpStorm
|
faVsco.js – CrmActivityService.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
#12121 on JY-20963-fix-im Project: faVsco.js, menu
#12121 on JY-20963-fix-import-on-deleted-entity, menu
Start Listening for PHP Debug Connections
ServiceTest
Run 'ServiceTest'
Debug 'ServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
22...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"#12121 on JY-20963-fix-import-on-deleted-entity, menu","depth":5,"on_screen":true,"help_text":"Pull request #12121 exists for current branch JY-20963-fix-import-on-deleted-entity, but pull request details loading failed","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"ServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'ServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'ServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"22","depth":4,"on_screen":true,"role_description":"text"}]...
|
-676106581489811936
|
-8706353842801636416
|
visual_change
|
hybrid
|
NULL
|
Project: faVsco.js, menu
#12121 on JY-20963-fix-im Project: faVsco.js, menu
#12121 on JY-20963-fix-import-on-deleted-entity, menu
Start Listening for PHP Debug Connections
ServiceTest
Run 'ServiceTest'
Debug 'ServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
22
JAlActivity MonitorFileEditViewWindowHelpActivity MonitorAll ProcessesProcess Name% CPUPhpStormkernel_taskVirtual Machine Service for DockerreplaydWindowServerscreenpipeFirefoxCP Isolated Web ContentFirefoxiTerm2coreaudiodSlack HelperClaudeSlack Helper (Renderer)Activity Monitorcef_server Helper (Renderer)language_server_macos_armWispr Flow Helper (Renderer)FirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentWispr FlowControl CentreKarabiner-Core-ServiceDockerBitwardenFirefoxsyspolicyd203,2186,679,559,244,719,310,07,76,15,54,94,34,24,03,53,33,02,62,62,42,32,01,71,51,51,41,41,4CPU Time2:29:33,4022:44:15,491:50:21,085:41:09,617:49:37,523:34:14,7133:29,121:46:40,841:03:22,9456:55,4714:19,4628:18,3257:18,726:41,588:10,727:17,513:41,7829:44,8615:10,3040:30,4823:18,682:33,7819:45,0619:48,041:24,332:02,6014:00,8816:31,47System:User:Idle:Threads CPUMemoryEnergyDiskNetworkIdle Wake-UpsKind% GPU430Apple AppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleApple11,30,00,00,040,50,00,00,00,30,03,70,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,047,96%51,81%0,23%CPU LOADThreads:Processes:GPU Time7:43,730,000,000,003:19:46,915:03.900,000,0419,180,006:04,590,000,000,000,000,000,000,000,000,000,000,000,040,000,000,000,010,00‹ >0alslPID Userlukasrootlukaslukas_windowserverlukaslukaslukaslukas_coreaudiodlukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukasrootlukaslukaslukasroot100% (8• Thu 28 May 17:05:30Search...
|
86368
|
NULL
|
NULL
|
NULL
|
|
86369
|
2961
|
35
|
2026-05-28T14:05:24.950734+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-28/1779 /Users/lukas/.screenpipe/data/data/2026-05-28/1779977124950_m2.jpg...
|
PhpStorm
|
faVsco.js – CrmActivityService.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
rapstomViewCoocWindowFV faVsco.|s ~#12121 on JY-20 rapstomViewCoocWindowFV faVsco.|s ~#12121 on JY-20963-fx-l>mutkeWebhookc Batchsynceo ecror.orrcRatchSuncRedeCanrce.o© Clent.phoc elosedbeaistdcssermiouDealFieldsService.pho© DecorateActivity.phgoreaochinidons.oneorieiolyecconvere.on(© HubspotClientinterface.pl© HubspotTokenManager.ph© PayloadBuilder.phoc) Remotcermocrec.wan .oulResponseNormalize.phoc) service.ono© SyncFieidAction.phoc Suncrelateohcwiywanx© WebhookSvncBatchProceuntearation4ooListeners→lweradatalMotaten→p nedrivesalestoes• FaldeMi Onnortun tMatchenOpportunitySyncStrategyProspectSearchStrategy• Ih SenniceTrairdeCant nhr© DecorateActivity.phpDeleteObiectsTrait.phpe Faldnafoitione mho© PayloadBuilder.pho(©) Profile.pho© QueryBuilder.php© QueryHandler.pho© Querviterator.ohc© QueryResuits.pho© Service.php© SyncBatchRedisService.otla Traitsg Basecllent.phpCBaseService.choc countrvcoderesolver ohdCrmActivitvProviderintegrateClermciv tsarioe.ooowalcnaedviycimDalo.ongo CimaeumyociceoroeC Team.phd(© JiminnyDebugCommand.phpcloss cinaczrzc suryeeeOSORERRNERE36858685&22228clermeontautatoncett docce109cTermebiac ccaes vero(@ nelauttDrosnantSearchStrateouoere Tuncczon uoboteulbardScraceoy = oerclasssorospec.searchstraceayif (SrenoteScarch) €try} catch (SocialAccountTokenInvalidException)Sthis-sloggen-swarnina(•(GonActivitvSerwicel CPM token exoired. falibing backactivity_id' => Sactivity->getidO,team_1d => Stean-›getido.Srecords = Sthis->updateParticipantsCrnData(tean: Stean.activity: Sactivity.participants: Sparticipantscraservace. soinserwaceif ( empty(Srecords)) 4Sactivity->updateActivityCrnData(Srecords)Kuetwwosmetresho* soaren collece oncPare cinanio Soare choant*ethrows Exceptton* Sreturn erroustLeadinuteAccount/null,Monontuns tulnittContact Inulh.ade louhestesnalnuth*lenneuta.nnfwata functtonundataPanticinantefsehatanTean SteanServiceTestTO0У L7Inu Lo mey twuoiL+0.= custom.logSF [minny@localhost)© Service.phgA console (EUTimiamy045 A1 A41 Y66 A VGROUP RY u.sd urenaitl, u.nane. M. softahone nunberORDER BY sms count DESCselect * from teans where 1d = 1select * fron rolesGGIEMCONCATu.id, CASE WHEN u.id = t.owner id THEN " (onner' ELSE ** END) AS user idu.cas?l711FЕRRAREEAREREEEEEA console [STAGING"E AURGMt.oanerid FROM social accounts saJOIN users u on u.id = sa.sociable_idJOIN teans t 1n<->1: on t.id = u.tean_idTHERE u,tean sid = 1117 and saroroviden = "hubspot*.SELECT * FROM activities WHERE uuid_to_bin('8024fffb-2df7-4017-91f4-d9f896850248') = uuid; # 79933459 YESSSLECT + FPOM activities WHERE unid to bin(+[CREDIT_CARD]-9276-464d2208185c*) = uuid: # 80186192 NdSELECT * FROM crn_configurations WHERE id = 1053:SELECT + FROM teans THERE $d = 1112select * fron users where id = 30249;select * from playbooks where 1a = s47x:select * fron playbook_categories where id = 43783;select + Eron dlavbook cateoonses whene nilavhook5d 5673select + Eron eon 6eld values where com Seid 3d = 650242)SSIEN + FROM Co 45e1d data 4.# JOIN cra fields + ON fd.crn field id = f.icAIOTN ACtvites A OM £d sctkustu 3a =a.30WHERE actávity_$d = 79933459PAlRAtA ChAe WN ANAUS ThARA AMMAIAA 10004-06011Cecsdales OrcnnworeebeionhDOCLRVAILES PCOCITTOICERON TAGERIEDY DOCLRILLYTTsactv ty-suocatel"opportun tyd nul"stane id a nulia:zb. Triacer renatch (this is KaY.)sdispatcher-odispatch(new DetachActivity0bject(Sactivity, Crn0bject::OPPORTUNETY) ):step 3: kematchAcuvityoncrmobtecibetach Listener Run:tond/Users/luxas/Bus: : chain(lnew natchacuivicyroatalacavicylo: sacavity-sgeczol, resotesearch: tatse)lStepnew CheckAndRetrReeoteMatch(actvityld: Sactvity-soetdO), creobfecte Screbbfect). II Steo 9SIAn h.MOtAhAMWCWNNt TAN GYAMItAS// @/Users/lukas/1iminny/app/app/Jobs/Crn/MatchActivityCrmData.phpawye hoyroosorves hooydethsesacelvvoKhKNRwwwtawonecontacacwirw.cthisesrcordsearthStep 5: CrmActivityService:updatcCrmData() Calledpublic function updatecrabata (Activity Sactivity, bool SremoteSearch = false): voigsrecords = sthis-supdateParticipantsCrnData(team: Steam, activity: sactivity, ...):1f (: capty(Srecords)) 4Sactiwty-ouodareAcivwemoataSrecords woites FXtolokSteo 6: update Partic oantscrmbatall Finds Deleted Ooportunity// @/Users/lukas/jiminny/app/app/Services/Crm/CrmActivityService.php: 109-189prevare vunccion uposterande pontserdotol..e ortavselect * fron accounts where team_id = 1 and nane = 'Columns':select * fron usens where nane Like '%Subrax": # 31954. 1117select + fron activity sparches where usen 1d = 319541select + fron activity sparch &iltens where actiuity seanch ia TN (9998). BROR)) :Kelwhi.%t4 space...
|
NULL
|
-3477504033457792215
|
NULL
|
click
|
ocr
|
NULL
|
rapstomViewCoocWindowFV faVsco.|s ~#12121 on JY-20 rapstomViewCoocWindowFV faVsco.|s ~#12121 on JY-20963-fx-l>mutkeWebhookc Batchsynceo ecror.orrcRatchSuncRedeCanrce.o© Clent.phoc elosedbeaistdcssermiouDealFieldsService.pho© DecorateActivity.phgoreaochinidons.oneorieiolyecconvere.on(© HubspotClientinterface.pl© HubspotTokenManager.ph© PayloadBuilder.phoc) Remotcermocrec.wan .oulResponseNormalize.phoc) service.ono© SyncFieidAction.phoc Suncrelateohcwiywanx© WebhookSvncBatchProceuntearation4ooListeners→lweradatalMotaten→p nedrivesalestoes• FaldeMi Onnortun tMatchenOpportunitySyncStrategyProspectSearchStrategy• Ih SenniceTrairdeCant nhr© DecorateActivity.phpDeleteObiectsTrait.phpe Faldnafoitione mho© PayloadBuilder.pho(©) Profile.pho© QueryBuilder.php© QueryHandler.pho© Querviterator.ohc© QueryResuits.pho© Service.php© SyncBatchRedisService.otla Traitsg Basecllent.phpCBaseService.choc countrvcoderesolver ohdCrmActivitvProviderintegrateClermciv tsarioe.ooowalcnaedviycimDalo.ongo CimaeumyociceoroeC Team.phd(© JiminnyDebugCommand.phpcloss cinaczrzc suryeeeOSORERRNERE36858685&22228clermeontautatoncett docce109cTermebiac ccaes vero(@ nelauttDrosnantSearchStrateouoere Tuncczon uoboteulbardScraceoy = oerclasssorospec.searchstraceayif (SrenoteScarch) €try} catch (SocialAccountTokenInvalidException)Sthis-sloggen-swarnina(•(GonActivitvSerwicel CPM token exoired. falibing backactivity_id' => Sactivity->getidO,team_1d => Stean-›getido.Srecords = Sthis->updateParticipantsCrnData(tean: Stean.activity: Sactivity.participants: Sparticipantscraservace. soinserwaceif ( empty(Srecords)) 4Sactivity->updateActivityCrnData(Srecords)Kuetwwosmetresho* soaren collece oncPare cinanio Soare choant*ethrows Exceptton* Sreturn erroustLeadinuteAccount/null,Monontuns tulnittContact Inulh.ade louhestesnalnuth*lenneuta.nnfwata functtonundataPanticinantefsehatanTean SteanServiceTestTO0У L7Inu Lo mey twuoiL+0.= custom.logSF [minny@localhost)© Service.phgA console (EUTimiamy045 A1 A41 Y66 A VGROUP RY u.sd urenaitl, u.nane. M. softahone nunberORDER BY sms count DESCselect * from teans where 1d = 1select * fron rolesGGIEMCONCATu.id, CASE WHEN u.id = t.owner id THEN " (onner' ELSE ** END) AS user idu.cas?l711FЕRRAREEAREREEEEEA console [STAGING"E AURGMt.oanerid FROM social accounts saJOIN users u on u.id = sa.sociable_idJOIN teans t 1n<->1: on t.id = u.tean_idTHERE u,tean sid = 1117 and saroroviden = "hubspot*.SELECT * FROM activities WHERE uuid_to_bin('8024fffb-2df7-4017-91f4-d9f896850248') = uuid; # 79933459 YESSSLECT + FPOM activities WHERE unid to bin(+[CREDIT_CARD]-9276-464d2208185c*) = uuid: # 80186192 NdSELECT * FROM crn_configurations WHERE id = 1053:SELECT + FROM teans THERE $d = 1112select * fron users where id = 30249;select * from playbooks where 1a = s47x:select * fron playbook_categories where id = 43783;select + Eron dlavbook cateoonses whene nilavhook5d 5673select + Eron eon 6eld values where com Seid 3d = 650242)SSIEN + FROM Co 45e1d data 4.# JOIN cra fields + ON fd.crn field id = f.icAIOTN ACtvites A OM £d sctkustu 3a =a.30WHERE actávity_$d = 79933459PAlRAtA ChAe WN ANAUS ThARA AMMAIAA 10004-06011Cecsdales OrcnnworeebeionhDOCLRVAILES PCOCITTOICERON TAGERIEDY DOCLRILLYTTsactv ty-suocatel"opportun tyd nul"stane id a nulia:zb. Triacer renatch (this is KaY.)sdispatcher-odispatch(new DetachActivity0bject(Sactivity, Crn0bject::OPPORTUNETY) ):step 3: kematchAcuvityoncrmobtecibetach Listener Run:tond/Users/luxas/Bus: : chain(lnew natchacuivicyroatalacavicylo: sacavity-sgeczol, resotesearch: tatse)lStepnew CheckAndRetrReeoteMatch(actvityld: Sactvity-soetdO), creobfecte Screbbfect). II Steo 9SIAn h.MOtAhAMWCWNNt TAN GYAMItAS// @/Users/lukas/1iminny/app/app/Jobs/Crn/MatchActivityCrmData.phpawye hoyroosorves hooydethsesacelvvoKhKNRwwwtawonecontacacwirw.cthisesrcordsearthStep 5: CrmActivityService:updatcCrmData() Calledpublic function updatecrabata (Activity Sactivity, bool SremoteSearch = false): voigsrecords = sthis-supdateParticipantsCrnData(team: Steam, activity: sactivity, ...):1f (: capty(Srecords)) 4Sactiwty-ouodareAcivwemoataSrecords woites FXtolokSteo 6: update Partic oantscrmbatall Finds Deleted Ooportunity// @/Users/lukas/jiminny/app/app/Services/Crm/CrmActivityService.php: 109-189prevare vunccion uposterande pontserdotol..e ortavselect * fron accounts where team_id = 1 and nane = 'Columns':select * fron usens where nane Like '%Subrax": # 31954. 1117select + fron activity sparches where usen 1d = 319541select + fron activity sparch &iltens where actiuity seanch ia TN (9998). BROR)) :Kelwhi.%t4 space...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
86368
|
2960
|
48
|
2026-05-28T14:05:24.853393+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-28/1779 /Users/lukas/.screenpipe/data/data/2026-05-28/1779977124853_m1.jpg...
|
PhpStorm
|
faVsco.js – CrmActivityService.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Activity MonitorFileEditViewWindowHelpActivity Mon Activity MonitorFileEditViewWindowHelpActivity MonitorAll ProcessesProcess Name% CPUPhpStormkernel_taskVirtual Machine Service for DockerreplaydscreenpipeWindowServerFirefoxCP Isolated Web Contentlanguage_server_macos_armlaunchservicesdcoreaudiodiTerm2Slack HelperActivity MonitorSlack Helper (Renderer)Wispr Flow Helper (Renderer)SlackFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContenttccdWispr FlowFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentsyspolicydNotion Helper (Renderer)logdFirefoxControl Centre290,3152,593,162,034,024,99,07,45,85,65,44,44,03,52,92,42,22,22,02,01,91,81,81,51,51,51,4io.kandji.KandjiAgent.ESF-Extension1,3CPU Time2:29:22,6822:44:05,651:50:16,885:41:06,483:34:13,707:49:35,1633:28,597:17,3459:58,7056:55,181:03:22,6214:19,216:41,3657:18,493:41,6213:12,4515:10,1740:30,368:35,192:33,6723:18,5629:44,7216:31,4023:54,0715:25,771:46:40,4319:44,9731:18,81System:User:Idle:Threads CPUMemoryEnergyDiskNetworkIdle Wake-UpsKind% GPU AppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleApple47.71%49,71%2,58%15,20,00,00,00,052,60,00,00,00,00,44,60,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,0CPU LOADThreads:Processes:GPU Time7:43,670,000,000,005:03,903:19:46,290,000,000,000,0019,176:04,510,000,000,000,050,000,000,000,000,000,000,000,000,000,040,040,00‹→0 allPID % C8• Thu 28 May 17:05:24SearchUserlukasrootlukaslukaslukas_windowserverlukaslukasroot_coreaudiodlukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukasrootlukasrootlukaslukasroot...
|
NULL
|
8737375369469564794
|
NULL
|
click
|
ocr
|
NULL
|
Activity MonitorFileEditViewWindowHelpActivity Mon Activity MonitorFileEditViewWindowHelpActivity MonitorAll ProcessesProcess Name% CPUPhpStormkernel_taskVirtual Machine Service for DockerreplaydscreenpipeWindowServerFirefoxCP Isolated Web Contentlanguage_server_macos_armlaunchservicesdcoreaudiodiTerm2Slack HelperActivity MonitorSlack Helper (Renderer)Wispr Flow Helper (Renderer)SlackFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContenttccdWispr FlowFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentsyspolicydNotion Helper (Renderer)logdFirefoxControl Centre290,3152,593,162,034,024,99,07,45,85,65,44,44,03,52,92,42,22,22,02,01,91,81,81,51,51,51,4io.kandji.KandjiAgent.ESF-Extension1,3CPU Time2:29:22,6822:44:05,651:50:16,885:41:06,483:34:13,707:49:35,1633:28,597:17,3459:58,7056:55,181:03:22,6214:19,216:41,3657:18,493:41,6213:12,4515:10,1740:30,368:35,192:33,6723:18,5629:44,7216:31,4023:54,0715:25,771:46:40,4319:44,9731:18,81System:User:Idle:Threads CPUMemoryEnergyDiskNetworkIdle Wake-UpsKind% GPU AppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleAppleApple47.71%49,71%2,58%15,20,00,00,00,052,60,00,00,00,00,44,60,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,0CPU LOADThreads:Processes:GPU Time7:43,670,000,000,005:03,903:19:46,290,000,000,000,0019,176:04,510,000,000,000,050,000,000,000,000,000,000,000,000,000,040,040,00‹→0 allPID % C8• Thu 28 May 17:05:24SearchUserlukasrootlukaslukaslukas_windowserverlukaslukasroot_coreaudiodlukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukasrootlukasrootlukaslukasroot...
|
NULL
|
NULL
|
NULL
|
NULL
|