143 Commits

Author SHA1 Message Date
b7e06a3408 feat: Se añade workflow a pending_task 2024-02-29 10:52:44 -05:00
9096983ffd feat(call): Se añade dominio de window usuario actual 2024-02-28 22:34:07 -05:00
660ac06c1d fix: Se agregan rutas relativas en importaciones 2024-01-31 18:29:35 +00:00
7b0bafa05f feat: Se añaden archivos __init__.py 2024-01-31 18:09:01 +00:00
4d88f9423d feat: Se agregan traducciones a configuración de operador 2024-01-30 13:16:04 -05:00
e0f3846bb5 chore: Se arregla formato 2024-01-28 17:13:10 -05:00
ef8f45267f chore: Se remueve código duplicado usando asignador genérico 2024-01-28 17:10:27 -05:00
5cc0277db9 chore: Se remueve código muerto 2024-01-28 16:56:00 -05:00
3f7b989781 Merge pull request 'Implementar asignación automática de prospecto al operador que lo creó' (#81) from automaticAssignOperatorWhoCreatedProspect#3 into 6.8
Reviewed-on: #81
2024-01-28 16:46:22 -05:00
6c31dfb64f chore(sale_opportunity): Se remueve código muerto 2024-01-28 16:41:19 -05:00
b1c3b21a9c feat(Asignación de prospectos): Auto-asignar prospectos al operador que lo creó 2024-01-28 16:36:34 -05:00
99de45dd32 Refactor(Asignación de prospectos): Se generaliza método para implementarlo en reassign_by_operator 2024-01-28 15:01:16 -05:00
950b9350cc refactor(Asignación de prospectos a operarios): Se extrae clase 2024-01-28 14:00:01 -05:00
6f69709823 refactor(Asignación de prospectos): se extrae método 2024-01-28 13:54:26 -05:00
cd0564c95f refactor: Se extraen archivos 2024-01-26 22:21:23 -05:00
4a3d178a92 WIP: Se agrega escenario de asignación de prospecto a usuario actual 2024-01-26 20:35:01 -05:00
4826687b3b Merge pull request 'fix(Entorno de desarrollo): Se ajusta versión de sao' (#80) from errorNoMuestraProspectos into 6.8
Reviewed-on: #80
2024-01-23 15:16:23 -05:00
bd62abb43f fix(Entorno de desarrollo): Se ajusta versión de sao 2024-01-23 15:12:37 -05:00
18d06374fe Merge pull request 'fix(Entorno de desarrollo): Se ajusta versión de proteus' (#79) from errorChangingContactMethod into 6.8
Reviewed-on: #79
2024-01-23 14:47:26 -05:00
d5ca112bff fix(Entorno de desarrollo): Se ajusta versión de proteus 2024-01-23 14:42:35 -05:00
d1ad40203b Merge pull request 'fix: al crear mecanismo de contacto desde prospecto no se actualiza correctamente en seguimiento de prospecto' (#77) from NoSeActualizaContactMethodEnProspectTrace_#76 into 6.8
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
Se replica error en las pruebas y se soluciona con un on_change
2023-10-20 16:19:03 -05:00
9c368809f9 fix: al crear mecanismo de contacto desde prospecto no se actualiza correctamente en seguimiento de prospecto
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/pr/woodpecker Pipeline was successful
2023-10-20 15:27:51 -05:00
e40991de02 feat: Se agregan dominios de vista de prospectos
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-22 10:39:10 -05:00
b8ba775733 chore(Tipificación del cliente): Se hacen traducciones
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-21 19:28:19 -05:00
336a266578 chore: Se hace visible rating en vista de arbol de prospectos
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-21 19:13:29 -05:00
22fb3736a7 chore(Agendación de tarea): Se agregan traducciones faltantes
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-21 19:08:24 -05:00
5cc7ef011d feat: Se agrega tipificación del cliente, closed #70
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-19 17:10:13 -05:00
72ae0cfa22 feat: Se hace posible agregar y modificar métodos de contactos desde seguimiento de prospectos, closed #72
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-19 15:56:21 -05:00
0b7d15f35b feat(Programación de tareas): Se agregan traducciones, #71
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-19 12:19:49 -05:00
8a0aa29368 feat: Se agregan estados a la vista de tareas pendientes, #71
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-19 11:57:29 -05:00
f71e6f4671 feat: Se implementa en las pruebas cierre de tarea pendientes
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-19 11:47:31 -05:00
cf874db55f chore(Task): se extrae clase a otro archivo, #71 2023-09-19 11:24:49 -05:00
e97e8fa99d chore: prueba error usuario gitea, #71
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-19 07:08:32 -05:00
5a2c5217a6 feat: Se agrega reporte de tareas pendientes, #71 2023-09-19 06:48:55 -05:00
d50cca2924 feat: Se implementa agendación de tarea en las vistas, #71
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-19 06:10:09 -05:00
c1be04d009 Merge branch '6.8' of https://gitea.onecluster.org/OneTeam/trytondo-sale_opportunity_management into 6.8
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-18 19:50:10 -05:00
214c6fc089 feat: Se implementa agendación de tarea en las pruebas, #71 2023-09-18 19:49:58 -05:00
a7bf0f2a38 Fix: Se añaden traducciones pendientes
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-12 19:29:24 -05:00
243f1123a8 Fix: se añade traduccion es administrador en usuarios
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-12 19:17:11 -05:00
faa8f35384 Fix: se añade traduccion pagina metodos de contacto
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-12 19:13:09 -05:00
78fcd0231d chore(Reasignación por prospecto): Solo se muestran los prospectos con un operario asignado, closed #68
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-10 14:07:13 -05:00
3ac393dd01 chore: Se corrige error en traducción
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-10 13:59:09 -05:00
abf0c05894 fix: Closed #65
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-09 18:14:16 -05:00
a73cd43582 prueba carpintero
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-05 18:22:37 -05:00
0ccb733dab chore: Se remueve código muerto
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2023-09-04 18:00:11 -05:00
649f610bb0 Fix: Se añade dominio en seguiemiento de prospectos por rol
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-04 17:42:24 -05:00
c32b4ce31c Fix: Se añade dominio en seguiemiento de prospectos por rol
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2023-09-04 17:39:20 -05:00
97bbcaed92 Fix: Se añade dominio en seguiemiento de prospectos por rol
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2023-09-04 17:33:37 -05:00
a1716141b0 Fix: Se añaden grupos de usuarios
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-04 13:43:20 -05:00
0c09498340 Fix: Se corrige errores de pruebas
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-04 09:53:01 -05:00
8e60272f91 Se añade grupos de usuarios
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2023-09-04 09:32:20 -05:00
4ec1d2e2cd feat: Se traducen encabezados de wizards, #63
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-02 11:26:22 -05:00
b36160c228 chore: Se remueve código muerto
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-02 11:04:50 -05:00
0460324ddf Chore: Se remueve página innecesaria
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-02 11:04:27 -05:00
33ac57175c feat: Se traducen dominios, #63
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-02 11:01:49 -05:00
732b9dbfb8 feat: Se traducen botones de wizards, #63
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-02 10:44:25 -05:00
01b5119f0b chore: Se agrega icono de reporte de llamadas
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-02 10:19:12 -05:00
eed86133f4 feat: Se agregan traducciones a selections, #63
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-31 18:56:13 -05:00
dd3cd79a1f fix: Se remueve traducción invalida
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-31 18:18:06 -05:00
766f3fbc0b feat: Gpt agrega traducciones a campos de modelos faltantes, #63
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-31 17:45:24 -05:00
2bd62e1b14 feat: Se agregan traducciones a campos de prospect, #63
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-31 17:26:07 -05:00
94321866eb feat: Se agregan traducciones a campos de prospect trace, #63
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-31 17:21:42 -05:00
f9bf0d3384 feat: Se agregan traducciones a botones, #63
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-31 17:13:30 -05:00
1bcc6e5294 chore: Se cambia enlace simbólico de documentación
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-30 21:31:00 -05:00
c9de0ab42e chore: Se cambian iconos
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2023-08-30 21:25:50 -05:00
d925fcf913 fix(Botón reabrir seguimiento): Se arreglan las evaluaciones de 'invisible'
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2023-08-30 21:19:29 -05:00
033f1aad43 feat(Prospect_Trace): Se agrega reanudación de seguimiento a las vistas, #64
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2023-08-30 19:13:28 -05:00
fd24a757fb feat: Se implementa reanudación de seguimiento en las pruebas, #64
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2023-08-30 19:00:31 -05:00
3d77cf9a98 chore: Se remueve código muerto, #64
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-30 18:31:42 -05:00
9efb29f1a1 feat(Prospect_Trace): Se agrega estado 'cerrado' las vistas, #64
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2023-08-30 18:30:11 -05:00
8c1ed18530 feat: Se implementa cierre de seguimiento en las pruebas, #64
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2023-08-30 18:04:27 -05:00
1a469a5106 feat: Se agregan traducciones (es) de menús
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-29 21:01:38 -05:00
fc70d7cb74 Fix: Se elimina campo currency_date de la vista
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-29 19:50:56 -05:00
ceb9f07aab feat: se añade dominio a campo para agendar proxima llamada
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-29 19:46:12 -05:00
bdcf78ad56 chore: En registros de llamadas se renombra 'operario asignado' por 'operario que llamó'
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-28 20:27:30 -05:00
79dd0454d6 chore(Calls): Se hace unidad de negocio de solo lectura
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-28 20:20:01 -05:00
a8c6ecaa79 feat: Se limita creación de llamadas desde otro lugar que no sea el asistente destinado a ello
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-28 20:18:28 -05:00
18413d9005 chore(Vista de prospectos): se cambia vista de menús por vista de pestañas, closed #62
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-28 20:13:16 -05:00
e0c47961bc chore: Se elimina código muerto
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-28 20:03:31 -05:00
22478c9e68 chore: se elimina código muerto
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-28 20:03:09 -05:00
8ec3e4b983 feat: Se inicia seguimiento automáticamente luego de asignar operario, closed #61
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-28 19:59:52 -05:00
543a52303c chore: Se remueve código duplicado
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-28 19:27:26 -05:00
70aa3f96d4 feat: Se agrega posible agendación de llamada en el flujo de crear una llamada, #58
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2023-08-27 20:52:42 -05:00
27d4ce8a77 feat: Se agregan vistas de reasignación de prospecto específico, #53
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-27 19:13:17 -05:00
2eb52e9dce feat: Se implementa reasignación de prospecto específico en las pruebas, #53
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-27 18:48:30 -05:00
ed07f5fcbc feat: Se hacen vistas a asistente de reasignación por operario, #53
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-27 11:39:33 -05:00
3d77056e6f feat: Se implementa en las pruebas asistene de reasignación por operario
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-27 11:13:43 -05:00
e260c70a11 Error: Solo se guarda nuevo operario en primer iteración
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2023-08-27 10:21:43 -05:00
416804983b chore: Se elimina código muerto
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-27 09:49:55 -05:00
0364110866 chore(scenario): Se usa registro de prospect_trace obtenido por el click de iniciar seguimiento
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-27 08:22:00 -05:00
27dcca74bc feat: Se crea botón para iniciar seguimiento de prospecto
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-26 20:38:40 -05:00
082d070643 chore: Se remueve menú innecesario
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2023-08-26 18:11:47 -05:00
1a1b262720 feat: Se hace posible filtrar llamadas por operario que realizó dicha llamada
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-26 17:59:08 -05:00
9067aa1446 chore: Se remueve código muerto y se da orden a las vistas, closed #54
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-26 17:35:59 -05:00
7c39fcc429 feat(ProspectTrace): Se agregan a la vista todos los contactos del prospecto 2023-08-26 16:36:32 -05:00
1ece8ae959 feat(ProspectTrace): Se agrega campo para todos los contactos del prospecto 2023-08-26 16:28:21 -05:00
05ae82839b feat: Se añade nuevo item en escala de interés, closed #57
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-24 15:18:21 -05:00
31fb4211e4 feat(AssignProspectsWizard): Se encuentran prospectos también por unidad de negocio, closed #55
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-24 15:01:39 -05:00
8fa5d20ea6 feat(Call): Se hace visible unidad de negocio, actualizandose junto con el seguimiento de prospecto, closed #56
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-24 14:11:05 -05:00
4ffb654814 feat(ProspectTrace): Se hace visible unidad de negocio, actualizandose al seleccionar prospecto, #56
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-24 13:16:21 -05:00
a33d43cb57 feat(Prospect): Se crea campo de unidad de negocio, #56
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-24 12:56:55 -05:00
3bb8ddbb2c chore: Se hace requerido prospect_trace en Call
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-22 19:03:07 -05:00
11a541a3eb chore: Se elimina código muerto
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-22 18:55:50 -05:00
06a7402f0c chore: Se da formato a formulario de asistente de creación de llamadas, closed #50
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-22 18:18:33 -05:00
f3f7493d6c chore: Se crea dominio en mecanismo de contacto, closed #48
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-22 18:11:19 -05:00
16508c5961 chore: Se agregan iconos personalizados, closed #51
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-22 17:56:02 -05:00
4449b57f60 chore: Se agregan iconos personalizados a ventana principal y wizard de creación de llamada
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-22 17:17:22 -05:00
9f8e0b96af feat: Se hace visible operario asignado desde la vista de árbol, closed #49
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-22 16:25:04 -05:00
229fa9044a chore: Se ajustan detalles en la documentación
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-22 16:14:45 -05:00
a8da18e2b6 feat: Se crea asistente para creación de llamadas a seguimiento, closed #39
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-21 12:48:25 -05:00
8d1d5d1553 fix: Se genera error cuando prospects_chunks es negativo
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-21 12:20:19 -05:00
36f074a519 Merge branch '6.8' of https://gitea.onecluster.org/OneTeam/trytondo-sale_opportunity_management into 6.8
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-21 11:59:46 -05:00
d330ac341a Se implementa wizard para crear llamadas a prospectos (en las pruebas), #39 2023-08-21 11:59:38 -05:00
1187dd6ecc Fix: Se da formato a la vista closed #46
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-21 10:18:43 -05:00
fd667a518b chore: Se hace que agendar llamada pendiente solo se pueda hacer desde el asistente
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-21 09:39:06 -05:00
b34a1e19af feat: Se crea asistente para agendar próxima llamada
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-21 09:27:48 -05:00
56550df81a Merge branch '6.8' of https://gitea.onecluster.org/OneTeam/trytondo-sale_opportunity_management into vistasWizardPendingCall 2023-08-21 09:08:53 -05:00
f0a5e30b38 feat(Wizard de asignación de prospectos): Se agrega campo para visualización de terceros encontrados según el chunk
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-21 09:05:48 -05:00
a63dd54089 Merge branch '6.8' of https://gitea.onecluster.org/OneTeam/trytondo-sale_opportunity_management into vistasWizardPendingCall
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2023-08-21 07:55:42 -05:00
3fc2db8543 chore: cambia pipeline por steps
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2023-08-20 23:04:46 +00:00
05ff4b8f8d Error registrando boton en ProspectTrace 2023-08-19 14:39:30 -05:00
881f34d582 feat: closed #44
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-19 13:01:00 -05:00
d7095e05bb chore: Se remueve código muerto
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-19 12:56:12 -05:00
c0db903ac8 Merge branch '6.8' of https://gitea.onecluster.org/OneTeam/trytondo-sale_opportunity_management into 6.8
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-19 12:45:33 -05:00
93c2f10040 Se implementa wizard para crear llamada pendiente en las pruebas, #35 2023-08-19 12:41:29 -05:00
590e604604 chore: usando imagen personalizada para plugin/matrix
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-18 20:41:09 -05:00
2c68c3bb53 chore: compatibilidad versión 1.0 woodpecker
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-18 20:34:34 -05:00
3c4e8f5a26 Feat: Se añade Wizard assign_operator_wizard a la vista
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-18 19:24:47 -05:00
51d58de12a Feat: se actualiza CONTRIBUIR.md
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-17 11:20:31 -05:00
1bb4af8f16 feat: Se añade wizard de asignación de operarios a la vista
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-16 20:49:00 -05:00
c3ad43d144 Merge branch '6.8' of https://gitea.onecluster.org/OneTeam/trytondo-sale_opportunity_management into 6.8
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-15 22:33:09 -05:00
bc4833b3cf feat: Se crea asistente para asignación de prospectos a operarios 2023-08-15 22:33:02 -05:00
1f52dcba26 chore: Se remueve código muerto 2023-08-15 16:35:44 -05:00
582c8adbd3 chore: se habilita nuevamente actualizador de cambios
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-14 21:44:04 -05:00
ca2d115e22 chore: se adiciona cliente sao 6.8
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
fixes #33
2023-08-14 21:15:53 -05:00
6d77af53ef feat: closed #36
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-14 20:32:49 -05:00
8ab3a97b3c feat: Se admite fecha y hora en llamada pendiente, closed #25
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-14 20:00:41 -05:00
0d1602fe1d chore: closed #21
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-14 19:48:46 -05:00
9b2c40d821 feat: Se hace visible agenda de llamadas en vista tree de prospect_trace, closed #31
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-13 21:05:57 -05:00
d7d5c95b29 chore: Se remueve código muerto luego de refactorizar
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-13 20:35:11 -05:00
1d38d797c5 fix: Un seguimiento a prospecto solo debe tener máximo una llamada agendada, closed #28
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-13 20:31:59 -05:00
452b97cdf9 feat: Se hace prospect_city de solo lectura
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-13 19:31:35 -05:00
87da50560a feat: Se añade vistas de estados
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-13 18:58:29 -05:00
70 changed files with 2296 additions and 473 deletions

View File

@@ -1 +1,2 @@
trytond: while true; do trytond -d ${DB_NAME} --dev -v -c $SCRIPT_DIR/trytond.cfg; done trytond: while true; do trytond -d ${DB_NAME} --dev -v -c $SCRIPT_DIR/trytond.cfg; done
monitor: python3 $SCRIPT_DIR/dev.py

View File

@@ -2,9 +2,12 @@
import sys import sys
import os import os
import logging import logging
import time
import inotify.adapters import inotify.adapters
logging.basicConfig(level=logging.INFO, stream=sys.stderr)
SRC = os.environ['SRC'] SRC = os.environ['SRC']
MODULE_NAME = os.environ['MODULE_NAME'] MODULE_NAME = os.environ['MODULE_NAME']
DB_NAME = os.environ['DB_NAME'] DB_NAME = os.environ['DB_NAME']
@@ -18,12 +21,17 @@ def _main():
for event in i.event_gen(yield_nones=False): for event in i.event_gen(yield_nones=False):
(_, type_names, path, filename) = event (_, type_names, path, filename) = event
(_, ext) = os.path.splitext(filename) (_, ext) = os.path.splitext(filename)
if 'IN_CLOSE_WRITE' not in type_names:
continue
if ext in ['.py', '.xml', '.cfg']: if ext in ['.py', '.xml', '.cfg']:
for _ in range(0, 10):
if os.system("trytond-admin -d {} -u {}".format(DB_NAME, MODULE_NAME)) != 0: if os.system("trytond-admin -d {} -u {}".format(DB_NAME, MODULE_NAME)) != 0:
print("fallo trytond-admin", file=sys.stderr) time.sleep(2)
logging.error("fallo trytond-admin")
else:
logging.info("ACTUALIZADO TRYTOND POR CAMBIO DE ARCHIVO %s", filename) logging.info("ACTUALIZADO TRYTOND POR CAMBIO DE ARCHIVO %s", filename)
break
if __name__ == '__main__': if __name__ == '__main__':

View File

@@ -7,7 +7,7 @@
[ ! -d "$SRC" ] && die "no se ubica ruta en SRC" [ ! -d "$SRC" ] && die "no se ubica ruta en SRC"
# dependencias minimas # dependencias minimas
pip3 install psycopg2 proteus inotify honcho pip3 install psycopg2 proteus==6.8 inotify honcho
# instalar dependencias de tryton desde paquete # instalar dependencias de tryton desde paquete
python3 setup.py install python3 setup.py install

View File

@@ -1,2 +1,3 @@
[web] [web]
listen = 0.0.0.0:8000 listen = 0.0.0.0:8000
root=/var/lib/trytond/www

View File

@@ -1,7 +1,6 @@
#
# variables que puedo usar? https://woodpecker-ci.org/docs/0.15/usage/environment#built-in-environment-variables # variables que puedo usar? https://woodpecker-ci.org/docs/0.15/usage/environment#built-in-environment-variables
pipeline: steps:
style: style:
image: python:3.9 image: python:3.9
commands: commands:
@@ -20,7 +19,7 @@ pipeline:
- python3 -m unittest - python3 -m unittest
notify-success: notify-success:
image: plugins/matrix image: gitea.onecluster.org/alnus/plugins/matrix:latest
settings: settings:
home_server: https://matrix.org home_server: https://matrix.org
roomid: "!oxZmsJwfvciTuLYIwg:matrix.org" roomid: "!oxZmsJwfvciTuLYIwg:matrix.org"
@@ -33,7 +32,7 @@ pipeline:
<p>${CI_COMMIT_AUTHOR}: <a href="${CI_BUILD_LINK}">Task ${CI_BUILD_NUMBER}</a> <font color="green"><b>TODO BIEN EN ${CI_REPO}</b></font></p> <p>${CI_COMMIT_AUTHOR}: <a href="${CI_BUILD_LINK}">Task ${CI_BUILD_NUMBER}</a> <font color="green"><b>TODO BIEN EN ${CI_REPO}</b></font></p>
<pre><a href="${CI_COMMIT_LINK}">${CI_COMMIT_MESSAGE}</a></pre> <pre><a href="${CI_COMMIT_LINK}">${CI_COMMIT_MESSAGE}</a></pre>
when: when:
status: [ success ] status: [success]
notify-failure: notify-failure:
image: plugins/matrix image: plugins/matrix
@@ -49,7 +48,7 @@ pipeline:
<p>${CI_COMMIT_AUTHOR}: <a href="${CI_BUILD_LINK}">Task ${CI_BUILD_NUMBER}</a> <font color="red"><b>!!!TODO MAL EN ${CI_REPO} </b></font></p> <p>${CI_COMMIT_AUTHOR}: <a href="${CI_BUILD_LINK}">Task ${CI_BUILD_NUMBER}</a> <font color="red"><b>!!!TODO MAL EN ${CI_REPO} </b></font></p>
<pre><a href="${CI_COMMIT_LINK}">${CI_COMMIT_MESSAGE}</a></pre> <pre><a href="${CI_COMMIT_LINK}">${CI_COMMIT_MESSAGE}</a></pre>
when: when:
status: [ failure ] status: [failure]
services: services:
postgres: postgres:

View File

@@ -18,12 +18,14 @@
1. iniciar entorno `rake init` 1. iniciar entorno `rake init`
2. iterar con `rake tdd` 2. iterar con `rake tdd`
3. detener el entorno `rake down` 3. detener el entorno `rake down`
4. limpiar cache `git clean -xdf`
### entorno vivo de desarrollo ### entorno vivo de desarrollo
1. iniciar entorno `rake live:up` 1. iniciar entorno `rake live:up`
2. conectar cliente tryton a `localhost:8000` usuario: `admin` y clave: `admin` 2. conectar cliente tryton a `localhost:8000` usuario: `admin` y clave: `admin`
1. si desea ver la salida de trytond usar `rake live:tail` 1. o bien usar cliente web via `http://localhost:8000`
2. si desea ver la salida de trytond usar `rake live:tail`
3. detener entorno `rake live:down` 3. detener entorno `rake live:down`
### consideraciones ### consideraciones

View File

@@ -1,4 +1,16 @@
# TOMADO DE: https://hg.tryton.org/tryton-docker/file/tip/6.6/Dockerfile
FROM node as builder-node
ENV SERIES 6.8
RUN npm install -g bower
RUN curl https://downloads.tryton.org/${SERIES}/tryton-sao-6.8.4.tgz | tar zxf - -C /
RUN cd /package && bower install --allow-root
FROM python:3.9-bullseye FROM python:3.9-bullseye
# trytond DB_CACHE requiere commandos `pg_dump` y `pg_restore` # trytond DB_CACHE requiere commandos `pg_dump` y `pg_restore`
RUN apt-get update && apt-get install -y postgresql-client RUN apt-get update && apt-get install -y postgresql-client
# TOMADO DE: https://hg.tryton.org/tryton-docker/file/tip/6.6/Dockerfile
COPY --from=builder-node /package /var/lib/trytond/www

View File

@@ -1 +1 @@
doc/index.rst ./tests/scenario_sale_opportunity_management.rst

View File

@@ -12,8 +12,8 @@ end
desc 'poblar entorno' desc 'poblar entorno'
task :init => [:up] do task :init => [:up] do
pecker = YAML.load_file(WOODPECKER_YML) pecker = YAML.load_file(WOODPECKER_YML)
['tests', 'style'].each do |pipeline| ['tests', 'style'].each do |steps|
pecker.dig('pipeline', pipeline, 'commands').grep(/install/).each do |cmd| pecker.dig('steps', steps, 'commands').grep(/install/).each do |cmd|
compose('exec', 'app.dev', cmd) compose('exec', 'app.dev', cmd)
end end
end end

View File

@@ -1,8 +1,31 @@
from trytond.pool import Pool from trytond.pool import Pool
from . import prospect
from . import prospect_trace # Prospect Core
from . import call from .core.Prospect.models.prospect import Prospect
from . import pending_call from .core.Prospect.models.contact_method import ContactMethod
from .core.Prospect.wizards.assign_operator \
import AssignOperator, AssignOperatorStart
from .core.Prospect.wizards.reassign_prospect_by_prospect \
import ReasignProspectByProspect, ReassignProspectByProspectStart
from .core.Prospect.wizards.reassign_prospect_by_operator \
import ReassignProspectByOperator, ReassignProspectByOperatorStart
# Prospect Trace Core
from .core.ProspectTrace.wizards.make_call \
import MakeCall, MakeCallAsk, MakeCallAskTask, MakeCallStart
from .core.ProspectTrace.wizards.schedule_call \
import ScheduleCall, ScheduleCallStart
from .core.ProspectTrace.models.prospect_trace \
import ProspectTrace
# Call Core
from .core.Call.models.call import Call
from .core.Call.models.pending_call import PendingCall
from .core.Call.models.pending_task import PendingTask
# Role core
from .core.Role.models.user import User
from .locations import city from .locations import city
from .locations import department from .locations import department
@@ -11,16 +34,29 @@ __all__ = ['register']
def register(): def register():
Pool.register( Pool.register(
pending_call.PendingCall, User,
call.Call, PendingCall,
Call,
PendingTask,
department.Department, department.Department,
city.City, city.City,
prospect.ContactMethod, ContactMethod,
prospect.Prospect, Prospect,
prospect_trace.ProspectTrace, ProspectTrace,
AssignOperatorStart,
ScheduleCallStart,
MakeCallStart,
MakeCallAsk,
MakeCallAskTask,
ReassignProspectByOperatorStart,
ReassignProspectByProspectStart,
module='sale_opportunity_management', type_='model') module='sale_opportunity_management', type_='model')
Pool.register( Pool.register(
ScheduleCall,
AssignOperator,
MakeCall,
ReassignProspectByOperator,
ReasignProspectByProspect,
module='sale_opportunity_management', type_='wizard') module='sale_opportunity_management', type_='wizard')
Pool.register( Pool.register(
module='sale_opportunity_management', type_='report') module='sale_opportunity_management', type_='report')

36
call.py
View File

@@ -1,36 +0,0 @@
from trytond.model import ModelSQL, ModelView, fields
from datetime import date
from .selections.interest import Interest
from .selections.call_types import CallTypes
from .selections.call_results import CallResults
class Call(ModelSQL, ModelView):
'Llamada'
__name__ = 'sale.call'
date = fields.Date('Date')
description = fields.Text('Description', strip=True)
prospect_trace = fields.Many2One('sale.prospect_trace', 'Prospect trace')
interest = fields.Selection(
Interest.get_interest_levels(), 'Interest', required=True)
call_type = fields.Selection(CallTypes.get_call_types(), 'Call type')
call_result = fields.Selection(
CallResults.get_call_results(), 'Call result')
@classmethod
def default_date(cls):
return date.today()
@fields.depends('interest', 'call_result')
def on_change_interest(self):
if self.interest:
if self.interest == '0':
self.call_result = 'missed_call'
else:
self.call_result = 'answered_call'

107
call.xml
View File

@@ -3,11 +3,28 @@
this repository contains the full copyright notices and license terms. --> this repository contains the full copyright notices and license terms. -->
<tryton> <tryton>
<data> <data>
<record model="res.group" id="group_call">
<field name="name">Calls</field>
</record>
<record model="res.group" id="group_call_admin">
<field name="name">Calls Administrator</field>
<field name="parent" ref="group_call"/>
</record>
<record model="res.user-res.group" id="user_admin_group_call">
<field name="user" ref="res.user_admin"/>
<field name="group" ref="group_call"/>
</record>
<record model="res.user-res.group" id="user_admin_group_call_admin">
<field name="user" ref="res.user_admin"/>
<field name="group" ref="group_call_admin"/>
</record>
<record model="ir.action.act_window" id="act_call_tree"> <record model="ir.action.act_window" id="act_call_tree">
<field name="name">Calls</field> <field name="name">Calls</field>
<field name="res_model">sale.call</field> <field name="res_model">sale.call</field>
<field name="domain"
eval="[('operator_who_called', '=', Eval('_user'))]" pyson="1"/>
</record> </record>
<record model="ir.ui.view" id="call_view_tree"> <record model="ir.ui.view" id="call_view_tree">
<field name="model">sale.call</field> <field name="model">sale.call</field>
<field name="type">tree</field> <field name="type">tree</field>
@@ -18,7 +35,6 @@ this repository contains the full copyright notices and license terms. -->
<field name="type">form</field> <field name="type">form</field>
<field name="name">call_form</field> <field name="name">call_form</field>
</record> </record>
<record model="ir.action.act_window.view" id="act_call_tree_view1"> <record model="ir.action.act_window.view" id="act_call_tree_view1">
<field name="sequence" eval="10"/> <field name="sequence" eval="10"/>
<field name="view" ref="call_view_tree"/> <field name="view" ref="call_view_tree"/>
@@ -29,15 +45,96 @@ this repository contains the full copyright notices and license terms. -->
<field name="view" ref="call_view_form"/> <field name="view" ref="call_view_form"/>
<field name="act_window" ref="act_call_tree"/> <field name="act_window" ref="act_call_tree"/>
</record> </record>
<menuitem <menuitem
name="Calls" name="Calls"
sequence="10" sequence="10"
id="menu_calls"/> id="menu_calls"
icon="tryton-phone"/>
<menuitem <menuitem
parent="menu_calls" parent="menu_calls"
sequence="30" sequence="30"
id="menu_calls_tree" id="menu_calls_tree"
action="act_call_tree"/> action="act_call_tree"
icon="tryton-graph"/>
<record model="ir.ui.menu-res.group" id="menu_call_group_call">
<field name="menu" ref="menu_calls_tree"/>
<field name="group" ref="group_call"/>
</record>
<record model="ir.model.access" id="access_calls">
<field name="model" search="[('model', '=', 'sale.call')]"/>
<field name="perm_read" eval="False"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_calls_calls">
<field name="model" search="[('model', '=', 'sale.call')]"/>
<field name="group" ref="group_call"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/>
<field name="perm_create" eval="True"/>
<field name="perm_delete" eval="True"/>
</record>
<record model="ir.action.act_window" id="act_pending_task_tree">
<field name="name">Pending tasks</field>
<field name="res_model">sale.pending_task</field>
<field name="domain"
eval="[If(Eval('context', {}).get('user_admin', None), (), ('prospect_trace.prospect_assigned_operator', '=', Eval('_user')))]"
pyson="1"/>
</record>
<record model="ir.ui.view" id="pending_task_view_tree">
<field name="model">sale.pending_task</field>
<field name="type">tree</field>
<field name="name">pending_task_tree</field>
</record>
<record model="ir.ui.view" id="pending_task_view_form">
<field name="model">sale.pending_task</field>
<field name="type">form</field>
<field name="name">pending_task_form</field>
</record>
<record model="ir.action.act_window.view" id="act_pending_task_tree_view1">
<field name="sequence" eval="30"/>
<field name="view" ref="pending_task_view_tree"/>
<field name="act_window" ref="act_pending_task_tree"/>
</record>
<record model="ir.action.act_window.view" id="act_pending_task_form_view1">
<field name="sequence" eval="40"/>
<field name="view" ref="pending_task_view_form"/>
<field name="act_window" ref="act_pending_task_tree"/>
</record>
<record model="ir.action.act_window.domain" id="act_task_domain_pending">
<field name="name">Pending</field>
<field name="sequence" eval="10"/>
<field name="domain" eval="[('state', '=', 'pending')]" pyson="1"/>
<field name="count" eval="True"/>
<field name="act_window" ref="act_pending_task_tree"/>
</record>
<record model="ir.action.act_window.domain" id="act_task_domain_done">
<field name="name">Done</field>
<field name="sequence" eval="20"/>
<field name="domain" eval="[('state', '=', 'done')]" pyson="1"/>
<field name="count" eval="True"/>
<field name="act_window" ref="act_pending_task_tree"/>
</record>
<record model="ir.action.act_window.domain" id="act_task_domain_all">
<field name="name">All</field>
<field name="sequence" eval="9999"/>
<field name="domain"/>
<field name="count" eval="True"/>
<field name="act_window" ref="act_pending_task_tree"/>
</record>
<menuitem
parent="menu_calls"
sequence="60"
id="menu_pending_tasks"
action="act_pending_task_tree"
icon="tryton-graph"/>
<record model="ir.model.button" id="close_task_button">
<field name="name">close_task</field>
<field name="string">Close task</field>
<field name="model" search="[('model', '=', 'sale.pending_task')]"/>
</record>
</data> </data>
</tryton> </tryton>

0
core/Call/__init__.py Normal file
View File

50
core/Call/models/call.py Normal file
View File

@@ -0,0 +1,50 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from trytond.model import ModelSQL, ModelView, fields
from datetime import date
from ....selections.interest import Interest
from ....selections.call_types import CallTypes
from ....selections.call_results import CallResults
class Call(ModelSQL, ModelView):
'Llamada'
__name__ = 'sale.call'
_order_name = 'date'
_states = {'readonly': True}
date = fields.Date('Date', states=_states)
description = fields.Text('Description', strip=True)
prospect_trace = fields.Many2One(
'sale.prospect_trace', 'Prospect trace', required=True, states=_states)
interest = fields.Selection(
Interest.get_interest_levels(), 'Interest', required=True)
call_type = fields.Selection(
CallTypes.get_call_types(), 'Call type', states=_states)
call_result = fields.Selection(
CallResults.get_call_results(),
'Call result', states=_states)
call_business_unit = fields.Selection(
[('brigade', 'Brigade'),
('optics', 'Optics'),
('equipment', 'Equipment')],
'Business unit', states=_states
)
operator_who_called = fields.Many2One(
'res.user', "Operator who called", states=_states)
@classmethod
def __setup__(cls):
super(Call, cls).__setup__()
cls._order = [
('date', 'DESC NULLS FIRST')
]
@classmethod
def default_date(cls):
return date.today()

View File

@@ -1,5 +1,6 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of # This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms. # this repository contains the full copyright notices and license terms.
from trytond.model import ModelSQL, ModelView, fields from trytond.model import ModelSQL, ModelView, fields
@@ -7,5 +8,8 @@ class PendingCall(ModelSQL, ModelView):
'Llamada pendiente a un prospecto' 'Llamada pendiente a un prospecto'
__name__ = "sale.pending_call" __name__ = "sale.pending_call"
date = fields.Date('Date', required=True) date = fields.DateTime('Date', required=True)
prospect_trace = fields.Many2One('sale.prospect_trace', 'Prospect trace')
def get_rec_name(self, name):
if self.date:
return str(self.date)

View File

@@ -0,0 +1,47 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from trytond.model import Workflow, ModelSQL, ModelView, fields
from trytond.pyson import Eval
class PendingTask(Workflow, ModelSQL, ModelView):
'Tarea a realizar a un seguimiento de prospecto'
__name__ = "sale.pending_task"
description = fields.Text(
'Description', required=True,
states={
'readonly': Eval('state') == 'done'
})
state = fields.Selection(
[('pending', 'Pending'),
('done', 'Done')],
'State')
prospect_trace = fields.Many2One(
'sale.prospect_trace', 'Prospect trace',
required=True, readonly=True)
@classmethod
def __setup__(cls):
super(PendingTask, cls).__setup__()
cls._transitions |= set((
('pending', 'done'),
))
cls._buttons.update({
'close_task': {
'invisible': ~Eval('state').in_(['pending']),
'depends':['state']
}
})
@classmethod
@ModelView.button
@Workflow.transition('done')
def close_task(cls, tasks):
pass
@classmethod
def default_state(cls):
return 'pending'

View File

View File

View File

@@ -0,0 +1,37 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from trytond.model import ModelSQL, ModelView, fields
class ContactMethod(ModelSQL, ModelView):
'Mecanismo de contacto'
__name__ = 'prospect.contact_method'
contact_type = fields.Selection([
('phone', 'Phone'),
('mobile', 'Mobile'),
('mail', 'Mail')
], 'Contact type', required=True)
value = fields.Char('Value', required=True)
name = fields.Char('Name')
job = fields.Char('Job')
prospect = fields.Many2One('sale.prospect', 'Prospect', required=True)
prospect_trace = fields.Many2One(
'sale.prospect_trace', 'Prospect Trace', required=False)
@classmethod
def default_contact_type(cls):
return 'mobile'
def get_rec_name(self, name):
fields = [self.name, self.job, self.value]
contact_rec_name = ''
for field in fields:
if field:
contact_rec_name += ' [' + str(field) + '] '
return contact_rec_name

View File

@@ -0,0 +1,80 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from trytond.model import ModelSQL, ModelView, fields, DeactivableMixin
from trytond.pyson import Eval, If
from trytond.transaction import Transaction
from trytond.pool import Pool
from ....core.Prospect.wizards.assign_operator import GenericAssign
class Prospect(ModelSQL, ModelView, DeactivableMixin):
'Prospecto'
__name__ = 'sale.prospect'
_rec_name = 'name'
name = fields.Char('Name', required=True)
business_unit = fields.Selection(
[('brigade', 'Brigade'),
('optics', 'Optics'),
('equipment', 'Equipment')],
'Business unit', required=True
)
contact_methods = fields.One2Many(
'prospect.contact_method',
'prospect', 'Contact methods', required=True)
department = fields.Many2One('sale.department', 'Department')
city = fields.Many2One('sale.city', 'City',
domain=[If(Eval('department'),
('parent', '=', Eval('department')))])
assigned_operator = fields.Many2One(
'res.user', "Assigned operator", readonly=True)
state = fields.Selection([
('unassigned', 'Unsassigned'),
('assigned', 'Assigned')], "State", readonly=True)
prospect_trace = fields.Many2One('sale.prospect_trace', 'Prospect trace')
rating = fields.Selection(
[(None, None),
('1', '1'),
('2', '2'),
('3', '3'),
('4', '4'),
('5', '5')], 'Rating (1-5)')
comments = fields.Text('Comments')
@classmethod
def default_state(cls):
return 'unassigned'
@fields.depends('prospect_trace', 'contact_methods')
def on_change_contact_methods(self):
for contact in self.contact_methods:
contact.prospect_trace = self.prospect_trace
@fields.depends('city', 'department')
def on_change_city(self):
if self.city:
self.department = self.city.parent
@classmethod
def create(cls, values):
user_id = Transaction().user
records = super().create(values)
cls.try_assign_to_current_operator(records, user_id)
return records
@classmethod
def try_assign_to_current_operator(cls, prospects, user_id):
User = Pool().get('res.user')
user, = User.search([('id', '=', user_id)])
if user.is_operator:
GenericAssign.assign_prospects_to_operator(prospects, user)

View File

View File

@@ -0,0 +1,108 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from trytond.wizard import Wizard, StateView, Button, StateTransition
from trytond.model import ModelView, fields
from trytond.pyson import Eval
from trytond.pool import Pool
from ....core.ProspectTrace.models.prospect_trace import ProspectTrace
class AssignOperator(Wizard):
'Asignar operador a prospecto'
__name__ = 'sale.prospect.assign'
start = StateView(
'sale.prospect.assign.start',
'sale_opportunity_management.assign_start_view_form', [
Button("Cancel", 'end', 'tryton-cancel'),
Button("Assign", 'assign', 'tryton-ok', default=True)])
assign = StateTransition()
def transition_assign(self):
_prospects = self.start.prospects
_operator = self.start.operator
GenericAssign.assign_prospects_to_operator(_prospects, _operator)
return 'end'
class AssignOperatorStart(ModelView):
'Inicio de asignación de operador'
__name__ = 'sale.prospect.assign.start'
prospects_chunk = fields.Integer(
'Prospects chunk', required=True,
states={
'readonly': ~Eval('business_unit', False)})
operator = fields.Many2One('res.user', 'Operator', required=True)
prospects = fields.One2Many(
'sale.prospect', None, 'Prospects', readonly=True)
business_unit = fields.Selection(
[('brigade', 'Brigade'),
('optics', 'Optics'),
('equipment', 'Equipment')],
'Business unit',
states={
'readonly': Eval('prospects_chunk', False)}
)
@classmethod
def default_prospects_chunk(cls):
return 0
@fields.depends('prospects_chunk', 'prospects', 'business_unit')
def on_change_prospects_chunk(self):
pool = Pool()
Prospect = pool.get('sale.prospect')
if self.prospects_chunk >= 1:
self.prospects = []
self.prospects = Prospect.search(
[('state', '=', 'unassigned'),
('business_unit', '=', self.business_unit)],
limit=self.prospects_chunk)
class GenericAssign():
@classmethod
def assign_prospects_to_operator(cls, prospects, operator):
for prospect in prospects:
prospect.assigned_operator = operator
prospect.state = 'assigned'
prospect.prospect_trace = cls._get_prosp_trace(prospect, operator)
prospect.save()
@classmethod
def _get_prosp_trace(cls, prospect, operator):
ProspectTrace = Pool().get('sale.prospect_trace')
prospect_trace = ProspectTrace.search([('prospect', '=', prospect)])
if not prospect_trace:
created_trace = cls._create_base_prospect_trace(prospect, operator)
prospect_trace.append(created_trace)
prospect_trace, = prospect_trace
prospect_trace.prospect_assigned_operator = operator
prospect_trace.save()
return prospect_trace
@staticmethod
def _create_base_prospect_trace(prospect, operator) -> ProspectTrace:
ProspectTrace = Pool().get('sale.prospect_trace')
prospect_trace = ProspectTrace(
prospect=prospect,
prospect_city=prospect.city,
prospect_business_unit=prospect.business_unit,
prospect_contacts=prospect.contact_methods
)
return prospect_trace

View File

@@ -0,0 +1,53 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from trytond.wizard import Wizard, StateView, Button, StateTransition
from trytond.model import ModelView, fields
from trytond.pool import Pool
from ....core.Prospect.wizards.assign_operator import GenericAssign
class ReassignProspectByOperatorStart(ModelView):
'Inicio de reasignación de prospecto por operario'
__name__ = 'sale.prospect.reassign_by_operator.start'
current_operator = fields.Many2One(
'res.user', "Current operator", required=True)
new_operator = fields.Many2One(
'res.user', "New operator", required=True)
prospects = fields.One2Many(
'sale.prospect', None, 'Prospects', readonly=True)
@fields.depends('current_operator', 'prospects')
def on_change_current_operator(self):
pool = Pool()
Prospect = pool.get('sale.prospect')
self.prospects = []
self.prospects = Prospect.search(
[('state', '=', 'assigned'),
('assigned_operator', '=', self.current_operator)])
class ReassignProspectByOperator(Wizard):
'Reasignar todos los prospectos de un operario, a otro operario'
__name__ = 'sale.prospect.reassign_by_operator'
start = StateView(
'sale.prospect.reassign_by_operator.start',
'sale_opportunity_management.reassign_by_operator_start_view_form',
[Button("Cancel", 'end', 'tryton-cancel'),
Button("Reassign", 'reassign_by_operator', 'tryton-ok', default=True)
])
reassign_by_operator = StateTransition()
def transition_reassign_by_operator(self):
_prospects = self.start.prospects
_operator = self.start.new_operator
GenericAssign.assign_prospects_to_operator(_prospects, _operator)
return 'end'

View File

@@ -0,0 +1,40 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from trytond.wizard import Wizard, StateView, Button, StateTransition
from trytond.model import ModelView, fields
from ....core.Prospect.wizards.assign_operator import GenericAssign
class ReassignProspectByProspectStart(ModelView):
'Inicio de reasignación de un prospecto en específico'
__name__ = 'sale.prospect.reassign_by_prospect.start'
prospect = fields.Many2One(
'sale.prospect', 'Prospect', required=True,
domain=[('assigned_operator', '!=', None)])
new_operator = fields.Many2One('res.user', "New operator", required=True)
class ReasignProspectByProspect(Wizard):
'Reasignar un prospecto en específico a un nuevo operario'
__name__ = 'sale.prospect.reassign_by_prospect'
start = StateView(
'sale.prospect.reassign_by_prospect.start',
'sale_opportunity_management.reassign_by_prospect_start_view_form',
[Button("Cancel", 'end', 'tryton-cancel'),
Button("Reassign", 'reassign_by_prospect', 'tryton-ok', default=True)
])
reassign_by_prospect = StateTransition()
def transition_reassign_by_prospect(self):
_prospect = self.start.prospect
_operator = self.start.new_operator
GenericAssign.assign_prospects_to_operator([_prospect], _operator)
return 'end'

View File

View File

View File

@@ -0,0 +1,106 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from trytond.model import ModelSQL, ModelView, fields
from trytond.pyson import Eval
from ....selections.interest import Interest
class ProspectTrace(ModelSQL, ModelView):
'Seguimiento de un prospecto'
__name__ = 'sale.prospect_trace'
_states = {'readonly': True}
prospect = fields.Many2One(
'sale.prospect', 'Prospect', required=True, states=_states)
prospect_business_unit = fields.Selection(
[('brigade', 'Brigade'),
('optics', 'Optics'),
('equipment', 'Equipment')],
'Business unit', states=_states
)
prospect_contacts = fields.One2Many(
'prospect.contact_method', 'prospect_trace',
'Prospect contacts', required=True)
prospect_city = fields.Many2One('sale.city', 'City',
states=_states)
prospect_assigned_operator = fields.Many2One(
'res.user', "Assigned operator", states=_states)
calls = fields.One2Many(
'sale.call', 'prospect_trace', 'Calls', states=_states)
pending_call = fields.Many2One(
'sale.pending_call', 'Pending call', states=_states)
current_interest = fields.Selection(
Interest.get_interest_levels(), 'Current interest',
states=_states)
state = fields.Selection([
('unassigned', 'Unassigned'),
('open', 'Open'),
('with_pending_calls', 'With pending calls'),
('closed', 'Closed')
], 'State',
states=_states)
@fields.depends('prospect_contacts', 'prospect')
def on_change_prospect_contacts(self):
for contact in self.prospect_contacts:
contact.prospect = self.prospect
@classmethod
def __setup__(cls):
super(ProspectTrace, cls).__setup__()
cls._buttons.update({
'wizard_schedule': {
'invisible': Eval('state') == 'with_pending_calls',
},
'wizard_make_call': {},
'close_trace': {
'invisible': Eval('state') == 'closed',
'depends': ['state']
},
'reopen_trace': {
'invisible': (Eval('state') == 'open')
| (Eval('state') == 'with_pending_calls'),
'depends': ['state']
}
})
@classmethod
def default_state(cls):
return 'open'
@classmethod
@ModelView.button_action(
'sale_opportunity_management.schedule_call_wizard')
def wizard_schedule(cls, prospect_traces):
pass
@classmethod
@ModelView.button_action(
'sale_opportunity_management.make_call_wizard')
def wizard_make_call(cls, prospect_traces):
pass
@classmethod
@ModelView.button
def close_trace(cls, prospect_traces):
for prospect_trace in prospect_traces:
prospect_trace.state = 'closed'
prospect_trace.save()
@classmethod
@ModelView.button
def reopen_trace(cls, prospect_traces):
for prospect_trace in prospect_traces:
prospect_trace.state = 'open'
prospect_trace.save()
def get_rec_name(self, name):
if self.prospect:
return '[' + str(self.id) + '] ' + self.prospect.name

View File

@@ -0,0 +1,147 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from trytond.wizard import Wizard, StateView, Button, StateTransition
from trytond.model import ModelView, fields
from trytond.pool import Pool
from trytond.pyson import Eval
from ....selections.call_types import CallTypes
from ....selections.interest import Interest
from datetime import datetime
class MakeCallStart(ModelView):
'Inicio de creación de llamada a seguimiento de prospecto'
__name__ = 'sale.prospect_trace.make_call.start'
description = fields.Text('Description')
interest = fields.Selection(
Interest.get_interest_levels(), 'Interest', required=True)
schedule_call = fields.Selection(
[('yes', 'Yes'),
('no', 'No')], 'Schedule call?', required=True)
schedule_task = fields.Selection(
[('yes', 'Yes'),
('no', 'No')], 'Schedule task?', required=True)
class MakeCallAsk(ModelView):
'Posible agendación de llamada luego de hacer llamada actual'
__name__ = 'sale.prospect_trace.make_call.ask'
currency_date = fields.DateTime('Currency Date', readonly=True)
datetime = fields.DateTime('Date time', domain=[
('datetime', '>=', Eval('currency_date'))])
@classmethod
def default_currency_date(cls):
date = datetime.now()
return date
class MakeCallAskTask(ModelView):
'Posible agendación de tarea luego de hacer llamada actual'
__name__ = 'sale.prospect_trace.make_call.ask_task'
task_description = fields.Text('Task description')
class MakeCall(Wizard):
'Crear llamada a un seguimiento de prospecto'
__name__ = 'sale.prospect_trace.make_call'
start = StateView(
'sale.prospect_trace.make_call.start',
'sale_opportunity_management.make_call_start_view_form', [
Button("Cancel", 'end', 'tryton-cancel'),
Button("Make call", 'make_call', 'tryton-ok', default=True)])
make_call = StateTransition()
ask = StateView(
'sale.prospect_trace.make_call.ask',
'sale_opportunity_management.make_call_ask_view_form', [
Button("Cancel", 'end', 'tryton-cancel'),
Button(
"Schedule call", 'schedule_call', 'tryton-ok', default=True)])
schedule_call = StateTransition()
ask_task = StateView(
'sale.prospect_trace.make_call.ask_task',
'sale_opportunity_management.make_call_ask_task_view_form', [
Button("Cancel", 'end', 'tryton-cancel'),
Button("Schedule task", 'schedule_task', 'tryton-ok', default=True)
]
)
schedule_task = StateTransition()
def transition_make_call(self):
prospect_trace = self.record
pool = Pool()
Call = pool.get('sale.call')
call = Call()
call.description = self.start.description
call.interest = self.start.interest
call.prospect_trace = self.record
call.call_business_unit = self.record.prospect_business_unit
call.operator_who_called = self.record.prospect_assigned_operator
if call.interest == '0':
call.call_result = 'missed_call'
else:
call.call_result = 'answered_call'
already_exist_a_call = len(prospect_trace.calls) >= 1
if already_exist_a_call:
followup_call_type = CallTypes.get_call_types()[1][0]
call.call_type = followup_call_type
else:
first_call_type = CallTypes.get_call_types()[0][0]
call.call_type = first_call_type
call.save()
prospect_trace.current_interest = call.interest
if prospect_trace.pending_call:
prospect_trace.pending_call = None
prospect_trace.state = 'open'
prospect_trace.calls += (call,)
prospect_trace.save()
if self.start.schedule_call == 'yes':
return 'ask'
if self.start.schedule_task == 'yes':
return 'ask_task'
return 'end'
def transition_schedule_task(self):
self.create_schedule_task(self.ask_task.task_description, self.record)
return 'end'
def transition_schedule_call(self):
self.create_schedule_call(self.ask.datetime, self.record)
if (self.start.schedule_call and self.start.schedule_task) == 'yes':
return 'ask_task'
return 'end'
@classmethod
def create_schedule_task(cls, description, prospect_trace):
pool = Pool()
Task = pool.get('sale.pending_task')
task = Task()
task.description = description
task.prospect_trace = prospect_trace
task.save()
@classmethod
def create_schedule_call(cls, datetime, prospect_trace):
pool = Pool()
PendingCall = pool.get('sale.pending_call')
pending_call = PendingCall()
pending_call.date = datetime
pending_call.save()
prospect_trace.pending_call = pending_call
prospect_trace.state = 'with_pending_calls'
prospect_trace.save()

View File

@@ -0,0 +1,40 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from trytond.wizard import Wizard, StateView, Button, StateTransition
from trytond.model import ModelView, fields
from trytond.pyson import Eval
from datetime import datetime
from ....core.ProspectTrace.wizards.make_call import MakeCall
class ScheduleCallStart(ModelView):
'Inicio agendar llamada a seguimiento de prospecto'
__name__ = 'sale.prospect_trace.schedule.start'
currency_date = fields.DateTime('Currency Date', readonly=True)
date_time = fields.DateTime('Date time', domain=[
('date_time', '>=', Eval('currency_date'))])
@classmethod
def default_currency_date(cls):
date = datetime.now()
return date
class ScheduleCall(Wizard):
'Agendar llamada a seguimiento de prospecto'
__name__ = 'sale.prospect_trace.schedule'
start = StateView(
'sale.prospect_trace.schedule.start',
'sale_opportunity_management.schedule_start_view_form', [
Button("Cancel", 'end', 'tryton-cancel'),
Button("Schedule", 'schedule', 'tryton-ok', default=True)])
schedule = StateTransition()
def transition_schedule(self):
MakeCall.create_schedule_call(self.start.date_time, self.record)
return 'end'

0
core/Role/__init__.py Normal file
View File

View File

18
core/Role/models/user.py Normal file
View File

@@ -0,0 +1,18 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from trytond.model import fields
from trytond.pool import PoolMeta
class User(metaclass=PoolMeta):
"User"
__name__ = 'res.user'
user_admin = fields.Boolean('Is Admin')
is_operator = fields.Boolean('Is Operator')
@classmethod
def __setup__(cls):
super(User, cls).__setup__()
cls._context_fields.insert(0, 'user_admin')

0
core/__init__.py Normal file
View File

35
icons.xml Normal file
View File

@@ -0,0 +1,35 @@
<?xml version="1.0"?>
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<tryton>
<data>
<record model="ir.ui.icon" id="party_icon">
<field name="name">tryton-party</field>
<field name="path">icons/tryton-party.svg</field>
</record>
<record model="ir.ui.icon" id="person_icon">
<field name="name">tryton-person</field>
<field name="path">icons/tryton-person.svg</field>
</record>
<record model="ir.ui.icon" id="unnasigned_icon">
<field name="name">tryton-person-negative</field>
<field name="path">icons/tryton-person-negative.svg</field>
</record>
<record model="ir.ui.icon" id="asigned_icon">
<field name="name">tryton-person-positive</field>
<field name="path">icons/tryton-person-positive.svg</field>
</record>
<record model="ir.ui.icon" id="tel_icon">
<field name="name">tryton-tel</field>
<field name="path">icons/tryton-tel.svg</field>
</record>
<record model="ir.ui.icon" id="target_icon">
<field name="name">tryton-target</field>
<field name="path">icons/tryton-target.svg</field>
</record>
<record model="ir.ui.icon" id="call_icon">
<field name="name">tryton-phone</field>
<field name="path">icons/tryton-phone.svg</field>
</record>
</data>
</tryton>

4
icons/tryton-party.svg Normal file
View File

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"/>
<path d="M0 0h24v24H0z" fill="none"/>
</svg>

After

Width:  |  Height:  |  Size: 258 B

View File

@@ -0,0 +1,7 @@
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 20 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="#000000">
<g id="SVGRepo_bgCarrier" stroke-width="0"/>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1,7 @@
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 20 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="#000000">
<g id="SVGRepo_bgCarrier" stroke-width="0"/>

After

Width:  |  Height:  |  Size: 1.3 KiB

7
icons/tryton-person.svg Normal file
View File

@@ -0,0 +1,7 @@
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 20 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="#000000">
<g id="SVGRepo_bgCarrier" stroke-width="0"/>

After

Width:  |  Height:  |  Size: 1.1 KiB

12
icons/tryton-phone.svg Normal file
View File

@@ -0,0 +1,12 @@
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="SVGRepo_bgCarrier" stroke-width="0"/>
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"/>
<g id="SVGRepo_iconCarrier"> <path d="M15.5562 14.5477L15.1007 15.0272C15.1007 15.0272 14.0181 16.167 11.0631 13.0559C8.10812 9.94484 9.1907 8.80507 9.1907 8.80507L9.47752 8.50311C10.1841 7.75924 10.2507 6.56497 9.63424 5.6931L8.37326 3.90961C7.61028 2.8305 6.13596 2.68795 5.26145 3.60864L3.69185 5.26114C3.25823 5.71766 2.96765 6.30945 3.00289 6.96594C3.09304 8.64546 3.81071 12.259 7.81536 16.4752C12.0621 20.9462 16.0468 21.1239 17.6763 20.9631C18.1917 20.9122 18.6399 20.6343 19.0011 20.254L20.4217 18.7584C21.3806 17.7489 21.1102 16.0182 19.8833 15.312L17.9728 14.2123C17.1672 13.7486 16.1858 13.8848 15.5562 14.5477Z" fill="#267F82"/> <path d="M13.2595 1.87983C13.3257 1.47094 13.7122 1.19357 14.1211 1.25976C14.1464 1.26461 14.2279 1.27983 14.2705 1.28933C14.3559 1.30834 14.4749 1.33759 14.6233 1.38082C14.9201 1.46726 15.3347 1.60967 15.8323 1.8378C16.8286 2.29456 18.1544 3.09356 19.5302 4.46936C20.906 5.84516 21.705 7.17097 22.1617 8.16725C22.3899 8.66487 22.5323 9.07947 22.6187 9.37625C22.6619 9.52466 22.6912 9.64369 22.7102 9.72901C22.7197 9.77168 22.7267 9.80594 22.7315 9.83125L22.7373 9.86245C22.8034 10.2713 22.5286 10.6739 22.1197 10.7401C21.712 10.8061 21.3279 10.53 21.2601 10.1231C21.258 10.1121 21.2522 10.0828 21.2461 10.0551C21.2337 9.9997 21.2124 9.91188 21.1786 9.79572C21.1109 9.56339 20.9934 9.21806 20.7982 8.79238C20.4084 7.94207 19.7074 6.76789 18.4695 5.53002C17.2317 4.29216 16.0575 3.59117 15.2072 3.20134C14.7815 3.00618 14.4362 2.88865 14.2038 2.82097C14.0877 2.78714 13.9417 2.75363 13.8863 2.7413C13.4793 2.67347 13.1935 2.28755 13.2595 1.87983Z" fill="#267F82"/> <path fill-rule="evenodd" clip-rule="evenodd" d="M13.4857 5.3293C13.5995 4.93102 14.0146 4.7004 14.4129 4.81419L14.2069 5.53534C14.4129 4.81419 14.4129 4.81419 14.4129 4.81419L14.4144 4.81461L14.4159 4.81505L14.4192 4.81602L14.427 4.81834L14.4468 4.8245C14.4618 4.82932 14.4807 4.8356 14.5031 4.84357C14.548 4.85951 14.6074 4.88217 14.6802 4.91337C14.8259 4.97581 15.0249 5.07223 15.2695 5.21694C15.7589 5.50662 16.4271 5.9878 17.2121 6.77277C17.9971 7.55775 18.4782 8.22593 18.7679 8.7154C18.9126 8.95991 19.009 9.15897 19.0715 9.30466C19.1027 9.37746 19.1254 9.43682 19.1413 9.48173C19.1493 9.50418 19.1555 9.52301 19.1604 9.53809L19.1665 9.55788L19.1688 9.56563L19.1698 9.56896L19.1702 9.5705C19.1702 9.5705 19.1707 9.57194 18.4495 9.77798L19.1707 9.57194C19.2845 9.97021 19.0538 10.3853 18.6556 10.4991C18.2607 10.6119 17.8492 10.3862 17.7313 9.99413L17.7276 9.98335C17.7223 9.96832 17.7113 9.93874 17.6928 9.89554C17.6558 9.8092 17.5887 9.66797 17.4771 9.47938C17.2541 9.10264 16.8514 8.53339 16.1514 7.83343C15.4515 7.13348 14.8822 6.73078 14.5055 6.50781C14.3169 6.39619 14.1757 6.32909 14.0893 6.29209C14.0461 6.27358 14.0165 6.26254 14.0015 6.25721L13.9907 6.25352C13.5987 6.13564 13.3729 5.72419 13.4857 5.3293Z" fill="#267F82"/> </g>
</svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

7
icons/tryton-target.svg Normal file
View File

@@ -0,0 +1,7 @@
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools -->
<svg fill="#267F82" width="800px" height="800px" viewBox="0 0 256 256" id="Flat" xmlns="http://www.w3.org/2000/svg" stroke="#267F82">
<g id="SVGRepo_bgCarrier" stroke-width="0"/>

After

Width:  |  Height:  |  Size: 1.2 KiB

18
icons/tryton-tel.svg Normal file
View File

@@ -0,0 +1,18 @@
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg" fill="none" stroke="#267F82">
<g id="SVGRepo_bgCarrier" stroke-width="0"/>
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"/>
<g id="SVGRepo_iconCarrier">
<path d="M50.19 20.6a3.33 3.33 0 0 0-4.62.8l-1.9 2.71a3.31 3.31 0 0 0 .8 4.61c5.41 3.81 1.59 9.22-.31 11.93S38.44 48.76 33 45a3.31 3.31 0 0 0-4.61.8l-1.91 2.7a3.31 3.31 0 0 0 .8 4.61C34.07 57.83 40 58 49.57 44.46s7.43-19.09.62-23.86z"/>
<rect x="8" y="8" width="6" height="6"/>
<rect x="20" y="8" width="6" height="6"/>
<rect x="32" y="8" width="6" height="6"/>

After

Width:  |  Height:  |  Size: 1.1 KiB

604
locale/es.po Normal file
View File

@@ -0,0 +1,604 @@
#
msgid ""
msgstr "Content-Type: text/plain; charset=utf-8\n"
msgctxt "model:res.group,name:group_prospect"
msgid "Prospects"
msgstr "Prospectos"
msgctxt "model:res.group,name:group_prospect_admin"
msgid "Prospect Administrator"
msgstr "Administración de Prospectos"
msgctxt "model:res.group,name:group_call"
msgid "Calls"
msgstr "Llamadas"
msgctxt "model:res.group,name:group_call_admin"
msgid "Calls Administrator"
msgstr "Administrador de Llamadas"
msgctxt "model:res.group,name:group_prospect_trace"
msgid "Prospect Traces"
msgstr "Seguimiento de Prospectos"
msgctxt "model:res.group,name:group_prospect_trace_admin"
msgid "Traces Administrator"
msgstr "Administración de Seguimientos"
msgctxt "field:res.user,user_admin:"
msgid "Is Admin"
msgstr "Es Administrador"
msgctxt "field:res.user,is_operator:"
msgid "Is Operator"
msgstr "Es Operador"
msgctxt "field:sale.prospect_trace,prospect:"
msgid "Prospect"
msgstr "Prospecto"
msgctxt "field:sale.prospect_trace,prospect_business_unit:"
msgid "Business unit"
msgstr "Unidad de negocio"
msgctxt "field:sale.prospect_trace,prospect_contacts:"
msgid "Prospect contacts"
msgstr "Métodos de contacto"
msgctxt "field:sale.prospect_trace,prospect_city:"
msgid "City"
msgstr "Ciudad"
msgctxt "field:sale.prospect_trace,prospect_assigned_operator:"
msgid "Assigned operator"
msgstr "Operario asignado"
msgctxt "field:sale.prospect_trace,calls:"
msgid "Calls"
msgstr "Llamadas"
msgctxt "field:sale.prospect_trace,pending_call:"
msgid "Pending call"
msgstr "LLamada pendiente"
msgctxt "field:sale.prospect_trace,current_interest:"
msgid "Current interest"
msgstr "Interés actual"
msgctxt "field:sale.prospect_trace,state:"
msgid "State"
msgstr "Estado"
msgctxt "field:sale.prospect,name:"
msgid "Name"
msgstr "Nombre"
msgctxt "field:sale.prospect,business_unit:"
msgid "Business unit"
msgstr "Unidad de negocio"
msgctxt "view:sale.prospect:"
msgid "Contact methods"
msgstr "Métodos de contacto"
msgctxt "view:sale.prospect:"
msgid "About prospect"
msgstr "Tipificación del prospecto"
msgctxt "field:sale.prospect,contact_methods:"
msgid "Contact methods"
msgstr "Métodos de contacto"
msgctxt "field:sale.prospect,department:"
msgid "Department"
msgstr "Departamento"
msgctxt "field:sale.prospect,city:"
msgid "City"
msgstr "Ciudad"
msgctxt "field:sale.prospect,assigned_operator:"
msgid "Assigned operator"
msgstr "Operario asignado"
msgctxt "field:sale.prospect,state:"
msgid "State"
msgstr "Estado"
msgctxt "field:sale.prospect,prospect_trace:"
msgid "Prospect trace"
msgstr "Seguimiento de prospecto"
msgctxt "field:sale.prospect,rating:"
msgid "Rating (1-5)"
msgstr "Calificación (1-5)"
msgctxt "field:sale.prospect,comments:"
msgid "Comments"
msgstr "Comentarios"
msgctxt "field:sale.call,date:"
msgid "Date"
msgstr "Fecha"
msgctxt "field:sale.call,description:"
msgid "Description"
msgstr "Descripción"
msgctxt "field:sale.call,prospect_trace:"
msgid "Prospect trace"
msgstr "Seguimiento de prospecto"
msgctxt "field:sale.call,interest:"
msgid "Interest"
msgstr "Interés"
msgctxt "field:sale.call,call_type:"
msgid "Call type"
msgstr "Tipo"
msgctxt "field:sale.call,call_result:"
msgid "Call result"
msgstr "Resultado"
msgctxt "field:sale.call,call_business_unit:"
msgid "Business unit"
msgstr "Unidad de negocio"
msgctxt "field:sale.call,operator_who_called:"
msgid "Operator who called"
msgstr "Llamado por"
msgctxt "field:sale.pending_call,date:"
msgid "Date"
msgstr "Fecha"
msgctxt "field:prospect.contact_method,contact_type:"
msgid "Contact type"
msgstr "Tipo de contacto"
msgctxt "field:prospect.contact_method,value:"
msgid "Value"
msgstr "Valor"
msgctxt "field:prospect.contact_method,name:"
msgid "Name"
msgstr "Nombre"
msgctxt "field:prospect.contact_method,job:"
msgid "Job"
msgstr "Cargo"
msgctxt "field:prospect.contact_method,prospect:"
msgid "Prospect"
msgstr "Prospecto"
msgctxt "field:prospect.contact_method,prospect_trace:"
msgid "Prospect Trace"
msgstr "Seguimiento de prospecto"
msgctxt "field:sale.prospect.assign.start,prospects_chunk:"
msgid "Prospects chunk"
msgstr "Cantidad de prospectos"
msgctxt "field:sale.prospect.assign.start,operator:"
msgid "Operator"
msgstr "Operador"
msgctxt "field:sale.prospect.assign.start,prospects:"
msgid "Prospects"
msgstr "Prospectos"
msgctxt "field:sale.prospect.assign.start,business_unit:"
msgid "Business unit"
msgstr "Unidad de negocio"
msgctxt "field:sale.prospect.reassign_by_operator.start,current_operator:"
msgid "Current operator"
msgstr "Operador actual"
msgctxt "field:sale.prospect.reassign_by_operator.start,new_operator:"
msgid "New operator"
msgstr "Nuevo operador"
msgctxt "field:sale.prospect.reassign_by_operator.start,prospects:"
msgid "Prospects"
msgstr "Prospectos"
msgctxt "field:sale.prospect.reassign_by_prospect.start,prospect:"
msgid "Prospect"
msgstr "Prospecto"
msgctxt "field:sale.prospect.reassign_by_prospect.start,new_operator:"
msgid "New operator"
msgstr "Nuevo operador"
msgctxt "field:sale.prospect_trace.schedule.start,currency_date:"
msgid "Currency Date"
msgstr "Fecha actual"
msgctxt "field:sale.prospect_trace.schedule.start,date_time:"
msgid "Date time"
msgstr "Fecha y hora"
msgctxt "field:sale.prospect_trace.make_call.start,description:"
msgid "Description"
msgstr "Descripción"
msgctxt "field:sale.prospect_trace.make_call.start,interest:"
msgid "Interest"
msgstr "Interés"
msgctxt "field:sale.prospect_trace.make_call.start,schedule_call:"
msgid "Schedule call?"
msgstr "¿Agendar llamada?"
msgctxt "field:sale.prospect_trace.make_call.ask,currency_date:"
msgid "Currency Date"
msgstr "Fecha actual"
msgctxt "field:sale.prospect_trace.make_call.ask,datetime:"
msgid "Date time"
msgstr "Fecha y hora"
msgctxt "field:sale.pending_task,description:"
msgid "Description"
msgstr "Descripción"
msgctxt "field:sale.pending_task,prospect_trace:"
msgid "Prospect trace"
msgstr "Seguimiento de prospecto"
msgctxt "field:sale.prospect_trace.make_call.start,schedule_task:"
msgid "Schedule task?"
msgstr "¿Agendar tarea?"
msgctxt "field:sale.prospect_trace.make_call.ask_task,task_description:"
msgid "Task description"
msgstr "Descripción de la tarea"
msgctxt "selection:sale.prospect,business_unit:"
msgid "Brigade"
msgstr "Brigada"
msgctxt "selection:sale.prospect,business_unit:"
msgid "Optics"
msgstr "Óptica"
msgctxt "selection:sale.prospect,business_unit:"
msgid "Equipment"
msgstr "Equipos"
msgctxt "selection:sale.prospect,state:"
msgid "Unsassigned"
msgstr "Sin asignar"
msgctxt "selection:sale.prospect,state:"
msgid "Assigned"
msgstr "Asignado"
msgctxt "selection:prospect.contact_method,contact_type:"
msgid "Phone"
msgstr "Teléfono fijo"
msgctxt "selection:prospect.contact_method,contact_type:"
msgid "Mobile"
msgstr "Celular"
msgctxt "selection:prospect.contact_method,contact_type:"
msgid "Mail"
msgstr "Correo electrónico"
msgctxt "selection:sale.prospect,rating:"
msgid "None"
msgstr "Sin calificación"
msgctxt "selection:sale.prospect.assign.start,business_unit:"
msgid "Brigade"
msgstr "Brigada"
msgctxt "selection:sale.prospect.assign.start,business_unit:"
msgid "Optics"
msgstr "Óptica"
msgctxt "selection:sale.prospect.assign.start,business_unit:"
msgid "Equipment"
msgstr "Equipos"
msgctxt "selection:sale.prospect_trace,prospect_business_unit:"
msgid "Brigade"
msgstr "Brigada"
msgctxt "selection:sale.prospect_trace,prospect_business_unit:"
msgid "Optics"
msgstr "Óptica"
msgctxt "selection:sale.prospect_trace,prospect_business_unit:"
msgid "Equipment"
msgstr "Equipos"
msgctxt "selection:sale.prospect_trace,current_interest:"
msgid "0 - Not answered"
msgstr "0 - No contestó"
msgctxt "selection:sale.prospect_trace,current_interest:"
msgid "1 - Complete disinterest"
msgstr "1 - Completo desinterés"
msgctxt "selection:sale.prospect_trace,current_interest:"
msgid "2 - Middle interest"
msgstr "2 - Interés intermedio"
msgctxt "selection:sale.prospect_trace,current_interest:"
msgid "3 - High interest"
msgstr "3 - Interés alto"
msgctxt "selection:sale.prospect_trace,current_interest:"
msgid "4 - Closed sale"
msgstr "4 - Venta cerrada"
msgctxt "selection:sale.prospect_trace,state:"
msgid "Open"
msgstr "Abierto"
msgctxt "selection:sale.prospect_trace,state:"
msgid "With pending calls"
msgstr "Con llamada pendiente"
msgctxt "selection:sale.prospect_trace,state:"
msgid "Closed"
msgstr "Cerrado"
msgctxt "selection:sale.prospect_trace.make_call.start,schedule_call:"
msgid "Yes"
msgstr "Si"
msgctxt "selection:sale.prospect_trace.make_call.start,interest:"
msgid "0 - Not answered"
msgstr "0 - No contestó"
msgctxt "selection:sale.prospect_trace.make_call.start,interest:"
msgid "1 - Complete disinterest"
msgstr "1 - Completo desinterés"
msgctxt "selection:sale.prospect_trace.make_call.start,interest:"
msgid "2 - Middle interest"
msgstr "2 - Interés intermedio"
msgctxt "selection:sale.prospect_trace.make_call.start,interest:"
msgid "3 - High interest"
msgstr "3 - Interés alto"
msgctxt "selection:sale.prospect_trace.make_call.start,interest:"
msgid "4 - Closed sale"
msgstr "4 - Venta cerrada"
msgctxt "selection:sale.call,interest:"
msgid "0 - Not answered"
msgstr "0 - No contestó"
msgctxt "selection:sale.call,interest:"
msgid "1 - Complete disinterest"
msgstr "1 - Completo desinterés"
msgctxt "selection:sale.call,interest:"
msgid "2 - Middle interest"
msgstr "2 - Interés intermedio"
msgctxt "selection:sale.call,interest:"
msgid "3 - High interest"
msgstr "3 - Interés alto"
msgctxt "selection:sale.call,interest:"
msgid "4 - Closed sale"
msgstr "4 - Venta cerrada"
msgctxt "selection:sale.call,call_type:"
msgid "First call"
msgstr "Primera llamada"
msgctxt "selection:sale.call,call_type:"
msgid "Follow up call"
msgstr "LLamada de seguimiento"
msgctxt "selection:sale.call,call_result:"
msgid "Missed call"
msgstr "Llamada perdida"
msgctxt "selection:sale.call,call_result:"
msgid "Answered call"
msgstr "LLamada contestada"
msgctxt "selection:sale.call,call_business_unit:"
msgid "Brigade"
msgstr "Brigada"
msgctxt "selection:sale.call,call_business_unit:"
msgid "Optics"
msgstr "Óptica"
msgctxt "selection:sale.call,call_business_unit:"
msgid "Equipment"
msgstr "Equipos"
msgctxt "selection:sale.prospect_trace.make_call.start,schedule_task:"
msgid "Yes"
msgstr "Si"
msgctxt "model:ir.ui.menu,name:menu_calls"
msgid "Calls"
msgstr "Llamadas"
msgctxt "model:ir.ui.menu,name:menu_calls_tree"
msgid "Calls"
msgstr "Llamadas"
msgctxt "model:ir.ui.menu,name:menu_prospect_trace_tree"
msgid "Prospect Traces"
msgstr "Seguimiento de Prospectos"
msgctxt "model:ir.ui.menu,name:menu_prospects_tree"
msgid "Prospects"
msgstr "Prospectos"
msgctxt "model:ir.ui.menu,name:menu_prospects_assigned_wizard"
msgid "Assign Operator"
msgstr "Asignar Operador"
msgctxt "model:ir.ui.menu,name:menu_reassign"
msgid "Reassign"
msgstr "Reasignar"
msgctxt "model:ir.ui.menu,name:menu_reassign_by_operator_wizard"
msgid "Reassign by operator"
msgstr "Reasignar por operador"
msgctxt "model:ir.ui.menu,name:menu_reassign_by_prospect_wizard"
msgid "Reassign by prospect"
msgstr "Reasignar por prospecto"
msgctxt "model:ir.ui.menu,name:menu_pending_tasks"
msgid "Pending tasks"
msgstr "Tareas pendientes"
msgctxt "model:ir.model.button,string:schedule_call_wizard_button"
msgid "Schedule call"
msgstr "Agendar llamada"
msgctxt "model:ir.model.button,string:make_call_wizard_button"
msgid "Make call"
msgstr "Hacer llamada"
msgctxt "model:ir.model.button,string:close_trace_button"
msgid "Close trace"
msgstr "Cerrar seguimiento"
msgctxt "model:ir.model.button,string:reopen_trace_button"
msgid "Reopen trace"
msgstr "Reabrir seguimiento"
msgctxt "model:ir.model.button,string:close_task_button"
msgid "Close task"
msgstr "Cerrar tarea"
msgctxt "wizard_button:sale.prospect_trace.make_call,start,end:"
msgid "Cancel"
msgstr "Cancelar"
msgctxt "wizard_button:sale.prospect_trace.make_call,start,make_call:"
msgid "Make call"
msgstr "Hacer llamada"
msgctxt "wizard_button:sale.prospect_trace.make_call,ask,end:"
msgid "Cancel"
msgstr "Cancelar"
msgctxt "wizard_button:sale.prospect_trace.make_call,ask,schedule_call:"
msgid "Schedule call"
msgstr "Agendar llamada"
msgctxt "wizard_button:sale.prospect_trace.schedule,start,end:"
msgid "Cancel"
msgstr "Cancelar"
msgctxt "wizard_button:sale.prospect_trace.schedule,start,schedule:"
msgid "Schedule"
msgstr "Agendar llamada"
msgctxt "wizard_button:sale.prospect_trace.make_call,ask_task,end:"
msgid "Cancel"
msgstr "Cancelar"
msgctxt "wizard_button:sale.prospect_trace.make_call,ask_task,schedule_task:"
msgid "Schedule task"
msgstr "Agendar tarea"
msgctxt "wizard_button:sale.prospect.assign,start,end:"
msgid "Cancel"
msgstr "Cancelar"
msgctxt "wizard_button:sale.prospect.assign,start,assign:"
msgid "Assign"
msgstr "Asignar"
msgctxt "wizard_button:sale.prospect.reassign_by_operator,start,end:"
msgid "Cancel"
msgstr "Cancelar"
msgctxt "wizard_button:sale.prospect.reassign_by_operator,start,reassign_by_operator:"
msgid "Reassign"
msgstr "Reasignar"
msgctxt "wizard_button:sale.prospect.reassign_by_prospect,start,end:"
msgid "Cancel"
msgstr "Cancelar"
msgctxt "wizard_button:sale.prospect.reassign_by_prospect,start,reassign_by_prospect:"
msgid "Reassign"
msgstr "Reasignar"
msgctxt "model:ir.action.act_window.domain,name:act_prospect_trace_domain_open"
msgid "Open"
msgstr "Abierto"
msgctxt "model:ir.action.act_window.domain,name:act_prospect_trace_domain_with_pending_calls"
msgid "With pending calls"
msgstr "Con llamada pendiente"
msgctxt "model:ir.action.act_window.domain,name:act_prospect_trace_domain_with_pending_calls"
msgid "With pending calls"
msgstr "Con llamada pendiente"
msgctxt "model:ir.action.act_window.domain,name:act_prospect_trace_domain_closed"
msgid "Closed"
msgstr "Cerrado"
msgctxt "model:ir.action.act_window.domain,name:act_prospect_trace_domain_all"
msgid "All"
msgstr "Todo"
msgctxt "model:ir.action.act_window.domain,name:act_prospect_domain_unassigned"
msgid "Unassigned"
msgstr "Sin asignar"
msgctxt "model:ir.action.act_window.domain,name:act_prospect_domain_assigned"
msgid "Assigned"
msgstr "Asignado"
msgctxt "model:ir.action.act_window.domain,name:act_task_domain_pending"
msgid "Pending"
msgstr "Pendiente"
msgctxt "model:ir.action.act_window.domain,name:act_task_domain_done"
msgid "Done"
msgstr "Completado"
msgctxt "model:ir.action.act_window.domain,name:act_task_domain_all"
msgid "All"
msgstr "Todo"
msgctxt "model:ir.action,name:schedule_call_wizard"
msgid "Schedule call"
msgstr "Agendar llamada"
msgctxt "model:ir.action,name:make_call_wizard"
msgid "Make call"
msgstr "Hacer llamada"
msgctxt "model:ir.action,name:assign_operator_wizard"
msgid "Assign Operator"
msgstr "Asignar operador"
msgctxt "model:ir.action,name:reassign_by_operator_wizard"
msgid "Reassign by operator"
msgstr "Reasignar por operador"
msgctxt "model:ir.action,name:reassign_by_prospect_wizard"
msgid "Reassign by prospect"
msgstr "Reasignar por prospecto"

View File

@@ -1,43 +0,0 @@
<?xml version="1.0"?>
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<tryton>
<data>
<record model="ir.action.act_window" id="act_pending_call_tree">
<field name="name">Pending calls</field>
<field name="res_model">sale.pending_call</field>
</record>
<record model="ir.ui.view" id="pending_call_view_tree">
<field name="model">sale.pending_call</field>
<field name="type">tree</field>
<field name="name">pending_call_tree</field>
</record>
<record model="ir.ui.view" id="pending_call_view_form">
<field name="model">sale.pending_call</field>
<field name="type">form</field>
<field name="name">pending_call_form</field>
</record>
<record model="ir.action.act_window.view" id="act_pending_call_tree_view1">
<field name="sequence" eval="10"/>
<field name="view" ref="pending_call_view_tree"/>
<field name="act_window" ref="act_pending_call_tree"/>
</record>
<record model="ir.action.act_window.view" id="act_pending_call_form_view1">
<field name="sequence" eval="20"/>
<field name="view" ref="pending_call_view_form"/>
<field name="act_window" ref="act_pending_call_tree"/>
</record>
<!-- <menuitem
name="Calls"
sequence="10"
id="menu_calls"/> -->
<menuitem
parent="menu_calls"
sequence="40"
id="menu_pending_calls_tree"
action="act_pending_call_tree"/>
</data>
</tryton>

View File

@@ -1,74 +0,0 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from trytond.model import ModelSQL, ModelView, fields
from trytond.pyson import Eval, If
class Prospect(ModelSQL, ModelView):
'Prospecto'
__name__ = 'sale.prospect'
_rec_name = 'name'
name = fields.Char('Name', required=True)
contact_methods = fields.One2Many(
'prospect.contact_method',
'prospect', 'Contact methods', required=True)
department = fields.Many2One('sale.department', 'Department')
city = fields.Many2One('sale.city', 'City',
domain=[If(Eval('department'),
('parent', '=', Eval('department')))])
assigned_operator = fields.Many2One('res.user', "Assigned operator")
state = fields.Selection([
('unassigned', 'Unsassigned'),
('assigned', 'Assigned')], "State", readonly=True)
@classmethod
def default_state(cls):
return 'unassigned'
@fields.depends('city', 'department')
def on_change_city(self):
if self.city:
self.department = self.city.parent
@fields.depends('assigned_operator', 'state')
def on_change_assigned_operator(self):
if self.assigned_operator:
self.state = 'assigned'
else:
self.state = 'unassigned'
class ContactMethod(ModelSQL, ModelView):
'Mecanismo de contacto'
__name__ = 'prospect.contact_method'
contact_type = fields.Selection([
('phone', 'Phone'),
('mobile', 'Mobile'),
('mail', 'Mail')
], 'Contact type', required=True)
value = fields.Char('Value', required=True)
name = fields.Char('Name')
job = fields.Char('job')
prospect = fields.Many2One('sale.prospect', 'Prospect', required=True)
@classmethod
def default_contact_type(cls):
return 'mobile'
def get_rec_name(self, name):
fields = [self.name, self.job, self.value]
contact_rec_name = ''
for field in fields:
if field:
contact_rec_name += ' [' + str(field) + '] '
return contact_rec_name

View File

@@ -3,11 +3,29 @@
this repository contains the full copyright notices and license terms. --> this repository contains the full copyright notices and license terms. -->
<tryton> <tryton>
<data> <data>
<record model="ir.action.act_window" id="act_prospect_tree"> <record model="res.group" id="group_prospect">
<field name="name">Prospects</field> <field name="name">Prospects</field>
<field name="res_model">sale.prospect</field> </record>
<record model="res.group" id="group_prospect_admin">
<field name="name">Prospect Administrator</field>
<field name="parent" ref="group_prospect" />
</record>
<record model="res.user-res.group" id="user_admin_group_prospect">
<field name="user" ref="res.user_admin" />
<field name="group" ref="group_prospect" />
</record>
<record model="res.user-res.group" id="user_admin_group_prospect_admin">
<field name="user" ref="res.user_admin" />
<field name="group" ref="group_prospect_admin" />
</record> </record>
<record model="ir.action.act_window" id="act_prospect_tree">
<field name="name">Prospects</field>
<field name="domain"
eval="[If(Eval('context', {}).get('user_admin', None), (), ('assigned_operator', '=', Eval('_user')))]"
pyson="1" />
<field name="res_model">sale.prospect</field>
</record>
<record model="ir.ui.view" id="prospect_view_tree"> <record model="ir.ui.view" id="prospect_view_tree">
<field name="model">sale.prospect</field> <field name="model">sale.prospect</field>
<field name="type">tree</field> <field name="type">tree</field>
@@ -18,97 +36,46 @@ this repository contains the full copyright notices and license terms. -->
<field name="type">form</field> <field name="type">form</field>
<field name="name">prospect_form</field> <field name="name">prospect_form</field>
</record> </record>
<record model="ir.action.act_window.view" id="act_prospect_tree_view1"> <record model="ir.action.act_window.view" id="act_prospect_tree_view1">
<field name="sequence" eval="10"/> <field name="sequence" eval="10" />
<field name="view" ref="prospect_view_tree"/> <field name="view" ref="prospect_view_tree" />
<field name="act_window" ref="act_prospect_tree"/> <field name="act_window" ref="act_prospect_tree" />
</record> </record>
<record model="ir.action.act_window.view" id="act_prospect_form_view1"> <record model="ir.action.act_window.view" id="act_prospect_form_view1">
<field name="sequence" eval="20"/> <field name="sequence" eval="20" />
<field name="view" ref="prospect_view_form"/> <field name="view" ref="prospect_view_form" />
<field name="act_window" ref="act_prospect_tree"/> <field name="act_window" ref="act_prospect_tree" />
</record>
<record model="ir.action.act_window.domain" id="act_prospect_domain_unassigned">
<field name="name">Unassigned</field>
<field name="sequence" eval="10" />
<field name="domain" eval="[('state', '=', 'unassigned')]" pyson="1" />
<field name="count" eval="True" />
<field name="act_window" ref="act_prospect_tree" />
</record>
<record model="ir.action.act_window.domain" id="act_prospect_domain_assigned">
<field name="name">Assigned</field>
<field name="sequence" eval="10" />
<field name="domain" eval="[('state', '=', 'assigned')]" pyson="1" />
<field name="count" eval="True" />
<field name="act_window" ref="act_prospect_tree" />
</record> </record>
<menuitem <menuitem
parent="menu_calls" parent="menu_calls"
sequence="10" sequence="10"
id="menu_prospects_tree" id="menu_prospects_tree"
action="act_prospect_tree"/> icon="tryton-party"
action="act_prospect_tree" />
<record model="ir.action.act_window" id="act_prospect_unassigned_tree"> <record model="ir.ui.menu-res.group" id="menu_prospects_group_prospect">
<field name="name">Unassigned</field> <field name="menu" ref="menu_prospects_tree" />
<field name="res_model">sale.prospect</field> <field name="group" ref="group_prospect" />
<field name="domain" eval="[('state', '=', 'unassigned')]" pyson="1"/>
</record> </record>
<record model="ir.ui.view" id="prospect_unassigned_view_tree">
<field name="model">sale.prospect</field>
<field name="type">tree</field>
<field name="name">prospect_tree</field>
</record>
<record model="ir.ui.view" id="prospect_unassigned_view_form">
<field name="model">sale.prospect</field>
<field name="type">form</field>
<field name="name">prospect_form</field>
</record>
<record model="ir.action.act_window.view" id="act_prospect_unassigned_tree_view1">
<field name="sequence" eval="10"/>
<field name="view" ref="prospect_unassigned_view_tree"/>
<field name="act_window" ref="act_prospect_unassigned_tree"/>
</record>
<record model="ir.action.act_window.view" id="act_prospect_unassigned_form_view1">
<field name="sequence" eval="20"/>
<field name="view" ref="prospect_view_form"/>
<field name="act_window" ref="act_prospect_unassigned_tree"/>
</record>
<menuitem
parent="menu_prospects_tree"
sequence="10"
id="menu_prospects_unassigned_tree"
action="act_prospect_unassigned_tree"/>
<record model="ir.action.act_window" id="act_prospect_assigned_tree">
<field name="name">Assigned</field>
<field name="res_model">sale.prospect</field>
<field name="domain" eval="[('state', '=', 'assigned')]" pyson="1"/>
</record>
<record model="ir.ui.view" id="prospect_assigned_view_tree">
<field name="model">sale.prospect</field>
<field name="type">tree</field>
<field name="name">prospect_tree</field>
</record>
<record model="ir.ui.view" id="prospect_assigned_view_form">
<field name="model">sale.prospect</field>
<field name="type">form</field>
<field name="name">prospect_form</field>
</record>
<record model="ir.action.act_window.view" id="act_prospect_assigned_tree_view1">
<field name="sequence" eval="10"/>
<field name="view" ref="prospect_assigned_view_tree"/>
<field name="act_window" ref="act_prospect_assigned_tree"/>
</record>
<record model="ir.action.act_window.view" id="act_prospect_assigned_form_view1">
<field name="sequence" eval="20"/>
<field name="view" ref="prospect_view_form"/>
<field name="act_window" ref="act_prospect_assigned_tree"/>
</record>
<menuitem
parent="menu_prospects_tree"
sequence="10"
id="menu_prospects_assigned_tree"
action="act_prospect_assigned_tree"/>
<record model="ir.action.act_window" id="act_contact_method_tree"> <record model="ir.action.act_window" id="act_contact_method_tree">
<field name="name">Contact method</field> <field name="name">Contact method</field>
<field name="res_model">prospect.contact_method</field> <field name="res_model">prospect.contact_method</field>
</record> </record>
<record model="ir.ui.view" id="contact_method_view_tree"> <record model="ir.ui.view" id="contact_method_view_tree">
<field name="model">prospect.contact_method</field> <field name="model">prospect.contact_method</field>
<field name="type">tree</field> <field name="type">tree</field>
@@ -119,16 +86,100 @@ this repository contains the full copyright notices and license terms. -->
<field name="type">form</field> <field name="type">form</field>
<field name="name">contact_method_form</field> <field name="name">contact_method_form</field>
</record> </record>
<record model="ir.action.act_window.view" id="act_contact_method_tree_view1"> <record model="ir.action.act_window.view" id="act_contact_method_tree_view1">
<field name="sequence" eval="10"/> <field name="sequence" eval="10" />
<field name="view" ref="contact_method_view_tree"/> <field name="view" ref="contact_method_view_tree" />
<field name="act_window" ref="act_contact_method_tree"/> <field name="act_window" ref="act_contact_method_tree" />
</record> </record>
<record model="ir.action.act_window.view" id="act_contact_method_form_view1"> <record model="ir.action.act_window.view" id="act_contact_method_form_view1">
<field name="sequence" eval="20"/> <field name="sequence" eval="20" />
<field name="view" ref="contact_method_view_form"/> <field name="view" ref="contact_method_view_form" />
<field name="act_window" ref="act_contact_method_tree"/> <field name="act_window" ref="act_contact_method_tree" />
</record>
<record model="ir.action.wizard" id="assign_operator_wizard">
<field name="name">Assign Operator</field>
<field name="wiz_name">sale.prospect.assign</field>
</record>
<record model="ir.ui.view" id="assign_start_view_form">
<field name="model">sale.prospect.assign.start</field>
<field name="type">form</field>
<field name="name">assign_start_form</field>
</record>
<record model="ir.action.keyword" id="assign_operator_wizard_keyword">
<field name="keyword">form_action</field>
<field name="model">sale.prospect, -1</field>
<field name="action" ref="assign_operator_wizard" />
</record>
<menuitem
parent="menu_prospects_tree"
sequence="40"
id="menu_prospects_assigned_wizard"
action="assign_operator_wizard" />
<record model="ir.ui.menu-res.group" id="menu_prospect_assigned_group_prospect">
<field name="menu" ref="menu_prospects_assigned_wizard" />
<field name="group" ref="group_prospect_admin" />
</record>
<record model="ir.action.wizard" id="reassign_by_operator_wizard">
<field name="name">Reassign by operator</field>
<field name="wiz_name">sale.prospect.reassign_by_operator</field>
</record>
<record model="ir.ui.view" id="reassign_by_operator_start_view_form">
<field name="model">sale.prospect.reassign_by_operator.start</field>
<field name="type">form</field>
<field name="name">reassign_by_operator_form</field>
</record>
<menuitem
name="Reassign"
parent="menu_prospects_tree"
sequence="50"
id="menu_reassign"
icon='tryton-refresh' />
<record model="ir.ui.menu-res.group" id="menu_prospect_reassigned_group_prospect">
<field name="menu" ref="menu_reassign" />
<field name="group" ref="group_prospect_admin" />
</record>
<menuitem
parent="menu_reassign"
sequence="10"
id="menu_reassign_by_operator_wizard"
action="reassign_by_operator_wizard" />
<record model="ir.action.wizard" id="reassign_by_prospect_wizard">
<field name="name">Reassign by prospect</field>
<field name="wiz_name">sale.prospect.reassign_by_prospect</field>
</record>
<record model="ir.ui.view" id="reassign_by_prospect_start_view_form">
<field name="model">sale.prospect.reassign_by_prospect.start</field>
<field name="type">form</field>
<field name="name">reassign_by_prospect_form</field>
</record>
<menuitem
parent="menu_reassign"
sequence="20"
id="menu_reassign_by_prospect_wizard"
action="reassign_by_prospect_wizard" />
<record model="ir.model.access" id="access_sale">
<field name="model" search="[('model', '=', 'sale.prospect')]" />
<field name="perm_read" eval="False" />
<field name="perm_write" eval="False" />
<field name="perm_create" eval="False" />
<field name="perm_delete" eval="False" />
</record>
<record model="ir.model.access" id="access_sale_prospect">
<field name="model" search="[('model', '=', 'sale.prospect')]" />
<field name="group" ref="group_prospect" />
<field name="perm_read" eval="True" />
<field name="perm_write" eval="True" />
<field name="perm_create" eval="True" />
<field name="perm_delete" eval="True" />
</record> </record>
</data> </data>
</tryton> </tryton>

View File

@@ -1,92 +0,0 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from trytond.model import ModelSQL, ModelView, fields, DeactivableMixin
from trytond.pool import Pool
from .selections.call_types import CallTypes
from .selections.interest import Interest
class ProspectTrace(DeactivableMixin, ModelSQL, ModelView):
'Seguimiento de un prospecto'
__name__ = 'sale.prospect_trace'
prospect = fields.Many2One('sale.prospect', 'Prospect', required=True)
prospect_contact = fields.Many2One(
'prospect.contact_method', 'Contact method')
prospect_city = fields.Many2One('sale.city', 'City')
calls = fields.One2Many('sale.call', 'prospect_trace', 'Calls')
pending_calls = fields.One2Many(
'sale.pending_call', 'prospect_trace', 'Pending calls')
current_interest = fields.Selection(
Interest.get_interest_levels(), 'Current interest')
state = fields.Selection([
('unassigned', 'Unassigned'),
('open', 'Open'),
('with_pending_calls', 'With pending calls'),
('closed', 'Closed')
], 'State')
@classmethod
def default_state(cls):
return 'open'
@fields.depends('calls', 'pending_calls', 'current_interest')
def on_change_calls(self):
if not self.calls:
return
last_call = self.calls[-1]
self.current_interest = last_call.interest
already_exist_a_call = len(self.calls) > 1
if already_exist_a_call:
followup_call_type = CallTypes.get_call_types()[1][0]
last_call.call_type = followup_call_type
else:
first_call_type = CallTypes.get_call_types()[0][0]
last_call.call_type = first_call_type
there_is_a_pending_call = len(self.pending_calls) >= 1
if there_is_a_pending_call:
self.pending_calls = None
self.state = 'open'
@fields.depends('pending_calls', 'state')
def on_change_pending_calls(self):
if len(self.pending_calls) >= 1:
self.state = 'with_pending_calls'
@fields.depends('prospect')
def on_change_prospect(self):
if not self.prospect:
return
self.prospect_city = self.prospect.city
mobile_contact = self._get_prospect_mobile_contact()
if mobile_contact:
self.prospect_contact = mobile_contact
def get_rec_name(self, name):
if self.prospect:
return '[' + str(self.id) + '] ' + self.prospect.name
def _get_current_interest(self, name):
if self.calls:
return self.calls[-1].interest
def _get_prospect_mobile_contact(self):
pool = Pool()
ContactMethod = pool.get('prospect.contact_method')
contact_mobile = ContactMethod.search(
[('prospect', '=', self.prospect.id),
('contact_type', '=', 'mobile')],
limit=1)
if contact_mobile:
return contact_mobile[0]

View File

@@ -3,6 +3,22 @@
this repository contains the full copyright notices and license terms. --> this repository contains the full copyright notices and license terms. -->
<tryton> <tryton>
<data> <data>
<record model="res.group" id="group_prospect_trace">
<field name="name">Prospect Traces</field>
</record>
<record model="res.group" id="group_prospect_trace_admin">
<field name="name">Traces Administrator</field>
<field name="parent" ref="group_prospect_trace"/>
</record>
<record model="res.user-res.group" id="user_admin_group_prospect_trace">
<field name="user" ref="res.user_admin"/>
<field name="group" ref="group_prospect_trace"/>
</record>
<record model="res.user-res.group" id="user_admin_group_prospect_traces_admin">
<field name="user" ref="res.user_admin"/>
<field name="group" ref="group_prospect_trace_admin"/>
</record>
<record model="ir.ui.view" id="prospect_trace_view_tree"> <record model="ir.ui.view" id="prospect_trace_view_tree">
<field name="model">sale.prospect_trace</field> <field name="model">sale.prospect_trace</field>
<field name="type">tree</field> <field name="type">tree</field>
@@ -15,6 +31,9 @@ this repository contains the full copyright notices and license terms. -->
</record> </record>
<record model="ir.action.act_window" id="act_prospect_trace_tree"> <record model="ir.action.act_window" id="act_prospect_trace_tree">
<field name="name">Prospect Traces</field> <field name="name">Prospect Traces</field>
<field name="domain"
eval="[If(Eval('context', {}).get('user_admin', None), (), ('prospect_assigned_operator', '=', Eval('_user')))]"
pyson="1"/>
<field name="res_model">sale.prospect_trace</field> <field name="res_model">sale.prospect_trace</field>
</record> </record>
<record model="ir.action.act_window.view" id="act_prospect_trace_tree_view1"> <record model="ir.action.act_window.view" id="act_prospect_trace_tree_view1">
@@ -36,11 +55,18 @@ this repository contains the full copyright notices and license terms. -->
</record> </record>
<record model="ir.action.act_window.domain" id="act_prospect_trace_domain_with_pending_calls"> <record model="ir.action.act_window.domain" id="act_prospect_trace_domain_with_pending_calls">
<field name="name">With pending calls</field> <field name="name">With pending calls</field>
<field name="sequence" eval="10"/> <field name="sequence" eval="20"/>
<field name="domain" eval="[('state', '=', 'with_pending_calls')]" pyson="1"/> <field name="domain" eval="[('state', '=', 'with_pending_calls')]" pyson="1"/>
<field name="count" eval="True"/> <field name="count" eval="True"/>
<field name="act_window" ref="act_prospect_trace_tree"/> <field name="act_window" ref="act_prospect_trace_tree"/>
</record> </record>
<record model="ir.action.act_window.domain" id="act_prospect_trace_domain_closed">
<field name="name">Closed</field>
<field name="sequence" eval="30"/>
<field name="domain" eval="[('state', '=', 'closed')]" pyson="1"/>
<field name="count" eval="True"/>
<field name="act_window" ref="act_prospect_trace_tree"/>
</record>
<record model="ir.action.act_window.domain" id="act_prospect_trace_domain_all"> <record model="ir.action.act_window.domain" id="act_prospect_trace_domain_all">
<field name="name">All</field> <field name="name">All</field>
<field name="sequence" eval="9999"/> <field name="sequence" eval="9999"/>
@@ -48,11 +74,85 @@ this repository contains the full copyright notices and license terms. -->
<field name="count" eval="True"/> <field name="count" eval="True"/>
<field name="act_window" ref="act_prospect_trace_tree"/> <field name="act_window" ref="act_prospect_trace_tree"/>
</record> </record>
<menuitem <menuitem
parent="menu_calls" parent="menu_calls"
sequence="20" sequence="20"
id="menu_prospect_trace_tree" id="menu_prospect_trace_tree"
icon="tryton-target"
action="act_prospect_trace_tree"/> action="act_prospect_trace_tree"/>
<record model="ir.ui.menu-res.group" id="menu_prospect_trace_group_prospect_trace">
<field name="menu" ref="menu_prospect_trace_tree"/>
<field name="group" ref="group_prospect_trace"/>
</record>
<record model="ir.model.access" id="access_prospect_trace">
<field name="model" search="[('model', '=', 'sale.prospect_trace')]"/>
<field name="perm_read" eval="False"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_prospect_trace_trace">
<field name="model" search="[('model', '=', 'sale.prospect_trace')]"/>
<field name="group" ref="group_prospect_trace"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/>
<field name="perm_create" eval="True"/>
<field name="perm_delete" eval="True"/>
</record>
<record model="ir.action.wizard" id="schedule_call_wizard">
<field name="name">Schedule call</field>
<field name="wiz_name">sale.prospect_trace.schedule</field>
</record>
<record model="ir.ui.view" id="schedule_start_view_form">
<field name="model">sale.prospect_trace.schedule.start</field>
<field name="type">form</field>
<field name="name">schedule_start_form</field>
</record>
<record model="ir.model.button" id="schedule_call_wizard_button">
<field name="name">wizard_schedule</field>
<field name="string">Schedule call</field>
<field name="model" search="[('model', '=', 'sale.prospect_trace')]"/>
</record>
<record model="ir.action.wizard" id="make_call_wizard">
<field name="name">Make call</field>
<field name="wiz_name">sale.prospect_trace.make_call</field>
</record>
<record model="ir.ui.view" id="make_call_start_view_form">
<field name="model">sale.prospect_trace.make_call.start</field>
<field name="type">form</field>
<field name="name">make_call_start_form</field>
</record>
<record model="ir.ui.view" id="make_call_ask_view_form">
<field name="model">sale.prospect_trace.make_call.ask</field>
<field name="type">form</field>
<field name="name">make_call_ask_form</field>
</record>
<record model="ir.ui.view" id="make_call_ask_task_view_form">
<field name="model">sale.prospect_trace.make_call.ask_task</field>
<field name="type">form</field>
<field name="name">make_call_ask_task_form</field>
</record>
<record model="ir.model.button" id="make_call_wizard_button">
<field name="name">wizard_make_call</field>
<field name="string">Make call</field>
<field name="model" search="[('model', '=', 'sale.prospect_trace')]"/>
</record>
<record model="ir.model.button" id="close_trace_button">
<field name="name">close_trace</field>
<field name="string">Close trace</field>
<field name="model" search="[('model', '=', 'sale.prospect_trace')]"/>
</record>
<record model="ir.model.button" id="reopen_trace_button">
<field name="name">reopen_trace</field>
<field name="string">Reopen trace</field>
<field name="model" search="[('model', '=', 'sale.prospect_trace')]"/>
</record>
</data> </data>
</tryton> </tryton>

View File

@@ -6,7 +6,8 @@ class Interest():
('0', '0 - Not answered'), ('0', '0 - Not answered'),
('1', '1 - Complete disinterest'), ('1', '1 - Complete disinterest'),
('2', '2 - Middle interest'), ('2', '2 - Middle interest'),
('3', '3 - High interest') ('3', '3 - High interest'),
('4', '4 - Closed sale')
] ]
return interest_levels return interest_levels

View File

@@ -0,0 +1,35 @@
Importaciones::
>>> from proteus import Model, Wizard
>>> from trytond.transaction import Transaction
>>> from trytond.tests.tools import activate_modules, set_user
Activar módulos::
>>> config = activate_modules('sale_opportunity_management')
Crear operario::
>>> User = Model.get('res.user')
>>> operator, = User.find([('name', '=', 'Administrator')])
>>> operator.is_operator = True
>>> operator.save()
>>> set_user(operator.id)
Crear prospecto::
>>> Prospect = Model.get('sale.prospect')
>>> prospect = Prospect()
>>> prospect.name = 'Assignable To Me S.A.S'
>>> contact_method = prospect.contact_methods.new(value='123123123', name='Ricardo', job='Infraestructura')
>>> prospect.business_unit = 'brigade'
>>> prospect.save()
[ Se inició seguimiento, asignado al operador que lo creó ]
>>> ProspectTrace = Model.get('sale.prospect_trace')
>>> prospect_trace, = ProspectTrace.find([('prospect', '=', prospect)])
>>> assigned_operator_id = prospect_trace.prospect_assigned_operator.id
>>> assert operator.id == assigned_operator_id
>>> assert prospect.assigned_operator.id == assigned_operator_id

View File

@@ -7,7 +7,7 @@ Imports::
>>> from proteus import Model, Wizard >>> from proteus import Model, Wizard
>>> from trytond.tests.tools import activate_modules >>> from trytond.tests.tools import activate_modules
>>> from datetime import date, timedelta >>> from datetime import date, timedelta, datetime
>>> import xml.etree.ElementTree as ET >>> import xml.etree.ElementTree as ET
Activate modules:: Activate modules::
@@ -42,71 +42,97 @@ El administrador deberá poder registrar los contactos de diferentes prospectos,
Crear prospecto:: Crear prospecto::
>>> Prospect = Model.get('sale.prospect') >>> Prospect = Model.get('sale.prospect')
>>> prospect = Prospect() >>> prospect1 = Prospect()
>>> prospect.name = 'guchito S.A.S'
>>> contact_method = prospect.contact_methods.new(value='31223425234', name='Roberto', job='Gerente R.H')
>>> contact_method = prospect.contact_methods.new(contact_type='mobile', value='12345678910', name='Pancracia', job='Asistente administrativo')
>>> contact_method = prospect.contact_methods.new(contact_type='mail', value='peralto@guchitos.org', name='Peralto', job='Administrador')
.. >>> Department = Model.get('sale.department')
.. >>> cundinamarca, = Department.find([('code', '=', 'CO-25')])
.. >>> prospect.department = cundinamarca
>>> prospect1.name = 'guchito S.A.S'
>>> contact_method = prospect1.contact_methods.new(value='31223425234', name='Roberto', job='Gerente R.H')
>>> contact_method = prospect1.contact_methods.new(contact_type='mobile', value='12345678910', name='Pancracia', job='Asistente administrativo')
>>> contact_method = prospect1.contact_methods.new(contact_type='mail', value='peralto@guchitos.org', name='Peralto', job='Administrador')
>>> City = Model.get('sale.city') >>> City = Model.get('sale.city')
>>> medellin, = City.find([('code', '=', 'CO-05001')]) >>> medellin, = City.find([('code', '=', 'CO-05001')])
>>> prospect.city = medellin >>> prospect1.city = medellin
>>> prospect.save() >>> prospect1.business_unit = 'brigade'
>>> prospect1.save()
Verificar estado final de creación de prospecto:: Verificar estado final de creación de prospecto::
>>> prospect.contact_methods >>> prospect1.contact_methods
[proteus.Model.get('prospect.contact_method')(1), proteus.Model.get('prospect.contact_method')(2), proteus.Model.get('prospect.contact_method')(3)] [proteus.Model.get('prospect.contact_method')(1), proteus.Model.get('prospect.contact_method')(2), proteus.Model.get('prospect.contact_method')(3)]
>>> prospect.contact_methods[0].contact_type >>> prospect1.contact_methods[0].contact_type
'mobile' 'mobile'
>>> prospect.contact_methods[0].job >>> prospect1.contact_methods[0].job
'Gerente R.H' 'Gerente R.H'
>>> prospect.contact_methods[2].name >>> prospect1.contact_methods[2].name
'Peralto' 'Peralto'
>>> prospect.contact_methods[2].value >>> prospect1.contact_methods[2].value
'peralto@guchitos.org' 'peralto@guchitos.org'
>>> prospect.city.code >>> prospect1.city.code
'CO-05001' 'CO-05001'
>>> prospect.department.code >>> prospect1.department.code
'CO-05' 'CO-05'
>>> prospect.state >>> prospect1.business_unit
'brigade'
>>> prospect1.state
'unassigned' 'unassigned'
Asignar operario a un prospecto:: Crear segundo prospecto::
>>> User = Model.get('res.user') >>> prospect2 = Prospect()
>>> user, = User.find([('name', '=', 'Administrator')])
>>> prospect.assigned_operator = user
>>> prospect.assigned_operator.name >>> prospect2.name = 'Modernitus S.A.S'
'Administrator' >>> contact_method = prospect2.contact_methods.new(value='3122390987', name='Pepe', job='Jefe de ventas')
>>> prospect.state
'assigned'
Remover operario asignado de un prospecto:: >>> City = Model.get('sale.city')
>>> prospect.assigned_operator = None >>> bogota, = City.find([('code', '=', 'CO-11001')])
>>> prospect.state >>> prospect2.city = bogota
'unassigned' >>> prospect2.business_unit = 'brigade'
>>> prospect2.save()
Crear tercer prospecto::
>>> prospect3 = Prospect()
>>> prospect3.name = 'Vision S.A.S'
>>> contact_method = prospect3.contact_methods.new(value='3122324287', name='Alfredo', job='Administrador')
>>> prospect3.business_unit = 'optics'
>>> prospect3.save()
Asignar tipificación a un prospecto
>>> prospect3.rating = '1'
>>> prospect3.comments = 'Calificación al cliente'
------------------------------------ ------------------------------------
Asignación de prospectos a operarios Asignación de prospectos a operarios
------------------------------------ ------------------------------------
**Como administrador, quiero poder asignar diferentes seguimientos de prospectos a diferentes operarios, para dividir el trabajo de una manera efectiva y que cada operario tenga sus propias llamadas y que no se mezcle con las de los demás** **Como administrador, quiero poder asignar diferentes seguimientos de prospectos a diferentes operarios, para dividir el trabajo de una manera efectiva y que cada operario tenga sus propias llamadas y que no se mezcle con las de los demás**
TODO Asignar prospectos a un operario::
>>> User = Model.get('res.user')
>>> user, = User.find([('name', '=', 'Administrator')])
>>> assign = Wizard('sale.prospect.assign', [prospect1, prospect2, prospect3])
>>> assign.form.business_unit = 'brigade'
>>> assign.form.prospects_chunk = 3
>>> assign.form.operator = user
>>> assign.form.prospects
[proteus.Model.get('sale.prospect')(1), proteus.Model.get('sale.prospect')(2)]
>>> assign.execute('assign')
>>> prospect1.assigned_operator.name
'Administrator'
>>> prospect1.state
'assigned'
>>> prospect2.assigned_operator.name
'Administrator'
>>> prospect2.state
'assigned'
----------------------- -----------------------
Seguimiento de llamadas Seguimiento de llamadas
----------------------- -----------------------
**Como operador quiero poder crear un seguimiento de prospecto para luego hacer una llamada** **Como operador quiero poder crear un seguimiento de prospecto para luego hacer una llamada**
**Como operador quiero registrar una llamada para luego generar reportes** **Como operador quiero registrar una llamada para luego generar reportes**
**Como operador quiero programar una llamada para luego obtener un reporte de trabajo pendiente** **Como operador quiero programar una llamada para luego obtener un reporte de trabajo pendiente**
["Crear Campo para registro de la fecha de la llamada"] ["Crear Campo para registro de la fecha de la llamada"]
@@ -155,38 +181,64 @@ Seguimiento de prospecto 2
* Seguimiento de prospecto al que pertence * Seguimiento de prospecto al que pertence
Crear seguimiento de prospecto:: Verificar creación de seguimiento de prospecto::
>>> ProspectTrace = Model.get('sale.prospect_trace') >>> ProspectTrace = Model.get('sale.prospect_trace')
>>> prospect_trace = ProspectTrace() >>> prospect_trace, = ProspectTrace.find([('prospect', '=', prospect1)])
>>> prospect_trace.prospect = prospect
>>> prospect_trace.save()
>>> prospect_trace.prospect.name >>> prospect_trace.prospect.name
'guchito S.A.S' 'guchito S.A.S'
>>> prospect_trace.prospect_business_unit
'brigade'
>>> prospect_trace.prospect_city.name >>> prospect_trace.prospect_city.name
'Medellín' 'Medellín'
>>> prospect_trace.prospect_contact.value >>> prospect_trace.prospect_assigned_operator.name
'31223425234' 'Administrator'
>>> prospect_trace.prospect_contact.contact_type >>> prospect_trace.prospect_contacts
'mobile' [proteus.Model.get('prospect.contact_method')(1), proteus.Model.get('prospect.contact_method')(2), proteus.Model.get('prospect.contact_method')(3)]
>>> prospect_trace.prospect_contact.job
'Gerente R.H'
Crear llamadas a un seguimiento de prospecto desde el seguimiento de prospecto:: Agregar un método de contacto desde el seguimiento de prospecto::
>>> call1 = prospect_trace.calls.new(description='First call', interest='0') >>> contact_method_ = prospect_trace.prospect_contacts.new(value='31231231212', name='Carlos', job='Supervisor')
>>> call2 = prospect_trace.calls.new(description='Second call', interest='1') >>> contact_method_.prospect
>>> call3 = prospect_trace.calls.new(description='Third call', interest='3') proteus.Model.get('sale.prospect')(1)
>>> prospect_trace.save()
Verificar estado final del seguimiento del prospecto y sus llamadas
Crear llamadas a un seguimiento de prospecto::
>>> make_call = Wizard('sale.prospect_trace.make_call', [prospect_trace])
>>> make_call.form.description = 'First call to the prospect'
>>> make_call.form.interest = '0'
>>> make_call.form.schedule_call = 'no'
>>> make_call.execute('make_call')
>>> make_call.state
'end'
>>> make_call = Wizard('sale.prospect_trace.make_call', [prospect_trace])
>>> make_call.form.description = 'Second call to the prospect'
>>> make_call.form.interest = '1'
>>> make_call.form.schedule_call = 'no'
>>> make_call.execute('make_call')
>>> make_call.state
'end'
>>> make_call = Wizard('sale.prospect_trace.make_call', [prospect_trace])
>>> make_call.form.description = 'Third call to the prospect'
>>> make_call.form.interest = '3'
>>> make_call.form.schedule_call = 'yes'
>>> make_call.execute('make_call')
>>> make_call.form.datetime = datetime(2023, 8, 14, 15, 30, 30)
>>> make_call.execute('schedule_call')
Verificar estado final del seguimiento del prospecto y sus llamadas::
>>> prospect_trace.calls[0].call_result >>> prospect_trace.calls[0].call_result
'missed_call' 'missed_call'
>>> prospect_trace.calls[0].call_type >>> prospect_trace.calls[0].call_type
'first_call' 'first_call'
>>> prospect_trace.calls[0].date == date.today() >>> prospect_trace.calls[0].date == date.today()
True True
>>> prospect_trace.calls[0].call_business_unit
'brigade'
>>> prospect_trace.calls[0].operator_who_called.name
'Administrator'
>>> prospect_trace.calls[1].call_result >>> prospect_trace.calls[1].call_result
'answered_call' 'answered_call'
>>> prospect_trace.calls[1].call_type >>> prospect_trace.calls[1].call_type
@@ -194,46 +246,143 @@ Verificar estado final del seguimiento del prospecto y sus llamadas
>>> prospect_trace.calls >>> prospect_trace.calls
[proteus.Model.get('sale.call')(1), proteus.Model.get('sale.call')(2), proteus.Model.get('sale.call')(3)] [proteus.Model.get('sale.call')(1), proteus.Model.get('sale.call')(2), proteus.Model.get('sale.call')(3)]
>>> prospect_trace.pending_call.date
datetime.datetime(2023, 8, 14, 15, 30, 30)
>>> prospect_trace.current_interest >>> prospect_trace.current_interest
'3' '3'
>>> prospect_trace.state >>> prospect_trace.state
'open' 'with_pending_calls'
Programar una próxima llamada pendiente al seguimiento de prospecto:: Programar una próxima llamada pendiente al seguimiento de prospecto::
>>> pending_call4 = prospect_trace.pending_calls.new(date = date.today() + timedelta(days=7)) >>> schedule = Wizard('sale.prospect_trace.schedule', [prospect_trace])
>>> prospect_trace.save() >>> schedule.form.date_time = datetime(2023, 8, 14, 15, 30, 30)
>>> schedule.execute('schedule')
>>> prospect_trace.pending_calls
[proteus.Model.get('sale.pending_call')(1)]
>>> prospect_trace.pending_call.date
datetime.datetime(2023, 8, 14, 15, 30, 30)
>>> prospect_trace.state >>> prospect_trace.state
'with_pending_calls' 'with_pending_calls'
Crear una llamada agendada previamente: Crear una llamada agendada previamente::
>>> call4 = prospect_trace.calls.new(description='fourth call', interest='3') >>> make_call = Wizard('sale.prospect_trace.make_call', [prospect_trace])
>>> make_call.form.description = 'Fourth call to the prospect'
>>> make_call.form.interest = '2'
>>> make_call.execute('make_call')
>>> prospect_trace.pending_call
>>> prospect_trace.pending_calls
[]
>>> prospect_trace.state >>> prospect_trace.state
'open' 'open'
Cuando se asigna prospecto sin método de contacto mobile, el contacto en el seguimiento es vacío:: Hacer llamada y programar tarea::
>>> prospect2 = Prospect() >>> make_call = Wizard('sale.prospect_trace.make_call', [prospect_trace])
>>> prospect2.name = 'Sin celulares S.A.S' >>> make_call.form.description = 'Prospect told me to send him an email'
>>> contact_method = prospect2.contact_methods.new(contact_type='mail', value='felpucio@sincelulares.org', name='felpucio', job='Supervisor') >>> make_call.form.interest = '3'
>>> prospect2.save() >>> make_call.form.schedule_call = 'yes'
>>> make_call.form.schedule_task = 'yes'
>>> make_call.execute('make_call')
>>> make_call.form.datetime = datetime(2023, 8, 14, 15, 30, 30)
>>> make_call.execute('schedule_call')
>>> make_call.form.task_description = 'I have to send a mail to prospect offering him this services...'
>>> make_call.execute('schedule_task')
>>> prospect_trace2 = ProspectTrace() >>> Task = Model.get('sale.pending_task')
>>> prospect_trace2.prospect = prospect2 >>> task, = Task.find([('description', '=', 'I have to send a mail to prospect offering him this services...')])
>>> task
proteus.Model.get('sale.pending_task')(1)
>>> prospect_trace2.save() >>> task.state
'pending'
>>> task.click('close_task')
>>> task.state
'done'
Hacer llamada y cerrar venta (Seguimiento de prospecto)::
>>> make_call = Wizard('sale.prospect_trace.make_call', [prospect_trace])
>>> make_call.form.description = 'Closed sale'
>>> make_call.form.interest = '4'
>>> make_call.execute('make_call')
>>> prospect_trace.click('close_trace')
>>> prospect_trace.state
'closed'
Reabrir seguimiento a prospecto una vez cerrado::
>>> prospect_trace.click('reopen_trace')
>>> prospect_trace.state
'open'
Reasignar prospectos por operador::
>>> operator2 = User();
>>> operator2.name = 'Operatus'
>>> operator2.login = 'login'
>>> operator2.save()
>>> reassign_by_operator = Wizard('sale.prospect.reassign_by_operator', [])
>>> reassign_by_operator.form.current_operator = user
>>> reassign_by_operator.form.prospects
[proteus.Model.get('sale.prospect')(1), proteus.Model.get('sale.prospect')(2)]
>>> reassign_by_operator.form.new_operator = operator2
>>> reassign_by_operator.execute('reassign_by_operator')
>>> prospect1.reload()
>>> prospect1.assigned_operator.name
'Operatus'
>>> prospect2.reload()
>>> prospect2.assigned_operator.name
'Operatus'
>>> prospect_trace.reload()
>>> prospect_trace.prospect_assigned_operator.name
'Operatus'
.. Las llamadas deben conservar el operador que las hizo
>>> prospect_trace.calls[0].operator_who_called.name
'Administrator'
Reasignar prospectos por prospecto::
>>> reassign_by_prospect = Wizard('sale.prospect.reassign_by_prospect', [])
>>> reassign_by_prospect.form.prospect = prospect1
>>> reassign_by_prospect.form.new_operator = user
>>> reassign_by_prospect.execute('reassign_by_prospect')
>>> prospect1.reload()
>>> prospect1.assigned_operator.name
'Administrator'
>>> prospect_trace.reload()
>>> prospect_trace.prospect_assigned_operator.name
'Administrator'
>>> prospect_trace.calls[0].operator_who_called.name
'Administrator'
Crear un usuario de rol administrador::
>>> User = Model.get('res.user')
>>> admin = User(name="Administrator", login="administrator", user_admin=True)
>>> admin.save()
>>> admin.user_admin == True
True
Agregar un nuevo método de contacto desde prospecto
>>> contact_method = prospect1.contact_methods.new(value='0000000000', name='Nuevo', job='Puesto increíble')
>>> prospect1.save()
>>> prospect1.contact_methods[-1].value
'0000000000'
>>> prospect_trace.prospect_contacts[-1].value
'0000000000'
-------- --------
Reportes Reportes
-------- --------
["Crear un reporte en el que evidencie por operario y consolidado"] ["Crear un reporte en el que evidencie por operario y consolidado"]
["Cantidad de llamadas realizadas en un período de tiempo"] ["Cantidad de llamadas realizadas en un período de tiempo"]
["Crear un reporte para verificar cantidad de llamadas por realizar"] ["Crear un reporte para verificar cantidad de llamadas por realizar"]
["Crear reporte para identificación de clientes potenciales (Cliente que en la llamada fueron marcados con un nivel alto)"] ["Crear reporte para identificación de clientes potenciales (Cliente que en la llamada fueron marcados con un nivel alto)"]
@@ -243,7 +392,7 @@ Reportes
* Observaciones * Observaciones
* Operario * Operario
* Reporte de seguimientos a prospectos (Análisis de prospectos): * Reporte de seguimiento a prospecto (Análisis de prospecto):
* Interés durante distintas etapas del seguimiento * Interés durante distintas etapas del seguimiento
@@ -257,3 +406,4 @@ Reportes
* Reporte de prospectos potenciales * Reporte de prospectos potenciales
* llamadas con un nivel de interés alto * llamadas con un nivel de interés alto
* Seguimiento de prospecto al que pertenecen las llamadas * Seguimiento de prospecto al que pertenecen las llamadas

View File

@@ -24,6 +24,12 @@ class RequiredValuesTestCase(ModuleTestCase):
Prospect = pool.get('sale.prospect') Prospect = pool.get('sale.prospect')
self.assertTrue(Prospect.name.required) self.assertTrue(Prospect.name.required)
@with_transaction()
def test_bussiness_unit_en_Prospect_es_obligatorio(self):
pool = Pool()
Prospect = pool.get('sale.prospect')
self.assertTrue(Prospect.business_unit.required)
@with_transaction() @with_transaction()
def test_value_en_ContactMethod_es_obligatorio(self): def test_value_en_ContactMethod_es_obligatorio(self):
pool = Pool() pool = Pool()

View File

@@ -4,9 +4,10 @@ depends:
ir ir
res res
xml: xml:
icons.xml
user.xml
call.xml call.xml
prospect_trace.xml prospect_trace.xml
prospect.xml prospect.xml
pending_call.xml
./locations/departments.xml ./locations/departments.xml
./locations/cities.xml ./locations/cities.xml

12
user.xml Normal file
View File

@@ -0,0 +1,12 @@
<?xml version="1.0"?>
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<tryton>
<data>
<record model="ir.ui.view" id="user_view_form" >
<field name="name">user_form</field>
<field name="inherit" ref="res.user_view_form"/>
<field name="model">res.user</field>
</record>
</data>
</tryton>

View File

@@ -0,0 +1,19 @@
<?xml version="1.0"?>
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<form col="6">
<label name="operator" />
<field name="operator" />
<label name="business_unit" />
<field name="business_unit" />
<label name="prospects_chunk" />
<field name="prospects_chunk" />
<notebook colspan="6">
<page string="Prospects" id="prospects_to_assign" col="6">
<group col="2" id="prospects">
<field name="prospects" />
</group>
</page>
</notebook>
</form>

View File

@@ -18,4 +18,6 @@ this repository contains the full copyright notices and license terms. -->
<label name="call_result"/> <label name="call_result"/>
<field name="call_result"/> <field name="call_result"/>
<label name="call_business_unit"/>
<field name="call_business_unit"/>
</form> </form>

View File

@@ -8,4 +8,6 @@ this repository contains the full copyright notices and license terms. -->
<field name="call_result" expand="1"/> <field name="call_result" expand="1"/>
<field name="prospect_trace" expand="1"/> <field name="prospect_trace" expand="1"/>
<field name="description" expand="1"/> <field name="description" expand="1"/>
<field name="call_business_unit" expand="1"/>
<field name="operator_who_called" expand="1"/>
</tree> </tree>

View File

@@ -4,7 +4,6 @@ this repository contains the full copyright notices and license terms. -->
<tree editable="1"> <tree editable="1">
<field name="contact_type" expand="1"/> <field name="contact_type" expand="1"/>
<field name="value" expand="1"/> <field name="value" expand="1"/>
<field name="prospect" expand="1"/>
<field name="name" expand="1"/> <field name="name" expand="1"/>
<field name="job" expand="1"/> <field name="job" expand="1"/>
</tree> </tree>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0"?>
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<form>
<label name="datetime"/>
<field name="datetime"/>
</form>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0"?>
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<form>
<label name="task_description"/>
<field name="task_description"/>
</form>

View File

@@ -0,0 +1,18 @@
<?xml version="1.0"?>
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<form>
<group col="6" id="description">
<label name="description"/>
<field name="description"/>
</group>
<newline/>
<group col="6" id="call_data">
<label name="interest"/>
<field name="interest" colspan="1"/>
<label name="schedule_call"/>
<field name="schedule_call" colspan="1"/>
<label name="schedule_task"/>
<field name="schedule_task" colspan="1"/>
</group>
</form>

View File

@@ -0,0 +1,12 @@
<?xml version="1.0"?>
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<form>
<group col="6" id="content">
<label name="prospect_trace"/>
<field name="prospect_trace"/>
<newline/>
<label name="description"/>
<field name="description"/>
</group>
</form>

View File

@@ -2,6 +2,7 @@
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of <!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. --> this repository contains the full copyright notices and license terms. -->
<tree> <tree>
<field name="date" expand="1"/>
<field name="prospect_trace" expand="1"/> <field name="prospect_trace" expand="1"/>
<field name="description" expand="1"/>
<button name="close_task"/>
</tree> </tree>

View File

@@ -6,6 +6,9 @@ this repository contains the full copyright notices and license terms. -->
<label name="name"/> <label name="name"/>
<field name="name"/> <field name="name"/>
<label name="business_unit"/>
<field name="business_unit"/>
<newline/> <newline/>
<label name="department"/> <label name="department"/>
<field name="department"/> <field name="department"/>
@@ -19,6 +22,24 @@ this repository contains the full copyright notices and license terms. -->
<page string="Contact methods" id="contact_methods"> <page string="Contact methods" id="contact_methods">
<field name="contact_methods" colspan="2"/> <field name="contact_methods" colspan="2"/>
</page> </page>
<page string="About prospect" id="contact_methods">
<label name="rating"/>
<field name="rating"/>
<newline/>
<label name="comments"/>
<field name="comments"/>
</page>
</notebook> </notebook>
<newline/>
<group col="6" colspan="5" id="footer" yalign="0">
<label name="assigned_operator"/>
<field name="assigned_operator"/>
<label name="state"/>
<field name="state"/>
<group col="-1" colspan="1" id="checkboxes">
<label name="active"/>
<field name="active" xexpand="0" width="25"/>
</group>
</group>
</form> </form>

View File

@@ -5,8 +5,8 @@ this repository contains the full copyright notices and license terms. -->
<group col="6" colspan="5" id="header" yalign="0"> <group col="6" colspan="5" id="header" yalign="0">
<label name="prospect"/> <label name="prospect"/>
<field name="prospect"/> <field name="prospect"/>
<label name="prospect_contact"/> <label name="prospect_business_unit"/>
<field name="prospect_contact"/> <field name="prospect_business_unit"/>
<label name="prospect_city"/> <label name="prospect_city"/>
<field name="prospect_city"/> <field name="prospect_city"/>
</group> </group>
@@ -14,18 +14,25 @@ this repository contains the full copyright notices and license terms. -->
<group col="6" colspan="5" id="trace_data" yalign="0"> <group col="6" colspan="5" id="trace_data" yalign="0">
<label name="current_interest"/> <label name="current_interest"/>
<field name="current_interest"/> <field name="current_interest"/>
<label name="active"/>
<field name="active" xexpand="0" width="25"/>
</group> </group>
<field name="calls" colspan="6"/>
<newline/>
<group col="-1" colspan="6" id="buttons">
<button name="wizard_make_call" icon="tryton-tel"/>
<button name="wizard_schedule" icon="tryton-calendar"/>
</group>
<newline/> <newline/>
<notebook colspan="6">
<page string="Calls - Oldest to Newest" id="calls">
<field name="calls" colspan="2"/>
</page>
</notebook>
<field name="pending_calls" colspan="6"/> <field name="prospect_contacts" colspan="6"/>
<label name="pending_call"/>
<field name="pending_call"/>
<label name="state"/> <label name="state"/>
<field name="state"/> <field name="state"/>
<group col="-1" colspan="6" id="footer_buttons">
<button name="close_trace" icon="tryton-archive"/>
<button name="reopen_trace" icon="tryton-unarchive"/>
</group>
</form> </form>

View File

@@ -3,9 +3,10 @@
this repository contains the full copyright notices and license terms. --> this repository contains the full copyright notices and license terms. -->
<tree> <tree>
<field name="prospect" expand="1"/> <field name="prospect" expand="1"/>
<field name="prospect_contact" expand="1"/> <field name="prospect_business_unit" expand="1"/>
<field name="prospect_city" expand="1"/> <field name="prospect_city" expand="1"/>
<field name="current_interest" expand="1"/> <field name="current_interest" expand="1"/>
<field name="calls" expand="1"/> <field name="calls" expand="1"/>
<field name="pending_calls" expand="1"/> <field name="pending_call" expand="1"/>
<field name="prospect_assigned_operator" expand="1"/>
</tree> </tree>

View File

@@ -3,7 +3,9 @@
this repository contains the full copyright notices and license terms. --> this repository contains the full copyright notices and license terms. -->
<tree> <tree>
<field name="name" expand="1"/> <field name="name" expand="1"/>
<field name="business_unit" expand="1"/>
<field name="department" expand="1"/> <field name="department" expand="1"/>
<field name="city" expand="1"/> <field name="city" expand="1"/>
<field name="contact_methods" expand="1"/> <field name="assigned_operator" expand="1"/>
<field name="rating" expand="1"/>
</tree> </tree>

View File

@@ -0,0 +1,10 @@
<?xml version="1.0"?>
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<form>
<label name="current_operator"/>
<field name="current_operator"/>
<label name="new_operator"/>
<field name="new_operator"/>
<field name="prospects" colspan="6"/>
</form>

View File

@@ -2,9 +2,8 @@
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of <!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. --> this repository contains the full copyright notices and license terms. -->
<form> <form>
<label name="date"/> <label name="prospect"/>
<field name="date"/> <field name="prospect"/>
<label name="new_operator"/>
<label name="prospect_trace"/> <field name="new_operator"/>
<field name="prospect_trace"/>
</form> </form>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0"?>
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<form>
<label name="date_time"/>
<field name="date_time"/>
</form>

11
view/user_form.xml Normal file
View File

@@ -0,0 +1,11 @@
<?xml version="1.0"?>
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<data>
<xpath expr="//field[@name='name']" position="after">
<label name="user_admin" />
<field name="user_admin" />
<label name="is_operator" />
<field name="is_operator" />
</xpath>
</data>