Esempio n. 1
0
class ResPartner(models.Model):
    _inherit = 'res.partner'

    @api.one
    @api.depends('phone_ids.phone', 'phone_ids.type')
    def _compute_partner_phone(self):
        phone = mobile = fax = False
        for partner_phone in self.phone_ids:
            not_phone_type = ('2_mobile', '4_home_fax', '5_office_fax')
            if partner_phone.type not in not_phone_type:
                phone = partner_phone.phone
            if partner_phone.type == '2_mobile':
                mobile = partner_phone.phone
            if partner_phone.type in ('5_office_fax', '4_home_fax'):
                fax = partner_phone.phone
        self.phone = phone
        self.mobile = mobile
        self.fax = fax

    phone_ids = fields.One2many('res.partner.phone',
                                'partner_id',
                                string='Phones')
    phone = Phone(compute='_compute_partner_phone', store=True, readonly=True)
    mobile = Phone(compute='_compute_partner_phone', store=True, readonly=True)
    fax = Fax(compute='_compute_partner_phone', store=True, readonly=True)
Esempio n. 2
0
class NumberNotFound(models.TransientModel):
    _inherit = "number.not.found"

    to_update_lead_id = fields.Many2one(
        'crm.lead',
        string='Lead to Update',
        domain=[('type', '=', 'lead')],
        help="Lead on which the phone number will be written")
    current_lead_phone = Phone(related='to_update_lead_id.phone',
                               string='Current Phone',
                               readonly=True)
    current_lead_mobile = Phone(related='to_update_lead_id.mobile',
                                string='Current Mobile',
                                readonly=True)

    @api.multi
    def create_lead(self):
        '''Function called by the related button of the wizard'''
        self.ensure_one()

        action = {
            'name': _('Create New Lead'),
            'type': 'ir.actions.act_window',
            'res_model': 'crm.lead',
            'view_mode': 'form,tree',
            'domain': ['|', ('type', '=', 'lead'), ('type', '=', False)],
            'nodestroy': False,
            'target': 'current',
            'context': {
                'default_%s' % self.number_type: self.e164_number,
                'default_type': 'lead',
                'stage_type': 'lead',
                'needaction_menu_ref': 'crm.menu_crm_opportunities',
            },
        }
        return action

    @api.multi
    def update_lead(self):
        self.ensure_one()
        if not self.to_update_lead_id:
            raise UserError(_("Select the Lead to Update."))
        self.to_update_lead_id.write({self.number_type: self.e164_number})
        action = {
            'name': _('Lead: %s' % self.to_update_lead_id.name),
            'type': 'ir.actions.act_window',
            'res_model': 'crm.lead',
            'view_mode': 'form,tree',
            'nodestroy': False,
            'target': 'current',
            'res_id': self.to_update_lead_id.id,
            'context': {
                'stage_type': 'lead',
                'needaction_menu_ref': 'crm.menu_crm_opportunities',
            },
        }
        return action
Esempio n. 3
0
class ResPartnerPhone(models.Model):
    _name = 'res.partner.phone'
    _order = 'partner_id, type'
    _phone_name_sequence = 8

    partner_id = fields.Many2one('res.partner', string='Related Partner')
    type = fields.Selection([('1_home', 'Home'), ('2_mobile', 'Mobile'),
                             ('3_office', 'Office'),
                             ('4_home_fax', 'Home Fax'),
                             ('5_office_fax', 'Office Fax'),
                             ('6_phone_fax_home', 'Phone/fax Home'),
                             ('7_other', 'Other')],
                            string='Phone Type',
                            required=True)
    phone = Phone('Phone', required=True, partner_field='partner_id')
    note = fields.Char('Note')

    def name_get(self):
        res = []
        for pphone in self:
            if pphone.partner_id:
                if self._context.get('callerid'):
                    name = pphone.partner_id.name_get()[0][1]
                else:
                    name = u'%s (%s)' % (pphone.phone, pphone.partner_id.name)
            else:
                name = pphone.phone
            res.append((pphone.id, name))
        return res
