Compare commits
143 Commits
f7e8e40d4c
...
6.8
| Author | SHA1 | Date | |
|---|---|---|---|
| b7e06a3408 | |||
| 9096983ffd | |||
| 660ac06c1d | |||
| 7b0bafa05f | |||
| 4d88f9423d | |||
| e0f3846bb5 | |||
| ef8f45267f | |||
| 5cc0277db9 | |||
| 3f7b989781 | |||
| 6c31dfb64f | |||
| b1c3b21a9c | |||
| 99de45dd32 | |||
| 950b9350cc | |||
| 6f69709823 | |||
| cd0564c95f | |||
| 4a3d178a92 | |||
| 4826687b3b | |||
| bd62abb43f | |||
| 18d06374fe | |||
| d5ca112bff | |||
| d1ad40203b | |||
| 9c368809f9 | |||
| e40991de02 | |||
| b8ba775733 | |||
| 336a266578 | |||
| 22fb3736a7 | |||
| 5cc7ef011d | |||
| 72ae0cfa22 | |||
| 0b7d15f35b | |||
| 8a0aa29368 | |||
| f71e6f4671 | |||
| cf874db55f | |||
| e97e8fa99d | |||
| 5a2c5217a6 | |||
| d50cca2924 | |||
| c1be04d009 | |||
| 214c6fc089 | |||
| a7bf0f2a38 | |||
| 243f1123a8 | |||
| faa8f35384 | |||
| 78fcd0231d | |||
| 3ac393dd01 | |||
| abf0c05894 | |||
| a73cd43582 | |||
| 0ccb733dab | |||
| 649f610bb0 | |||
| c32b4ce31c | |||
| 97bbcaed92 | |||
| a1716141b0 | |||
| 0c09498340 | |||
| 8e60272f91 | |||
| 4ec1d2e2cd | |||
| b36160c228 | |||
| 0460324ddf | |||
| 33ac57175c | |||
| 732b9dbfb8 | |||
| 01b5119f0b | |||
| eed86133f4 | |||
| dd3cd79a1f | |||
| 766f3fbc0b | |||
| 2bd62e1b14 | |||
| 94321866eb | |||
| f9bf0d3384 | |||
| 1bcc6e5294 | |||
| c9de0ab42e | |||
| d925fcf913 | |||
| 033f1aad43 | |||
| fd24a757fb | |||
| 3d77cf9a98 | |||
| 9efb29f1a1 | |||
| 8c1ed18530 | |||
| 1a469a5106 | |||
| fc70d7cb74 | |||
| ceb9f07aab | |||
| bdcf78ad56 | |||
| 79dd0454d6 | |||
| a8c6ecaa79 | |||
| 18413d9005 | |||
| e0c47961bc | |||
| 22478c9e68 | |||
| 8ec3e4b983 | |||
| 543a52303c | |||
| 70aa3f96d4 | |||
| 27d4ce8a77 | |||
| 2eb52e9dce | |||
| ed07f5fcbc | |||
| 3d77056e6f | |||
| e260c70a11 | |||
| 416804983b | |||
| 0364110866 | |||
| 27dcca74bc | |||
| 082d070643 | |||
| 1a1b262720 | |||
| 9067aa1446 | |||
| 7c39fcc429 | |||
| 1ece8ae959 | |||
| 05ae82839b | |||
| 31fb4211e4 | |||
| 8fa5d20ea6 | |||
| 4ffb654814 | |||
| a33d43cb57 | |||
| 3bb8ddbb2c | |||
| 11a541a3eb | |||
| 06a7402f0c | |||
| f3f7493d6c | |||
| 16508c5961 | |||
| 4449b57f60 | |||
| 9f8e0b96af | |||
| 229fa9044a | |||
| a8da18e2b6 | |||
| 8d1d5d1553 | |||
| 36f074a519 | |||
| d330ac341a | |||
| 1187dd6ecc | |||
| fd667a518b | |||
| b34a1e19af | |||
| 56550df81a | |||
| f0a5e30b38 | |||
| a63dd54089 | |||
| 3fc2db8543 | |||
| 05ff4b8f8d | |||
| 881f34d582 | |||
| d7095e05bb | |||
| c0db903ac8 | |||
| 93c2f10040 | |||
| 590e604604 | |||
| 2c68c3bb53 | |||
| 3c4e8f5a26 | |||
| 51d58de12a | |||
| 1bb4af8f16 | |||
| c3ad43d144 | |||
| bc4833b3cf | |||
| 1f52dcba26 | |||
| 582c8adbd3 | |||
| ca2d115e22 | |||
| 6d77af53ef | |||
| 8ab3a97b3c | |||
| 0d1602fe1d | |||
| 9b2c40d821 | |||
| d7d5c95b29 | |||
| 1d38d797c5 | |||
| 452b97cdf9 | |||
| 87da50560a |
@@ -1 +1,2 @@
|
||||
trytond: while true; do trytond -d ${DB_NAME} --dev -v -c $SCRIPT_DIR/trytond.cfg; done
|
||||
monitor: python3 $SCRIPT_DIR/dev.py
|
||||
12
.dev/dev.py
@@ -2,9 +2,12 @@
|
||||
import sys
|
||||
import os
|
||||
import logging
|
||||
import time
|
||||
|
||||
import inotify.adapters
|
||||
|
||||
logging.basicConfig(level=logging.INFO, stream=sys.stderr)
|
||||
|
||||
SRC = os.environ['SRC']
|
||||
MODULE_NAME = os.environ['MODULE_NAME']
|
||||
DB_NAME = os.environ['DB_NAME']
|
||||
@@ -18,12 +21,17 @@ def _main():
|
||||
for event in i.event_gen(yield_nones=False):
|
||||
(_, type_names, path, filename) = event
|
||||
(_, ext) = os.path.splitext(filename)
|
||||
if 'IN_CLOSE_WRITE' not in type_names:
|
||||
continue
|
||||
|
||||
if ext in ['.py', '.xml', '.cfg']:
|
||||
for _ in range(0, 10):
|
||||
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)
|
||||
break
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
[ ! -d "$SRC" ] && die "no se ubica ruta en SRC"
|
||||
|
||||
# dependencias minimas
|
||||
pip3 install psycopg2 proteus inotify honcho
|
||||
pip3 install psycopg2 proteus==6.8 inotify honcho
|
||||
|
||||
# instalar dependencias de tryton desde paquete
|
||||
python3 setup.py install
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
[web]
|
||||
listen = 0.0.0.0:8000
|
||||
root=/var/lib/trytond/www
|
||||
@@ -1,7 +1,6 @@
|
||||
#
|
||||
# variables que puedo usar? https://woodpecker-ci.org/docs/0.15/usage/environment#built-in-environment-variables
|
||||
|
||||
pipeline:
|
||||
steps:
|
||||
style:
|
||||
image: python:3.9
|
||||
commands:
|
||||
@@ -20,7 +19,7 @@ pipeline:
|
||||
- python3 -m unittest
|
||||
|
||||
notify-success:
|
||||
image: plugins/matrix
|
||||
image: gitea.onecluster.org/alnus/plugins/matrix:latest
|
||||
settings:
|
||||
home_server: https://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>
|
||||
<pre><a href="${CI_COMMIT_LINK}">${CI_COMMIT_MESSAGE}</a></pre>
|
||||
when:
|
||||
status: [ success ]
|
||||
status: [success]
|
||||
|
||||
notify-failure:
|
||||
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>
|
||||
<pre><a href="${CI_COMMIT_LINK}">${CI_COMMIT_MESSAGE}</a></pre>
|
||||
when:
|
||||
status: [ failure ]
|
||||
status: [failure]
|
||||
|
||||
services:
|
||||
postgres:
|
||||
|
||||
@@ -18,12 +18,14 @@
|
||||
1. iniciar entorno `rake init`
|
||||
2. iterar con `rake tdd`
|
||||
3. detener el entorno `rake down`
|
||||
4. limpiar cache `git clean -xdf`
|
||||
|
||||
### entorno vivo de desarrollo
|
||||
|
||||
1. iniciar entorno `rake live:up`
|
||||
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`
|
||||
|
||||
### consideraciones
|
||||
|
||||
12
Dockerfile
@@ -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
|
||||
|
||||
# trytond DB_CACHE requiere commandos `pg_dump` y `pg_restore`
|
||||
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
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
doc/index.rst
|
||||
./tests/scenario_sale_opportunity_management.rst
|
||||
4
Rakefile
@@ -12,8 +12,8 @@ end
|
||||
desc 'poblar entorno'
|
||||
task :init => [:up] do
|
||||
pecker = YAML.load_file(WOODPECKER_YML)
|
||||
['tests', 'style'].each do |pipeline|
|
||||
pecker.dig('pipeline', pipeline, 'commands').grep(/install/).each do |cmd|
|
||||
['tests', 'style'].each do |steps|
|
||||
pecker.dig('steps', steps, 'commands').grep(/install/).each do |cmd|
|
||||
compose('exec', 'app.dev', cmd)
|
||||
end
|
||||
end
|
||||
|
||||
56
__init__.py
@@ -1,8 +1,31 @@
|
||||
from trytond.pool import Pool
|
||||
from . import prospect
|
||||
from . import prospect_trace
|
||||
from . import call
|
||||
from . import pending_call
|
||||
|
||||
# Prospect Core
|
||||
from .core.Prospect.models.prospect import Prospect
|
||||
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 department
|
||||
|
||||
@@ -11,16 +34,29 @@ __all__ = ['register']
|
||||
|
||||
def register():
|
||||
Pool.register(
|
||||
pending_call.PendingCall,
|
||||
call.Call,
|
||||
User,
|
||||
PendingCall,
|
||||
Call,
|
||||
PendingTask,
|
||||
department.Department,
|
||||
city.City,
|
||||
prospect.ContactMethod,
|
||||
prospect.Prospect,
|
||||
prospect_trace.ProspectTrace,
|
||||
|
||||
ContactMethod,
|
||||
Prospect,
|
||||
ProspectTrace,
|
||||
AssignOperatorStart,
|
||||
ScheduleCallStart,
|
||||
MakeCallStart,
|
||||
MakeCallAsk,
|
||||
MakeCallAskTask,
|
||||
ReassignProspectByOperatorStart,
|
||||
ReassignProspectByProspectStart,
|
||||
module='sale_opportunity_management', type_='model')
|
||||
Pool.register(
|
||||
ScheduleCall,
|
||||
AssignOperator,
|
||||
MakeCall,
|
||||
ReassignProspectByOperator,
|
||||
ReasignProspectByProspect,
|
||||
module='sale_opportunity_management', type_='wizard')
|
||||
Pool.register(
|
||||
module='sale_opportunity_management', type_='report')
|
||||
|
||||
36
call.py
@@ -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
@@ -3,11 +3,28 @@
|
||||
this repository contains the full copyright notices and license terms. -->
|
||||
<tryton>
|
||||
<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">
|
||||
<field name="name">Calls</field>
|
||||
<field name="res_model">sale.call</field>
|
||||
<field name="domain"
|
||||
eval="[('operator_who_called', '=', Eval('_user'))]" pyson="1"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="call_view_tree">
|
||||
<field name="model">sale.call</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="name">call_form</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.act_window.view" id="act_call_tree_view1">
|
||||
<field name="sequence" eval="10"/>
|
||||
<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="act_window" ref="act_call_tree"/>
|
||||
</record>
|
||||
|
||||
<menuitem
|
||||
name="Calls"
|
||||
sequence="10"
|
||||
id="menu_calls"/>
|
||||
id="menu_calls"
|
||||
icon="tryton-phone"/>
|
||||
<menuitem
|
||||
parent="menu_calls"
|
||||
sequence="30"
|
||||
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>
|
||||
</tryton>
|
||||
0
core/Call/__init__.py
Normal file
50
core/Call/models/call.py
Normal 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()
|
||||
@@ -1,5 +1,6 @@
|
||||
# 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
|
||||
|
||||
|
||||
@@ -7,5 +8,8 @@ class PendingCall(ModelSQL, ModelView):
|
||||
'Llamada pendiente a un prospecto'
|
||||
__name__ = "sale.pending_call"
|
||||
|
||||
date = fields.Date('Date', required=True)
|
||||
prospect_trace = fields.Many2One('sale.prospect_trace', 'Prospect trace')
|
||||
date = fields.DateTime('Date', required=True)
|
||||
|
||||
def get_rec_name(self, name):
|
||||
if self.date:
|
||||
return str(self.date)
|
||||
47
core/Call/models/pending_task.py
Normal 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'
|
||||
0
core/Prospect/__init__.py
Normal file
0
core/Prospect/models/__init__.py
Normal file
37
core/Prospect/models/contact_method.py
Normal 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
|
||||
80
core/Prospect/models/prospect.py
Normal 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)
|
||||
0
core/Prospect/wizards/__init__.py
Normal file
108
core/Prospect/wizards/assign_operator.py
Normal 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
|
||||
53
core/Prospect/wizards/reassign_prospect_by_operator.py
Normal 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'
|
||||
40
core/Prospect/wizards/reassign_prospect_by_prospect.py
Normal 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'
|
||||
0
core/ProspectTrace/__init__.py
Normal file
0
core/ProspectTrace/models/__init__.py
Normal file
106
core/ProspectTrace/models/prospect_trace.py
Normal 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
|
||||
147
core/ProspectTrace/wizards/make_call.py
Normal 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()
|
||||
40
core/ProspectTrace/wizards/schedule_call.py
Normal 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
0
core/Role/models/__init__.py
Normal file
18
core/Role/models/user.py
Normal 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
35
icons.xml
Normal 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
@@ -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 |
7
icons/tryton-person-negative.svg
Normal 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 |
7
icons/tryton-person-positive.svg
Normal 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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"
|
||||
|
||||
@@ -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>
|
||||
74
prospect.py
@@ -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
|
||||
221
prospect.xml
@@ -3,11 +3,29 @@
|
||||
this repository contains the full copyright notices and license terms. -->
|
||||
<tryton>
|
||||
<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="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 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">
|
||||
<field name="model">sale.prospect</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="name">prospect_form</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.act_window.view" id="act_prospect_tree_view1">
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="view" ref="prospect_view_tree"/>
|
||||
<field name="act_window" ref="act_prospect_tree"/>
|
||||
<field name="sequence" eval="10" />
|
||||
<field name="view" ref="prospect_view_tree" />
|
||||
<field name="act_window" ref="act_prospect_tree" />
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_prospect_form_view1">
|
||||
<field name="sequence" eval="20"/>
|
||||
<field name="view" ref="prospect_view_form"/>
|
||||
<field name="act_window" ref="act_prospect_tree"/>
|
||||
<field name="sequence" eval="20" />
|
||||
<field name="view" ref="prospect_view_form" />
|
||||
<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>
|
||||
|
||||
<menuitem
|
||||
parent="menu_calls"
|
||||
sequence="10"
|
||||
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">
|
||||
<field name="name">Unassigned</field>
|
||||
<field name="res_model">sale.prospect</field>
|
||||
<field name="domain" eval="[('state', '=', 'unassigned')]" pyson="1"/>
|
||||
<record model="ir.ui.menu-res.group" id="menu_prospects_group_prospect">
|
||||
<field name="menu" ref="menu_prospects_tree" />
|
||||
<field name="group" ref="group_prospect" />
|
||||
</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">
|
||||
<field name="name">Contact method</field>
|
||||
<field name="res_model">prospect.contact_method</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="contact_method_view_tree">
|
||||
<field name="model">prospect.contact_method</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="name">contact_method_form</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.act_window.view" id="act_contact_method_tree_view1">
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="view" ref="contact_method_view_tree"/>
|
||||
<field name="act_window" ref="act_contact_method_tree"/>
|
||||
<field name="sequence" eval="10" />
|
||||
<field name="view" ref="contact_method_view_tree" />
|
||||
<field name="act_window" ref="act_contact_method_tree" />
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_contact_method_form_view1">
|
||||
<field name="sequence" eval="20"/>
|
||||
<field name="view" ref="contact_method_view_form"/>
|
||||
<field name="act_window" ref="act_contact_method_tree"/>
|
||||
<field name="sequence" eval="20" />
|
||||
<field name="view" ref="contact_method_view_form" />
|
||||
<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>
|
||||
</data>
|
||||
</tryton>
|
||||
@@ -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]
|
||||
@@ -3,6 +3,22 @@
|
||||
this repository contains the full copyright notices and license terms. -->
|
||||
<tryton>
|
||||
<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">
|
||||
<field name="model">sale.prospect_trace</field>
|
||||
<field name="type">tree</field>
|
||||
@@ -15,6 +31,9 @@ this repository contains the full copyright notices and license terms. -->
|
||||
</record>
|
||||
<record model="ir.action.act_window" id="act_prospect_trace_tree">
|
||||
<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>
|
||||
</record>
|
||||
<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 model="ir.action.act_window.domain" id="act_prospect_trace_domain_with_pending_calls">
|
||||
<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="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_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">
|
||||
<field name="name">All</field>
|
||||
<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="act_window" ref="act_prospect_trace_tree"/>
|
||||
</record>
|
||||
|
||||
<menuitem
|
||||
parent="menu_calls"
|
||||
sequence="20"
|
||||
id="menu_prospect_trace_tree"
|
||||
icon="tryton-target"
|
||||
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>
|
||||
</tryton>
|
||||
@@ -6,7 +6,8 @@ class Interest():
|
||||
('0', '0 - Not answered'),
|
||||
('1', '1 - Complete disinterest'),
|
||||
('2', '2 - Middle interest'),
|
||||
('3', '3 - High interest')
|
||||
('3', '3 - High interest'),
|
||||
('4', '4 - Closed sale')
|
||||
]
|
||||
|
||||
return interest_levels
|
||||
|
||||
35
tests/scenario_assign_prospect_to_me.rst
Normal 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
|
||||
|
||||
@@ -7,7 +7,7 @@ Imports::
|
||||
|
||||
>>> from proteus import Model, Wizard
|
||||
>>> from trytond.tests.tools import activate_modules
|
||||
>>> from datetime import date, timedelta
|
||||
>>> from datetime import date, timedelta, datetime
|
||||
>>> import xml.etree.ElementTree as ET
|
||||
|
||||
Activate modules::
|
||||
@@ -42,71 +42,97 @@ El administrador deberá poder registrar los contactos de diferentes prospectos,
|
||||
|
||||
Crear prospecto::
|
||||
>>> Prospect = Model.get('sale.prospect')
|
||||
>>> prospect = 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 = Prospect()
|
||||
|
||||
>>> 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')
|
||||
>>> medellin, = City.find([('code', '=', 'CO-05001')])
|
||||
>>> prospect.city = medellin
|
||||
>>> prospect.save()
|
||||
>>> prospect1.city = medellin
|
||||
>>> prospect1.business_unit = 'brigade'
|
||||
>>> prospect1.save()
|
||||
|
||||
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)]
|
||||
>>> prospect.contact_methods[0].contact_type
|
||||
>>> prospect1.contact_methods[0].contact_type
|
||||
'mobile'
|
||||
>>> prospect.contact_methods[0].job
|
||||
>>> prospect1.contact_methods[0].job
|
||||
'Gerente R.H'
|
||||
>>> prospect.contact_methods[2].name
|
||||
>>> prospect1.contact_methods[2].name
|
||||
'Peralto'
|
||||
>>> prospect.contact_methods[2].value
|
||||
>>> prospect1.contact_methods[2].value
|
||||
'peralto@guchitos.org'
|
||||
|
||||
>>> prospect.city.code
|
||||
>>> prospect1.city.code
|
||||
'CO-05001'
|
||||
>>> prospect.department.code
|
||||
>>> prospect1.department.code
|
||||
'CO-05'
|
||||
>>> prospect.state
|
||||
>>> prospect1.business_unit
|
||||
'brigade'
|
||||
>>> prospect1.state
|
||||
'unassigned'
|
||||
|
||||
Asignar operario a un prospecto::
|
||||
>>> User = Model.get('res.user')
|
||||
>>> user, = User.find([('name', '=', 'Administrator')])
|
||||
>>> prospect.assigned_operator = user
|
||||
Crear segundo prospecto::
|
||||
>>> prospect2 = Prospect()
|
||||
|
||||
>>> prospect.assigned_operator.name
|
||||
'Administrator'
|
||||
>>> prospect.state
|
||||
'assigned'
|
||||
>>> prospect2.name = 'Modernitus S.A.S'
|
||||
>>> contact_method = prospect2.contact_methods.new(value='3122390987', name='Pepe', job='Jefe de ventas')
|
||||
|
||||
Remover operario asignado de un prospecto::
|
||||
>>> prospect.assigned_operator = None
|
||||
>>> prospect.state
|
||||
'unassigned'
|
||||
>>> City = Model.get('sale.city')
|
||||
>>> bogota, = City.find([('code', '=', 'CO-11001')])
|
||||
>>> prospect2.city = bogota
|
||||
>>> 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
|
||||
------------------------------------
|
||||
**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
|
||||
-----------------------
|
||||
**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 programar una llamada para luego obtener un reporte de trabajo pendiente**
|
||||
|
||||
["Crear Campo para registro de la fecha de la llamada"]
|
||||
@@ -155,38 +181,64 @@ Seguimiento de prospecto 2
|
||||
* Seguimiento de prospecto al que pertence
|
||||
|
||||
|
||||
Crear seguimiento de prospecto::
|
||||
Verificar creación de seguimiento de prospecto::
|
||||
>>> ProspectTrace = Model.get('sale.prospect_trace')
|
||||
>>> prospect_trace = ProspectTrace()
|
||||
|
||||
>>> prospect_trace.prospect = prospect
|
||||
>>> prospect_trace.save()
|
||||
>>> prospect_trace, = ProspectTrace.find([('prospect', '=', prospect1)])
|
||||
|
||||
>>> prospect_trace.prospect.name
|
||||
'guchito S.A.S'
|
||||
>>> prospect_trace.prospect_business_unit
|
||||
'brigade'
|
||||
>>> prospect_trace.prospect_city.name
|
||||
'Medellín'
|
||||
>>> prospect_trace.prospect_contact.value
|
||||
'31223425234'
|
||||
>>> prospect_trace.prospect_contact.contact_type
|
||||
'mobile'
|
||||
>>> prospect_trace.prospect_contact.job
|
||||
'Gerente R.H'
|
||||
>>> prospect_trace.prospect_assigned_operator.name
|
||||
'Administrator'
|
||||
>>> prospect_trace.prospect_contacts
|
||||
[proteus.Model.get('prospect.contact_method')(1), proteus.Model.get('prospect.contact_method')(2), proteus.Model.get('prospect.contact_method')(3)]
|
||||
|
||||
Crear llamadas a un seguimiento de prospecto desde el seguimiento de prospecto::
|
||||
>>> call1 = prospect_trace.calls.new(description='First call', interest='0')
|
||||
>>> call2 = prospect_trace.calls.new(description='Second call', interest='1')
|
||||
>>> call3 = prospect_trace.calls.new(description='Third call', interest='3')
|
||||
>>> prospect_trace.save()
|
||||
Agregar un método de contacto desde el seguimiento de prospecto::
|
||||
>>> contact_method_ = prospect_trace.prospect_contacts.new(value='31231231212', name='Carlos', job='Supervisor')
|
||||
>>> contact_method_.prospect
|
||||
proteus.Model.get('sale.prospect')(1)
|
||||
|
||||
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
|
||||
'missed_call'
|
||||
>>> prospect_trace.calls[0].call_type
|
||||
'first_call'
|
||||
>>> prospect_trace.calls[0].date == date.today()
|
||||
True
|
||||
|
||||
>>> prospect_trace.calls[0].call_business_unit
|
||||
'brigade'
|
||||
>>> prospect_trace.calls[0].operator_who_called.name
|
||||
'Administrator'
|
||||
>>> prospect_trace.calls[1].call_result
|
||||
'answered_call'
|
||||
>>> prospect_trace.calls[1].call_type
|
||||
@@ -194,46 +246,143 @@ Verificar estado final del seguimiento del prospecto y sus llamadas
|
||||
|
||||
>>> prospect_trace.calls
|
||||
[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
|
||||
'3'
|
||||
>>> prospect_trace.state
|
||||
'open'
|
||||
'with_pending_calls'
|
||||
|
||||
Programar una próxima llamada pendiente al seguimiento de prospecto::
|
||||
>>> pending_call4 = prospect_trace.pending_calls.new(date = date.today() + timedelta(days=7))
|
||||
>>> prospect_trace.save()
|
||||
|
||||
>>> prospect_trace.pending_calls
|
||||
[proteus.Model.get('sale.pending_call')(1)]
|
||||
>>> schedule = Wizard('sale.prospect_trace.schedule', [prospect_trace])
|
||||
>>> schedule.form.date_time = datetime(2023, 8, 14, 15, 30, 30)
|
||||
>>> schedule.execute('schedule')
|
||||
|
||||
>>> prospect_trace.pending_call.date
|
||||
datetime.datetime(2023, 8, 14, 15, 30, 30)
|
||||
>>> prospect_trace.state
|
||||
'with_pending_calls'
|
||||
|
||||
Crear una llamada agendada previamente:
|
||||
>>> call4 = prospect_trace.calls.new(description='fourth call', interest='3')
|
||||
Crear una llamada agendada previamente::
|
||||
>>> 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
|
||||
'open'
|
||||
|
||||
Cuando se asigna prospecto sin método de contacto mobile, el contacto en el seguimiento es vacío::
|
||||
>>> prospect2 = Prospect()
|
||||
>>> prospect2.name = 'Sin celulares S.A.S'
|
||||
>>> contact_method = prospect2.contact_methods.new(contact_type='mail', value='felpucio@sincelulares.org', name='felpucio', job='Supervisor')
|
||||
>>> prospect2.save()
|
||||
Hacer llamada y programar tarea::
|
||||
>>> make_call = Wizard('sale.prospect_trace.make_call', [prospect_trace])
|
||||
>>> make_call.form.description = 'Prospect told me to send him an email'
|
||||
>>> make_call.form.interest = '3'
|
||||
>>> 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()
|
||||
>>> prospect_trace2.prospect = prospect2
|
||||
>>> Task = Model.get('sale.pending_task')
|
||||
>>> 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
|
||||
--------
|
||||
["Crear un reporte en el que evidencie por operario y consolidado"]
|
||||
|
||||
["Cantidad de llamadas realizadas en un período de tiempo"]
|
||||
|
||||
["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)"]
|
||||
|
||||
|
||||
@@ -243,7 +392,7 @@ Reportes
|
||||
* Observaciones
|
||||
* 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
|
||||
|
||||
|
||||
@@ -257,3 +406,4 @@ Reportes
|
||||
* Reporte de prospectos potenciales
|
||||
* llamadas con un nivel de interés alto
|
||||
* Seguimiento de prospecto al que pertenecen las llamadas
|
||||
|
||||
|
||||
@@ -24,6 +24,12 @@ class RequiredValuesTestCase(ModuleTestCase):
|
||||
Prospect = pool.get('sale.prospect')
|
||||
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()
|
||||
def test_value_en_ContactMethod_es_obligatorio(self):
|
||||
pool = Pool()
|
||||
|
||||
@@ -4,9 +4,10 @@ depends:
|
||||
ir
|
||||
res
|
||||
xml:
|
||||
icons.xml
|
||||
user.xml
|
||||
call.xml
|
||||
prospect_trace.xml
|
||||
prospect.xml
|
||||
pending_call.xml
|
||||
./locations/departments.xml
|
||||
./locations/cities.xml
|
||||
12
user.xml
Normal 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>
|
||||
19
view/assign_start_form.xml
Normal 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>
|
||||
@@ -18,4 +18,6 @@ this repository contains the full copyright notices and license terms. -->
|
||||
|
||||
<label name="call_result"/>
|
||||
<field name="call_result"/>
|
||||
<label name="call_business_unit"/>
|
||||
<field name="call_business_unit"/>
|
||||
</form>
|
||||
|
||||
@@ -8,4 +8,6 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<field name="call_result" expand="1"/>
|
||||
<field name="prospect_trace" expand="1"/>
|
||||
<field name="description" expand="1"/>
|
||||
<field name="call_business_unit" expand="1"/>
|
||||
<field name="operator_who_called" expand="1"/>
|
||||
</tree>
|
||||
@@ -4,7 +4,6 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<tree editable="1">
|
||||
<field name="contact_type" expand="1"/>
|
||||
<field name="value" expand="1"/>
|
||||
<field name="prospect" expand="1"/>
|
||||
<field name="name" expand="1"/>
|
||||
<field name="job" expand="1"/>
|
||||
</tree>
|
||||
|
||||
7
view/make_call_ask_form.xml
Normal 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>
|
||||
7
view/make_call_ask_task_form.xml
Normal 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>
|
||||
18
view/make_call_start_form.xml
Normal 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>
|
||||
12
view/pending_task_form.xml
Normal 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>
|
||||
@@ -2,6 +2,7 @@
|
||||
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
this repository contains the full copyright notices and license terms. -->
|
||||
<tree>
|
||||
<field name="date" expand="1"/>
|
||||
<field name="prospect_trace" expand="1"/>
|
||||
<field name="description" expand="1"/>
|
||||
<button name="close_task"/>
|
||||
</tree>
|
||||
@@ -6,6 +6,9 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<label name="name"/>
|
||||
<field name="name"/>
|
||||
|
||||
<label name="business_unit"/>
|
||||
<field name="business_unit"/>
|
||||
|
||||
<newline/>
|
||||
<label 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">
|
||||
<field name="contact_methods" colspan="2"/>
|
||||
</page>
|
||||
<page string="About prospect" id="contact_methods">
|
||||
<label name="rating"/>
|
||||
<field name="rating"/>
|
||||
<newline/>
|
||||
<label name="comments"/>
|
||||
<field name="comments"/>
|
||||
</page>
|
||||
</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>
|
||||
@@ -5,8 +5,8 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<group col="6" colspan="5" id="header" yalign="0">
|
||||
<label name="prospect"/>
|
||||
<field name="prospect"/>
|
||||
<label name="prospect_contact"/>
|
||||
<field name="prospect_contact"/>
|
||||
<label name="prospect_business_unit"/>
|
||||
<field name="prospect_business_unit"/>
|
||||
<label name="prospect_city"/>
|
||||
<field name="prospect_city"/>
|
||||
</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">
|
||||
<label name="current_interest"/>
|
||||
<field name="current_interest"/>
|
||||
<label name="active"/>
|
||||
<field name="active" xexpand="0" width="25"/>
|
||||
</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/>
|
||||
<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"/>
|
||||
<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>
|
||||
@@ -3,9 +3,10 @@
|
||||
this repository contains the full copyright notices and license terms. -->
|
||||
<tree>
|
||||
<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="current_interest" 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>
|
||||
@@ -3,7 +3,9 @@
|
||||
this repository contains the full copyright notices and license terms. -->
|
||||
<tree>
|
||||
<field name="name" expand="1"/>
|
||||
<field name="business_unit" expand="1"/>
|
||||
<field name="department" 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>
|
||||
10
view/reassign_by_operator_form.xml
Normal 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>
|
||||
@@ -2,9 +2,8 @@
|
||||
<!-- 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"/>
|
||||
<field name="date"/>
|
||||
|
||||
<label name="prospect_trace"/>
|
||||
<field name="prospect_trace"/>
|
||||
<label name="prospect"/>
|
||||
<field name="prospect"/>
|
||||
<label name="new_operator"/>
|
||||
<field name="new_operator"/>
|
||||
</form>
|
||||
7
view/schedule_start_form.xml
Normal 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
@@ -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>
|
||||