Esempio n. 4
0
class ResPartner(models.Model):
    _inherit = 'res.partner'

    @api.model
    def convert_from_international_to_e164(self, phone_num):
        res = False
        try:
            res_parse = phonenumbers.parse(phone_num)
            res = phonenumbers.format_number(
                res_parse, phonenumbers.PhoneNumberFormat.E164)
        except:
            pass
        return res

    # without this convert, we would have in DB:
    # E.164 format in res_partner_phone table
    # phonenumbers.PhoneNumberFormat.INTERNATIONAL in res_partner
    # TODO bug: but even with this, it doesn't work, the format
    # is stored in international format in res_partner
    # => I'll try to find the reason later

    @api.multi
    @api.depends('phone_ids.phone', 'phone_ids.type')
    def _compute_partner_phone(self):
        for partner in self:
            phone = mobile = fax = False
            for partner_phone in partner.phone_ids:
                num_e164 = self.convert_from_international_to_e164(
                    partner_phone.phone)
                if num_e164:
                    if partner_phone.type == '2_mobile':
                        mobile = num_e164
                    elif partner_phone.type in ('5_office_fax', '4_home_fax'):
                        fax = num_e164
                    else:
                        phone = num_e164
            partner.phone = phone
            partner.mobile = mobile
            partner.fax = fax

    phone_ids = fields.One2many('res.partner.phone',
                                'partner_id',
                                string='Phones')
    phone = Phone(compute='_compute_partner_phone', store=True, readonly=True)
    mobile = Phone(compute='_compute_partner_phone', store=True, readonly=True)
    fax = Fax(compute='_compute_partner_phone', store=True, readonly=True)
Esempio n. 5
0
class CrmLead(models.Model):
    _inherit = 'crm.lead'
    _phone_name_sequence = 20

    phone = Phone(country_field='country_id', partner_field='partner_id')
    mobile = Phone(country_field='country_id', partner_field='partner_id')
    fax = Fax(country_field='country_id', partner_field='partner_id')
    phonecall_ids = fields.One2many(
        'crm.phonecall', 'opportunity_id', string='Phone Calls')
    phonecall_count = fields.Integer(
        compute='_count_phonecalls', string='Number of Phonecalls',
        readonly=True)

    @api.multi
    def name_get(self):
        if self._context.get('callerid'):
            res = []
            for lead in self:
                if lead.partner_name and lead.contact_name:
                    name = u'%s (%s)' % (lead.contact_name, lead.partner_name)
                elif lead.partner_name:
                    name = lead.partner_name
                elif lead.contact_name:
                    name = lead.contact_name
                else:
                    name = lead.name
                res.append((lead.id, name))
            return res
        else:
            return super(CrmLead, self).name_get()

    @api.multi
    @api.depends('phonecall_ids')
    def _count_phonecalls(self):
        cpo = self.env['crm.phonecall']
        for lead in self:
            try:
                lead.phonecall_count = cpo.search_count(
                    [('opportunity_id', '=', lead.id)])
            except:
                lead.phonecall_count = 0
Esempio n. 6
0
class HrApplicant(models.Model):
    _inherit = 'hr.applicant'
    _phone_name_sequence = 50

    partner_phone = Phone(partner_field='partner_id')
    partner_mobile = Phone(partner_field='partner_id')

    @api.multi
    def name_get(self):
        if self._context.get('callerid'):
            res = []
            for appl in self:
                if appl.partner_id:
                    name = u'%s (%s)' % (appl.partner_id.name, appl.name)
                elif appl.partner_name:
                    name = u'%s (%s)' % (appl.partner_name, appl.name)
                else:
                    name = appl.name
                res.append((appl.id, name))
            return res
        else:
            return super(HrApplicant, self).name_get()
Esempio n. 7
0
class ResPartnerPhone(models.Model):
    _name = 'res.partner.phone'
    _order = 'partner_id, type'

    partner_id = fields.Many2one('res.partner', string='Related Partner')
    type = fields.Selection([('1_home', 'Home'), ('2_mobile', 'Mobile'),
                             ('3_office', 'Office'),
                             ('4_home_fax', 'Home Fax'),
                             ('5_office_fax', 'Office Fax'),
                             ('6_phone_fax_home', 'Phone/fax Home'),
                             ('7_other', 'Other')],
                            string='Phone Type',
                            required=True)
    phone = Phone('Phone', required=True, partner_field='partner_id')
    note = fields.Char('Note')
Esempio n. 8
0
class SmsChildRequest(models.Model):
    _name = 'sms.child.request'
    _inherit = ['mail.thread', 'ir.needaction_mixin']
    _description = 'SMS Child request'
    _rec_name = 'child_id'
    _order = 'date desc'

    sender = Phone(partner_field='partner_id',
                   country_field='country_id')
    date = fields.Datetime(required=True, default=fields.Datetime.now)
    full_url = fields.Char(compute='_compute_full_url')
    step1_url_id = fields.Many2one('link.tracker')
    step1_url = fields.Char('Step 1 URL', related='step1_url_id.short_url')
    step2_url_id = fields.Many2one('link.tracker')
    step2_url = fields.Char('Step 2 URL', related='step2_url_id.short_url')
    state = fields.Selection([
        ('new', 'Request received'),
        ('child_reserved', 'Child reserved'),
        ('step1', 'Step 1 completed'),
        ('step2', 'Step 2 completed'),
        ('expired', 'Request expired')
    ], default='new', track_visibility='onchange')
    partner_id = fields.Many2one('res.partner', 'Partner')
    country_id = fields.Many2one(
        'res.country', related='partner_id.country_id', readonly=True)
    child_id = fields.Many2one(
        'compassion.child', 'Child', ondelete='set null')
    hold_id = fields.Many2one('compassion.hold', related='child_id.hold_id')
    event_id = fields.Many2one(
        'crm.event.compassion', 'Event',
        domain=[('accepts_sms_booking', '=', True)],
        compute='_compute_event', inverse='_inverse_event', store=True
    )
    sponsorship_id = fields.Many2one('recurring.contract', 'Sponsorship')
    sponsorship_confirmed = fields.Boolean('Sponsorship confirmed')
    lang_code = fields.Char('Language', required=True)
    source = fields.Char()

    # Filter criteria made by sender
    gender = fields.Selection([
        ('Male', 'Male'),
        ('Female', 'Female')
    ])
    min_age = fields.Integer(size=2)
    max_age = fields.Integer(size=2, default=DEFAULT_MAX_AGE)
    field_office_id = fields.Many2one(
        'compassion.field.office', 'Field Office')

    is_trying_to_fetch_child = fields.Boolean(
        help="This is set to true when a child is currently being fetched. "
             "It prevents to fetch multiple children.")
    sms_reminder_sent = fields.Boolean(default=False)
    has_filter = fields.Boolean(compute='_compute_has_filter')

    @api.multi
    def _compute_full_url(self):
        for request in self:
            if request.state == 'step1':
                request.full_url = request.step2_url
            else:
                request.full_url = request.step1_url

    @api.multi
    @api.depends('date')
    def _compute_event(self):
        limit_date = datetime.today() - relativedelta(days=7)
        for request in self.filtered('date'):
            event_id = self.env['crm.event.compassion'].search([
                ('accepts_sms_booking', '=', True),
                ('start_date', '<=', request.date),
                ('start_date', '>=', fields.Datetime.to_string(limit_date))
            ], order='start_date desc', limit=1)
            # event_id is None if start_date of most recent event is>1 week old
            request.event_id = event_id

    def _inverse_event(self):
        # Allows to manually set an event
        return True

    @api.multi
    def _compute_has_filter(self):
        for request in self:
            request.has_filter = request.gender or request.min_age or \
                request.field_office_id or (request.max_age and
                                            request.max_age != DEFAULT_MAX_AGE)

    @api.model
    def create(self, vals):
        if 'partner_id' not in vals:
            # Try to find a matching partner given phone number
            phone = vals.get('sender')
            partner_obj = self.env['res.partner']
            partner = partner_obj.search(['|', ('mobile', 'like', phone),
                                          ('phone', 'like', phone),
                                          '|', ('active', '=', True),
                                          ('active', '=', False)])
            if partner and len(partner) == 1:
                vals['partner_id'] = partner.id
                vals['lang_code'] = partner.lang
        request = super(SmsChildRequest, self).create(vals)
        base_url = self.env['ir.config_parameter'].get_param(
            'web.external.url') + '/'
        request.write({
            'step1_url_id': self.env['link.tracker'].sudo().create({
                'url': base_url + request.lang_code +
                '/sms_sponsorship/step1/' + str(request.id),
            }).id,
            'is_trying_to_fetch_child': True
        })
        # Directly commit for the job to work
        if not test_mode:
            self.env.cr.commit()  # pylint: disable=invalid-commit
        request.with_delay(priority=5).reserve_child()
        return request

    @api.onchange('partner_id')
    def onchange_partner_id(self):
        phone = self.partner_id.mobile or self.partner_id.phone
        if phone:
            self.sender = phone
        self.lang_code = self.partner_id.lang

    @api.multi
    def change_child(self):
        """ Release current child and take another."""
        self.hold_id.write({'sms_request_id': False})
        self.write({
            'state': 'new',
            'child_id': False,
            'is_trying_to_fetch_child': True
        })
        return self.reserve_child()

    @api.multi
    def cancel_request(self):
        self.hold_id.write({'sms_request_id': False})
        return self.write({
            'state': 'expired',
            'child_id': False
        })

    @job(default_channel='root.sms_request')
    @related_action(action='related_action_sms_request')
    def reserve_child(self):
        """Finds a child for SMS sponsorship service.
        Try to fetch a child in the event allocation pool or
        put a new child on hold for the sms request.
        """
        self.ensure_one()
        child_fetched = False
        if self.event_id:
            child_fetched = self._take_child_from_event()
        if not child_fetched:
            child_fetched = self.take_child_from_childpool()
        self.is_trying_to_fetch_child = False
        return child_fetched

    def complete_step1(self, sponsorship_id):
        """
        Create short link for step2, send confirmation to partner.
        :param sponsorship_id: id of the new sponsorship.
        :return: True
        """
        self.ensure_one()
        base_url = self.env['ir.config_parameter'].get_param(
            'web.external.url') + '/'
        self.write({
            'sponsorship_id': sponsorship_id,
            'state': 'step1',
            'step2_url_id': self.env['link.tracker'].sudo().create({
                'url': base_url + self.lang_code + '/sms_sponsorship/step2/' +
                str(sponsorship_id)
            }).id
        })
        self.partner_id.sms_send_step1_confirmation(self)
        return True

    def complete_step2(self):
        """
        Send confirmation to partner and update state.
        :return: True
        """
        self.ensure_one()
        self.partner_id.set_privacy_statement(origin='sms_sponsorship')
        self.partner_id.sms_send_step2_confirmation(self)
        return self.mark_done()

    def mark_done(self):
        """
        Simply put the request in done state (in case it was processed
        manually).
        :return: True
        """
        return self.write({'state': 'step2'})

    def take_child_from_childpool(self):
        try:
            childpool_search = self.env[
                'compassion.childpool.search'].create({
                    'take': 1,
                    'gender': self.gender,
                    'min_age': self.min_age,
                    'max_age': self.max_age,
                    'field_office_ids': [(6, 0,
                                          self.field_office_id.ids or [])]
                })
            childpool_search.with_context(skip_value=1000).do_search()
            # Request is valid two days, reminder is sent one day after
            expiration = datetime.now() + relativedelta(days=2)
            result_action = self.env['child.hold.wizard'].with_context(
                active_id=childpool_search.id, async_mode=False).create({
                    'type': HoldType.E_COMMERCE_HOLD.value,
                    'expiration_date': fields.Datetime.to_string(expiration),
                    'primary_owner': self.env.uid,
                    'event_id': self.event_id.id,
                    'campaign_id': self.event_id.campaign_id.id,
                    'ambassador': self.event_id.user_id.partner_id.id or
                    self.env.uid,
                    'channel': 'sms',
                    'source_code': 'sms_sponsorship',
                    'return_action': 'view_holds'
                }
            ).send()
            child_hold = self.env['compassion.hold'].browse(
                result_action['domain'][0][2])
            child_hold.sms_request_id = self.id
            if child_hold.state == 'active':
                self.write({
                    'child_id': child_hold.child_id.id,
                    'state': 'child_reserved'
                })
                _logger.info("SMS child directly taken from global pool")
                return True
            else:
                _logger.error("SMS child couldn't be put on hold from global "
                              "pool")
                return False
        except:
            _logger.error("Error during SMS child reservation", exc_info=True)
            self.env.cr.rollback()
            self.env.invalidate_all()
            return False
        finally:
            self.is_trying_to_fetch_child = False

    def _take_child_from_event(self):
        """ Search in the allocated children for the event.
        """
        available_children = self.event_id.hold_ids.filtered(
            lambda h: h.state == 'active' and h.channel == 'sms' and not
            h.sms_request_id).mapped('child_id')
        selected_children = available_children - self.child_id
        if self.has_filter:
            selected_children = selected_children.filtered(
                self.check_child_parameters)

        if selected_children:
            # Take a random child among the selection
            index = randint(0, len(selected_children) - 1)
            child = selected_children[index]
            self.write({
                'child_id': child.id,
                'state': 'child_reserved'
            })
            child.hold_id.sms_request_id = self.id
            # Put a new child in event buffer
            self.event_id.with_delay().hold_children_for_sms(1)
            _logger.info("SMS child taken from event pool")
            return True
        return False

    # TODO more than one field_office_id for some countries
    def check_child_parameters(self, child):
        """
        Used to filter children and tells if child corresponds to search
        filters activated in the SMS request.
        :param child: <compassion.child> record
        :return: True if child meets search criterias
        """
        gender_match = not self.gender or self.gender[0] == child.gender
        min_age_match = not self.min_age or self.min_age <= child.age
        max_age_match = not self.max_age or self.max_age >= child.age
        country_match = not self.field_office_id or \
            self.field_office_id == child.field_office_id

        return gender_match and min_age_match and max_age_match and \
            country_match

    @api.multi
    def send_step1_reminder(self):
        """ Can be extended to use a SMS API and send a reminder to user. """
        self.ensure_one()
        self.write({'sms_reminder_sent': True})

    @api.model
    def _needaction_domain_get(self):
        """
        Used to display a count icon in the menu
        :return: domain of jobs counted
        """
        return [('state', 'in', ['new', 'child_reserved', 'step1'])]

    @api.model
    def sms_reminder_cron(self):
        """
        CRON job that sends SMS reminders to people that didn't complete
        step 1.
        :return: True
        """
        sms_requests = self.search([
            ('sms_reminder_sent', '=', False),
            ('date', '<', fields.Date.today()),
            ('state', 'in', ['new', 'child_reserved']),
        ])
        for request in sms_requests:
            request.with_context(lang=request.lang_code).send_step1_reminder()
        return True

    @api.model
    def sms_notification_unfinished_cron(self):
        """
        CRON job that sends mails weekly, which notify the staff that some SMS
        Sponsorship are ongoing.
        :return: True
        """
        nb_sms_requests = self.search_count([
            ('state', 'in', ['new', 'child_reserved', 'step1']),
        ])

        # send staff notification
        notify_ids = self.env['staff.notification.settings'].get_param(
            'new_partner_notify_ids')
        if nb_sms_requests and notify_ids:
            self.message_post(
                body=_("{} partner(s) have ongoing SMS Sponsorship").format(
                    nb_sms_requests),
                subject=_("{} SMS Sponsorship {} ongoing").format(
                    nb_sms_requests,
                    _('is') if nb_sms_requests <= 1 else _('are')),
                partner_ids=notify_ids,
                type='comment',
                subtype='mail.mt_comment',
                content_subtype='plaintext'
            )
Esempio n. 9
0
class EventRegistration(models.Model):
    _inherit = 'event.registration'
    _phone_name_sequence = 100

    phone = Phone(partner_field='partner_id')
Esempio n. 10
0
class CrmPhonecall(models.Model):
    _name = 'crm.phonecall'
    _inherit = ['mail.thread']
    _order = "id desc"

    # Restore the object that existed in v8
    # and doesn't exist in v9 community any more
    name = fields.Char(string='Call Summary',
                       required=True,
                       track_visibility='onchange')
    date = fields.Datetime(string='Date',
                           track_visibility='onchange',
                           copy=False,
                           default=lambda self: fields.Datetime.now())
    description = fields.Text(string='Description', copy=False)
    company_id = fields.Many2one('res.company',
                                 string='Company',
                                 default=lambda self: self.env['res.company'].
                                 _company_default_get('crm.phonecall'))
    user_id = fields.Many2one('res.users',
                              string='Responsible',
                              track_visibility='onchange',
                              default=lambda self: self.env.user)
    team_id = fields.Many2one(
        'crm.team',
        string='Sales Team',
        track_visibility='onchange',
        default=lambda self: self.env['crm.team']._get_default_team_id())
    partner_id = fields.Many2one('res.partner',
                                 string='Contact',
                                 ondelete='cascade')
    partner_phone = Phone(string='Phone', partner_field='partner_id')
    partner_mobile = Phone(string='Mobile', partner_field='partner_id')
    priority = fields.Selection([('0', 'Low'), ('1', 'Normal'), ('2', 'High')],
                                string='Priority',
                                track_visibility='onchange',
                                default='1')
    opportunity_id = fields.Many2one('crm.lead',
                                     string='Lead/Opportunity',
                                     ondelete='cascade',
                                     track_visibility='onchange')
    state = fields.Selection(
        [
            ('open', 'To Do'),
            ('done', 'Held'),
            ('cancel', 'Cancelled'),
        ],
        string='Status',
        default='open',
        copy=False,
        required=True,
        track_visibility='onchange',
        help='The status is set to Confirmed, when a case is created.\n'
        'When the call is over, the status is set to Held.\n'
        'If the call is not applicable anymore, the status can be set to '
        'Cancelled.')
    direction = fields.Selection([
        ('inbound', 'Inbound'),
        ('outbound', 'Outbound'),
    ],
                                 string='Type',
                                 required=True,
                                 default='outbound')

    @api.onchange('partner_id')
    def onchange_partner_id(self):
        if self.partner_id:
            self.partner_phone = self.partner_id.phone
            self.partner_mobile = self.partner_id.mobile

    @api.onchange('opportunity_id')
    def onchange_opportunity_id(self):
        if self.opportunity_id:
            self.partner_phone = self.opportunity_id.phone
            self.partner_mobile = self.opportunity_id.mobile
            self.team_id = self.opportunity_id.team_id.id
            self.partner_id = self.opportunity_id.partner_id.id

    @api.multi
    def schedule_another_call(self):
        self.ensure_one()
        cur_call = self[0]
        ctx = self._context.copy()
        ctx.update({
            'default_date': False,
            'default_partner_id': cur_call.partner_id.id,
            'default_opportunity_id': cur_call.opportunity_id.id,
            'default_direction': 'outbound',
            'default_partner_phone': cur_call.partner_phone,
            'default_partner_mobile': cur_call.partner_mobile,
        })
        action = {
            'name': _('Phone Call'),
            'type': 'ir.actions.act_window',
            'res_model': 'crm.phonecall',
            'view_mode': 'form,tree,calendar',
            'context': ctx,
        }
        return action
Esempio n. 11
0
class HrEmployee(models.Model):
    _inherit = 'hr.employee'
    _phone_name_sequence = 30

    work_phone = Phone(country_field='country_id')
    mobile_phone = Phone(country_field='country_id')