Beispiel #1
0
class Adjustment(models.TransientModel):
    _name = 'xopgi.unrealized_gl_adjustment'

    wizard = fields.Many2one('xopgi.unrealized_gl_wizard')
    account = fields.Many2one('account.account')

    account_name = fields.Char(related="account.name")
    account_code = fields.Char(related="account.code")
    account_currency = fields.Many2one(related="account.currency_id")

    foreign_balance = fields.Float(compute='_compute_all', default=0)
    balance = fields.Float(compute='_compute_all', default=0)
    adjusted_balance = fields.Float(compute='_compute_all', default=0)
    gainloss = fields.Float(compute='_compute_all', default=0)

    @api.depends('account', 'wizard')
    def _compute_all(self):
        precision = DecimalPrecision.precision_get('Account')
        company_currency = self.env.user.company_id.currency_id
        # Map records to accounts so that we can compute the balances in a single
        # DB query
        account_map = dict(zip(self.mapped('account.id'), self))
        assert len(account_map) == len(self)
        close_date = self[0].wizard.close_date
        tables, where_clause, where_params = AccountMoveLine.with_context(
            state='posted', date_to=close_date)._query_get()
        if not tables:
            tables = '"account_move_line"'
        if where_clause.strip():
            filters = [where_clause]
        else:
            filters = []
        filters.append('"account_move_line"."account_id" IN %s')
        where_params.append(tuple(account_map.keys()))
        query = ('''
            SELECT account_id AS id,
                   COALESCE(SUM(debit), 0) - COALESCE(SUM(credit), 0) AS balance,
                   COALESCE(SUM(amount_currency), 0) as foreign_balance
               FROM {tables}
               WHERE {filters}
               GROUP BY account_id
        ''').format(tables=tables, filters=' AND '.join(filters))
        self.env.cr.execute(query, where_params)
        for row in self.env.cr.dictfetchall():
            record = account_map.pop(int(
                row['id']))  # cast to int, otherwise KeyError
            account = record.account
            record.balance = balance = row['balance']
            record.foreign_balance = row['foreign_balance']
            record.adjusted_balance = adjusted = account.currency_id.with_context(
                date=close_date).compute(
                    record.foreign_balance,
                    company_currency,
                    round=False,
                )
            record.gainloss = round(adjusted - balance, precision)
        for record in account_map.values():
            record.balance = record.foreign_balance = 0
            record.adjusted_balance = record.gainloss = 0
Beispiel #2
0
class B(models.Model):
    _name = 'model.b'

    name = fields.Char()
    modela_id = fields.Many2one('model.a', 'Merge')
    melda_ids = fields.Many2many(comodel_name='model.a',
                                 column1='meld_column1_id',
                                 column2='meld_column2_id',
                                 string='relation')
    partner_id = fields.Many2one('res.partner', 'Parent')
Beispiel #3
0
class RecurrentEvent(models.Model):
    _name = 'cdr.recurrent.event'
    _description = "Recurrent CDR event"

    _inherits = {
        'cdr.system.event': 'event_id',
        'cdr.recurrent.event.def': 'recurrence_def'
    }

    event_id = fields.Many2one('cdr.system.event',
                               required=True,
                               ondelete='cascade')

    time = fields.Float()

    recurrence_def = fields.Many2one('cdr.recurrent.event.def',
                                     required=True,
                                     ondelete='cascade')

    def update_event(self, value):
        '''Update the fields next call, state and action for an event.
        When an event is evaluated is necessary to update its values.

        '''
        next_call = self.recurrence_def.next_date(self.recurrence_def.rrule)
        state = 'raising' if value else 'not_raising'
        action = 'raise' if state == 'raising' else 'do_nothing'
        values = dict(next_call=next_call, state=state, action=action)
        self.write(values)

    def evaluate(self, cycle):
        '''Evaluate the recurrent event in a evaluation cycle.

        '''
        if isinstance(cycle, int):
            cycle = self.env['cdr.evaluation.cycle'].browse(cycle)
        try:
            value = self.event_id._evaluate()
        except Exception:
            logger.exception('Error evaluating event %s defined as: ',
                             self.name, self.definition)
            return None
        else:
            self.update_event(value)

    @api.model
    @api.returns('self', lambda value: value.id)
    def create(self, vals):
        if any(field in self.recurrence_def._fields for field in vals):
            vals.update(is_recurrent=True)
        return super(RecurrentEvent, self).create(vals)
Beispiel #4
0
class FieldMergeWayRel(models.Model):
    _name = 'field.merge.way.rel'

    name = fields.Many2one('ir.model.fields', required=True)
    merge_way = fields.Many2one('field.merge.way', required=True)
    model = fields.Many2one('ir.model', required=True)

    @api.multi
    def meld(self, sources, target):
        res = {}
        for item in self:
            val = item.merge_way.apply(sources, target, item.name)
            if val is not None:
                res[item.name.name] = val
        return res
Beispiel #5
0
class StatementLine(models.Model):
    _inherit = 'account.bank.statement.line'

    # In order to be able to efficiently create the statement from the
    # payments I need to keep the relation of each line which the payment that
    # generated it.
    created_from_payment_id = fields.Many2one('account.payment')

    @api.multi
    def _reconcile_from_payments(self):
        self = self.filtered(lambda l: l.created_from_payment_id and l.
                             created_from_payment_id._payment_move_line)
        for line in self:
            line.process_reconciliation(
                payment_aml_rec=line.created_from_payment_id._payment_move_line
            )

    @api.multi
    def button_cancel_reconciliation(self):
        # Allow to revert automatic reconciliations of any date.
        automatically_created = self.filtered(
            lambda l: l.created_from_payment_id and l.created_from_payment_id.
            _payment_move_line)
        res = super(
            StatementLine,
            automatically_created.with_context(
                check_move_validity=False)).button_cancel_reconciliation()
        other = self - automatically_created
        if other:
            res = super(StatementLine, other).button_cancel_reconciliation()
        return res
Beispiel #6
0
class CommonThreadWizard(models.TransientModel):
    _name = 'common.thread.wizard'

    model_id = fields.Selection(
        string='Model',
        selection=lambda self: get_model_selection(self),
        required=True)
    view = fields.Many2one('xopgi.selectable.view')
    views_count = fields.Integer(compute='_get_views_count')

    @api.onchange('model_id')
    @api.depends('model_id')
    def _get_views_count(self):
        selectable_view = self.env['xopgi.selectable.view']
        for wizard in self:
            if wizard.model_id:
                conf_views = selectable_view.get_views(self.model_id)
                views_count = len(conf_views)
                self.views_count = views_count
                if views_count >= 1:
                    self.view = conf_views[0]
            else:
                self.views_count = 0
                self.view = False

    def get_thread_action(self, res_id=None):
        """ Returns the action that shows the form of this model
        """
        return self.view.get_action(model=self.model_id, res_id=res_id)
Beispiel #7
0
class crm_valias(models.Model):
    _name = 'crm.valias'
    _inherit = ['xopgi.mail.alias.mocker']

    type = fields.Selection(
        [('lead', 'Lead'), ('opportunity', 'Opportunity')],
        'Type',
        index=True,
        help="Type of object to create by incoming messages.")

    if MAJOR_ODOO_VERSION < 9:
        section_id = fields.Many2one(TEAM_MODEL_NAME, string='Sale Team')
    else:
        team_id = fields.Many2one(TEAM_MODEL_NAME, string='Sale Team')

    user_id = fields.Many2one('res.users', string='Team Leader')
Beispiel #8
0
class AccountConfigSettings(models.TransientModel):
    _inherit = "account.config.settings"

    ugl_journal_id = fields.Many2one(related="company_id.ugl_journal_id")

    ugl_gain_account_id = fields.Many2one(
        related="company_id.ugl_gain_account_id")

    ugl_loss_account_id = fields.Many2one(
        related="company_id.ugl_loss_account_id")

    @api.onchange('ugl_journal_id')
    def _update_ugl_gain_and_loss_account(self):
        if not self.ugl_gain_account_id:
            self.ugl_gain_account_id = self.ugl_journal_id.default_credit_account_id
        if not self.ugl_loss_account_id:
            self.ugl_loss_account_id = self.ugl_journal_id.default_debit_account_id
Beispiel #9
0
    class AnalyticAccount(models.Model):
        _inherit = models.get_modelname(AccountAnalyticAccount)

        manager_id = fields.Many2one(
            'res.users',
            'Account Manager',
            track_visibility='onchange'
        )
Beispiel #10
0
class Partner(models.Model):
    _inherit = 'res.partner'

    property_account_payment_advance_id = fields.Many2one(
        'account.account',
        company_dependent=True,
        string="Pre-payment Account",
        domain=REGULAR_ACCOUNT_DOMAIN,
        help=("This account will be used instead of the default one as the"
              "payment advance account for the current partner"),
        required=True)

    property_account_receivable_advance_id = fields.Many2one(
        'account.account',
        company_dependent=True,
        string="Pre-collection Account",
        domain=REGULAR_ACCOUNT_DOMAIN,
        help=("This account will be used instead of the default one as the"
              "receivable advance account for the current partner"),
        required=True)
Beispiel #11
0
class XopgiBoardWidget(models.Model):
    _name = WIDGET_MODEL_NAME
    _description = "Board Widget"

    _order = 'category, name'

    name = fields.Char(translate=True)
    category = fields.Many2one('ir.module.category')
    template_name = fields.Char(required=True)
    xml_template = fields.Text(translate=True)
    python_code = fields.Text()

    @api.multi
    def name_get(self):
        '''Returns a list with id, name of widgets or name's template

        '''
        return [(item.id, item.name or item.template_name) for item in self]

    def get_widgets_dict(self):
        '''Returns a dictionary list that represents the widgets that the user
        has access to.

        '''
        widgets = self.env[WIDGET_REL_MODEL_NAME].get_widgets()
        logger.debug('Widgets to show %r' % [w['name'] for w in widgets])
        today = normalize_datetime(fields.Date.today(self))
        for widget in widgets:
            self._eval_python_code(widget, today)
        return widgets

    def _eval_python_code(self, widget, today):
        '''Evaluate the python code of a widget

        '''
        python_code = widget.get('python_code', '')
        if not python_code:
            return
        name = widget.get('name', '')
        env = self.env
        local_dict = locals()
        local_dict.update(globals().get('__builtins__', {}))
        try:
            logger.debug('Starting evaluation of Python code for widget %s' %
                         name)
            safe_eval(python_code, local_dict, mode='exec', nocopy=True)
            logger.debug('Python code for widget %s evaluated sussefully.' %
                         name)
        except ValueError:
            logger.exception(
                'An error happen trying to execute the Python '
                'code for \'%s\' board widget, python code: %s', name,
                python_code)
        widget.update(local_dict.get('result', {}))
Beispiel #12
0
class XopgiBoardWidgetRel(models.AbstractModel):
    _name = WIDGET_REL_MODEL_NAME
    _order = 'priority'

    widget = fields.Many2one(WIDGET_MODEL_NAME,
                             delegate=True,
                             required=True,
                             ondelete='cascade')
    priority = fields.Integer(default=1000)

    def get_widgets(self):
        """ Get all widget dicts for uid and sorts them by priority.

        """
        models = self.get_widget_capable_models()
        widgets = sorted(itertools.chain(
            *[list(model.get_user_widgets()) for model in models]),
                         key=operator.attrgetter('priority'))
        result = []
        # Adding missing widget
        for widget in widgets:
            widget.get_set_widgets(result)
        return result

    def get_user_widgets(self):
        """ It must be implemented on extended models.

        Should return a recordset of user's corresponding widgets.

        """
        raise NotImplementedError()

    def get_widget_capable_models(self):
        """ Get a list of models instances that have `get_user_widgets` item

        """
        result = []
        for model in self.env.registry.values():
            if hasattr(model, "get_user_widgets"):
                if model._name != WIDGET_REL_MODEL_NAME:
                    result.append(self.env[model._name])
        return result

    def get_set_widgets(self, result):
        """ Update in-place result adding missing widgets.

        """
        for widget in self.read(fields=[
                'name', 'category', 'template_name', 'xml_template',
                'python_code'
        ]):
            widget.pop('id', None)
            if widget not in result:
                result.append(widget)
Beispiel #13
0
class Company(models.Model):
    _inherit = "res.company"

    ugl_journal_id = fields.Many2one(
        'account.journal',
        'Unrealized gain & loss journal',
        domain=GENERAL_JOURNAL_DOMAIN,
    )

    ugl_gain_account_id = fields.Many2one(
        'account.account',
        'Unrealized gain account',
        domain=REGULAR_ACCOUNT_DOMAIN,
    )

    ugl_loss_account_id = fields.Many2one(
        'account.account',
        'Unrealized loss account',
        domain=REGULAR_ACCOUNT_DOMAIN,
    )
Beispiel #14
0
class Invoice(models.Model):
    '''An account invoice.

    Parent's agency field and use it in search view.

    '''
    _inherit = 'account.invoice'

    partner_company = fields.Many2one(related='partner_id.parent_id',
                                      string="Partner's Company",
                                      store=True)
Beispiel #15
0
class PriceType(models.Model):
    """The price type is used to points which field in the product form is a price
       and in which currency is this price expressed.  When a field is a
       price, you can use it in pricelists to base sale and purchase prices
       based on some fields of the product.

    """
    _name = "product.price.type"
    _description = "Price Type"

    @api.model
    def _get_currency(self):
        comp = self.env.user.company_id
        if not comp:
            comp = self.env['res.company'].search([], limit=1)
        return comp.currency_id.id

    active = fields.Boolean(
        string="Active",
        default=True
    )
    name = fields.Char(
        string='Price Name',
        required=True,
        translate=True,
        help="Name of this kind of price."
    )
    field = fields.Selection(
        selection="_price_field_get",
        string="Product Field",
        size=32,
        required=True,
        help="Associated field in the product form."
    )
    currency_id = fields.Many2one(
        comodel_name='res.currency',
        string="Currency",
        required=True,
        default=_get_currency,
        help="The currency the field is expressed in."
    )

    @api.model
    def _price_field_get(self):
        mf = self.env['ir.model.fields']
        fields = mf.search(
            [('model', 'in', (('product.product'), ('product.template'))),
             ('ttype', '=', 'float')]
        )
        res = []
        for field in fields:
            if not (field.name, field.field_description) in res:
                res.append((field.name, field.field_description))
        return res
Beispiel #16
0
class project_valias(models.Model):
    _name = 'project.valias'
    _inherit = ['xopgi.mail.alias.mocker']

    @api.model
    def _get_models(self):
        models = _get_model_ids(self)
        return models.name_get()

    alias_model_id = fields.Selection(
        _get_models,
        'Aliased Model',
        required=True,
        help=("The model (OpenERP Document Kind) to "
              "which this alias corresponds. Any "
              "incoming email that does not reply to an "
              "existing record will cause the creation "
              "of a new record of this model "
              "(e.g. a Project Task)"))

    project_id = fields.Many2one('project.project', string='Project')
    user_id = fields.Many2one('res.users', string='Project Manager')
Beispiel #17
0
class ResGroupWidget(models.Model):
    _name = 'res.group.widget'
    _order = 'group, priority'
    _inherit = WIDGET_REL_MODEL_NAME

    group = fields.Many2one(get_modelname(res_groups), required=True)

    def get_user_widgets(self):
        '''Returns the groups in which the current user is located

        '''
        return self.sudo().search([('group.users', '=', self._uid)
                                   ]).sudo(self._uid)
Beispiel #18
0
class NewThreadWizard(models.TransientModel):
    _name = 'new.thread.wizard'
    _inherit = 'common.thread.wizard'

    message_id = fields.Many2one('mail.message', 'Message', readonly=True)
    leave_msg = fields.Boolean(
        'Preserve original message',
        default=True,
        help="Check for no remove message from original thread.")

    @api.model
    def fields_view_get(self,
                        view_id=None,
                        view_type='form',
                        toolbar=False,
                        submenu=False):
        context = dict(self._context or {})
        if self._uid != SUPERUSER_ID and not self.env['res.users'].has_group(
                'xopgi_mail_new_thread.group_new_thread'):
            raise exceptions.AccessDenied()
        if view_type == 'form':
            if (len(context.get('active_ids', [])) > 1
                    or not context.get('default_message_id', False)):
                raise exceptions.ValidationError(
                    _('You should select one and only one message.'))
        result = super(NewThreadWizard,
                       self).fields_view_get(view_id=view_id,
                                             view_type=view_type,
                                             toolbar=toolbar,
                                             submenu=submenu)
        return result

    @api.multi
    def confirm(self):
        '''Create a new mail thread, post new message, remove original
        message if not leave_msg and open new thread on edit form.

        '''
        thread_model = self.env[self.model_id]
        self.ensure_one()
        context = dict(wizard_id=self.id,
                       new_thread_from_mail_msg=True,
                       thread_model=self.model_id)
        rec_name = thread_model.fields_get().get(
            thread_model._rec_name or 'name', False)
        if rec_name and rec_name['type'] in ('char', 'text'):
            name = self.message_id.subject or self.message_id.display_name
            if name:
                context['default_%s' %
                        (thread_model._rec_name or 'name')] = name
        return self.with_context(**context).get_thread_action()
Beispiel #19
0
class SelectableView(models.Model):
    _name = 'xopgi.selectable.view'
    _rec_name = 'label'
    _order = 'priority'

    label = fields.Char(translate=True, required=True)
    model_id = fields.Selection(
        string='Model',
        selection=lambda self: get_model_selection(self),
        required=True)
    view = fields.Many2one('ir.ui.view', required=True)
    priority = fields.Integer(default=16)

    def get_views(self, model):
        domain = [('model_id', '=', model)]
        return self.search(domain)

    def get_action(self, model=None, target='current', res_id='None'):
        """ Return an ir.actions.act_window
        """
        res = dict(target=target)
        if self:
            view = self[0]
            model = view.model_id

        if res_id is not None:
            # If the recordset is empty in Odoo8 it returns an empty list.In
            # odoo10 gives error.
            values_action = self.env[model].browse(res_id).get_access_action()

            # If values_action contains a list it is because get_acess_action
            # was executed #in odoo8 and returns a [{}], in odoo10 returns a {}.
            if isinstance(values_action, list):
                values_action = values_action[0]
            res = dict(values_action, **res)
        else:
            values = {
                'type': 'ir.actions.act_window',
                'res_model': model,
                'view_type': 'form',
                'view_mode': 'form',
                'views': [(self.view.id, 'form')],
                'context': self._context
            }
            res = dict(values, **res)
        return res

    @api.multi
    def try_selected_view(self):
        return self.get_action(target='new')
Beispiel #20
0
class A(models.Model):
    _name = 'model.a'

    add_char = fields.Char()
    add_text = fields.Text()
    price_int = fields.Integer()
    cost_float = fields.Float()
    min_int = fields.Integer()
    max_int = fields.Integer()
    active = fields.Boolean(default=True)
    parent_id = fields.Many2one('model.a', 'Parent')
    meldb_ids = fields.Many2many(comodel_name='model.b',
                                 column1='meld_column2_id',
                                 column2='meld_column1_id',
                                 string='relation')
Beispiel #21
0
class AnalyticAccount(models.Model):
    _inherit = get_modelname(AccountAnalyticAccount)

    parent_id = fields.Many2one(comodel_name='account.analytic.account',
                                string='Parent Analytic Account',
                                select=2)
    child_ids = fields.One2many(comodel_name='account.analytic.account',
                                inverse_name='parent_id',
                                string='Child Accounts')

    @api.multi
    def check_recursion(self, parent=None):
        return super(AnalyticAccount, self)._check_recursion(parent=parent)

    _constraints = [
        (check_recursion,
         'Error! You cannot create recursive analytic accounts.',
         ['parent_id']),
    ]
Beispiel #22
0
def BoardValue(xml_id, model, module, **kwargs):
    '''A board value field.

    A value is a M2O field stored like as an XML_ID of the `model` and
    `module` provided.

    To be used within the model 'xopgi.board.config' to introduce further
    values or configuration.

    '''
    module = module.rsplit('.', 1)[-1]  # Allow module to be fully qualified
                                        # so you can say __name__  # noqa
    return fields.Many2one(
        model,
        compute=lambda self: self._compute_value(xml_id, module=module),
        inverse=lambda self: self._set_value(xml_id, model, module=module),
        default=lambda self: self._compute_value(xml_id, module=module),
        **kwargs
    )
Beispiel #23
0
class PurchaseOrder(models.Model):
    _inherit = models.get_modelname(PurchaseOrder)

    bid_date = fields.Datetime(
        string='Bid Received On',
        readonly=True,
        help="Date on which the bid was received"
    )

    state = fields.Selection([
        ('draft', 'Draft PO'),
        ('sent', 'RFQ Sent'),
        ('bid', 'Bid Received'),
        ('to approve', 'To Approve'),
        ('purchase', 'Purchase Order'),
        ('done', 'Done'),
        ('cancel', 'Cancelled')])

    incoterm_id = fields.Many2one(
        'stock.incoterms',
        string="Incoterm",
        help=("International Commercial Terms are a series of predefined "
              "commercial terms used in international transactions.")
    )

    @api.multi
    def accion_bid_received(self):
        return self.write({'state': 'bid', 'bid_date': fields.Datetime.now()})

    @api.multi
    def button_confirm(self):
        approve = self.browse()
        for order in self:
            if order.state in ['draft', 'sent', 'bid']:
                approve |= order
                # Odoo 9+ skips orders that are not in 'draft', or 'sent'.  So
                # we trick it to process orders in state 'bid'.
                if order.state == 'bid':
                    order.state = 'sent'
        return super(PurchaseOrder, approve).button_confirm()
Beispiel #24
0
class MergePartnerGroup(models.Model):
    """A group of partner which are deemed duplicates.

    - Is a partner when `parent_id` points to another instance of the same
      type representing the group.

    - Is a group when has no `parent_id`  and several partners point to here.
      In this case the referenced partner is the destination partner.

    """
    _name = 'xopgi.partner.merge.group'
    _order = "name asc"

    dest_partner_id = fields.Many2one(
        'res.partner',
        string='Destination partner'
    )

    partner_ids = fields.Many2many(
        comodel_name='res.partner',
        relation='xopgi_partner_merge_group_partners',
        column1='category_id',
        column2='partner_id',
        string='Partners'
    )

    name = fields.Char(
        related=('dest_partner_id', 'name'),
        string='Name',
        readonly=True,
        store=True,
    )

    @api.multi
    @mute_logger('openerp.osv.expression', 'openerp.models')
    def merge(self):
        """Merge several `partners` into a single destination partner.

        Original `partners` will be removed from the DB afterwards.  Only
        target will remain.  All references to the original partners
        will be re-establish to the target partner.

        If `partners` constains less that 2 partners, do nothing.  All
        partners must have the same email.

        If sources `partner` is none, the target partner defaults to the last
        created record in `partners`.

        :param sources: The source partners.
        :type sources: A recordset of 'res.partners'.

        :param target: The target partner.
        :type target: A singleton recordset of 'res.partners' or None.

        """
        sources = self.partner_ids
        target = self.dest_partner_id
        if sources.sudo().exists() and len(sources) < 2:
            raise UserError(_("Constains less that 2 partners, do nothing"))
        partner_different_emails = {
            p.email
            for p in sources
            if p.email and p.email.strip()
        }
        if len(partner_different_emails) > 1:
            user = self.env.user
            if user.has_group('xopgi_partner_merge.base_parter_merger'):
                object_merger = self.env['object.merger']
                object_merger.merge(sources, target)
            else:
                raise UserError(
                    _("All contacts must have the same email. Only the "
                      "users with Partner Merge rights can merge contacts "
                      "with different emails.")
                )
        object_merger = self.env['object.merger']
        object_merger.merge(sources, target)
        self.unlink()
Beispiel #25
0
class BasicEvent(models.Model):
    _name = 'cdr.basic.event'
    _description = 'Basic CDR event'

    _inherits = {'cdr.system.event': 'event_id'}

    event_id = fields.Many2one('cdr.system.event',
                               required=True,
                               ondelete='cascade')

    interval = fields.Float(
        required=True,
        help='Time (in hours:minutes format) between evaluations.')

    time_to_wait = fields.Float(
        required=True,
        help='Time (in hours:minutes format) getting '
        'consecutive positive evaluations before raise.')

    times_to_raise = fields.Integer(
        help='Waiting time to launch an event while an evidence is true in a '
        'time interval')

    @api.depends('interval')
    def get_next_call(self):
        '''Compute the next evaluation date.

        '''
        for event in self:
            if event.active and event.interval:
                event.next_call = datetime.now() + timedelta(
                    hours=event.interval)
            else:
                event.next_call = False

    def update_event(self, value, cycle):
        '''Update the fields next call, state and action for an event.
        When an event is evaluated is necessary to update its values.

        '''
        next_call = str2dt(cycle.create_date) + timedelta(hours=self.interval)
        # If the interval is less or equal zero that means that the event does
        # not wait any time to launch.
        if self.interval <= 0:
            times_to_raise = -1
        else:
            times_to_raise = ((self.time_to_wait / self.interval)
                              if not value else self.times_to_raise - 1)
        state = 'raising' if value and times_to_raise < 1 else 'not_raising'
        if self.state == 'raising':
            action = 'continue_raising' if state == 'raising' else 'stop_raising'
        else:
            action = 'raise' if state == 'raising' else 'do_nothing'
        values = dict(next_call=next_call, state=state, action=action)
        self.write(values)

    def evaluate(self, cycle):
        '''Evaluate the basic event in a evaluation cycle.

        '''
        if isinstance(cycle, int):
            cycle = self.env['cdr.evaluation.cycle'].browse(cycle)
        try:
            value = self.event_id._evaluate()
        except Exception:
            logger.exception('Error evaluating event %s defined as: ',
                             self.name, self.definition)
            return None
        else:
            self.update_event(value, cycle)
Beispiel #26
0
class AnalyticLine(models.Model):
    _inherit = get_modelname(AccountAnalyticLine)

    parent_account_id = fields.Many2one(related="account_id.parent_id",
                                        store=True,
                                        readonly=True)
Beispiel #27
0
class XopgiAccountAdvancementConfig(models.AbstractModel):
    _name = 'xopgi.account_adv.config.settings'

    @api.multi
    def get_account_types(self):
        '''Returns a dictionary of type opendict() with the system params:
        advanced_receivable_type_id and advanced_payable_type_id and their
        values.

        '''
        res = opendict()
        get_param = self.env['ir.config_parameter'].get_param
        for param in DEFAULT_ACCOUNT_TYPES:
            value = safe_eval(str(get_param(param))) or False
            res[param] = value
            if self:
                setattr(self, param, value)
        return res

    @api.one
    def _set_account_types(self):
        '''Set the account types Receivable Advanced when fields
        advanced_receivable_type_id or advanced_payable_type_id change and
        update the filter in account settings with the new field values.

        '''
        set_param = self.env['ir.config_parameter'].set_param
        for param in DEFAULT_ACCOUNT_TYPES:
            set_param(param, repr(getattr(self, param).id))

    @api.multi
    def get_advanced_journal_types(self):
        '''Returns a dictionary of type opendict() with the system params:
        and their precollection_journal_type_id and prepayment_journal_type_id
        values.

        '''
        res = opendict()
        get_param = self.env['ir.config_parameter'].get_param
        for param in DEFAULT_ADVANCED_JOURNAL_TYPES:
            value = safe_eval(str(get_param(param))) or False
            res[param] = value
            if self:
                setattr(self, param, value)
        return res

    @api.one
    def _set_advanced_journal_types(self):
        '''Set the advance journal types when fields
        precollection_journal_type_id or prepayment_journal_type_id change and
        update the filter in account settings with the new field values.

        '''
        set_param = self.env['ir.config_parameter'].set_param
        for param in DEFAULT_ADVANCED_JOURNAL_TYPES:
            set_param(param, repr(getattr(self, param).id))

    advanced_receivable_type_id = fields.Many2one(
        'account.account.type',
        compute='get_account_types',
        inverse='_set_account_types',
        default=lambda self: self.get_account_types().
        advanced_receivable_type_id,
        help='The type of pre-collection accounts.',
    )

    advanced_payable_type_id = fields.Many2one(
        'account.account.type',
        compute='get_account_types',
        inverse='_set_account_types',
        default=lambda self: self.get_account_types().advanced_payable_type_id,
        help='The type of the pre-payment accounts.',
    )

    precollection_journal_type_id = fields.Many2one(
        'account.journal',
        compute='get_advanced_journal_types',
        inverse='_set_advanced_journal_types',
        default=lambda self: self.get_advanced_journal_types().
        precollection_journal_type_id,
        help='The type of journal for pre-collection accounts',
    )

    prepayment_journal_type_id = fields.Many2one(
        'account.journal',
        compute='get_advanced_journal_types',
        inverse='_set_advanced_journal_types',
        default=lambda self: self.get_advanced_journal_types().
        prepayment_journal_type_id,
        help='The type of journal for pre-payment accounts',
    )
Beispiel #28
0
class Product(models.Model):
    _inherit = 'product.template'

    product_manager = fields.Many2one('res.users', string="Manager")
Beispiel #29
0
class BounceRecord(models.Model):
    '''An index for bounce address to message, thread and recipient.

    This model encodes the same information of old VERP addresses but allow a
    simpler address, like: ``[email protected]``.

    '''
    _name = 'xopgi.verp.record'

    bounce_alias = fields.Char(
        help=('The alias where the bounce was sent to. You may change the'
              'alias configuration midways and this will still work'),
        required=True,
        default='bounces'
    )

    thread_index = fields.Char(
        help='The unique index reference for the thread.',
        required=True,
        index=True,
    )

    message_id = fields.Many2one(
        'mail.message',
        required=True,
        # ondelete=cascade: If the message is delete remove the VERP
        # address.  This happens for invitations, for instance.  The
        # message is create and the bounce address is properly generated,
        # but afterwards the message is removed.  This make the bounce
        # reference ephemeral for these cases, but if the message is lost
        # we won't be able to know who to notify.
        ondelete="cascade",
        help=('The message id originating this notification. This allows '
              'to know who to notify about bounces.')
    )
    reference = fields.Char(
        help='The unique part for the bounce address.',
        size=100,
        required=True,
        index=True,
    )

    recipient = fields.Char(
        help='The recipient for which this VERP address was created',
        required=True,
        index=True,
    )

    _sql_constraints = [
        ('verp_unique', 'unique (reference)', 'The VERP is duplicated.'),
    ]

    @api.model
    @api.returns('self', lambda r: r.reference)  # return the reference
    def create(self, vals):
        try:
            from openerp.addons.mail.xopgi.index import generate_reference
        except ImportError:
            try:  # Odoo 9
                from openerp.addons.mail.models.xopgi.index import generate_reference
            except ImportError:  # Odoo 10
                from odoo.addons.mail.models.xopgi.index import generate_reference
        reference = generate_reference(
            lambda r: self.search([('reference', '=', r)]),
            start=3,
            lower=True
        )
        assert reference
        vals.update(reference=reference)
        return super(BounceRecord, self).create(vals)

    @api.model
    def cleanup(self):
        '''Remove all bounce address references that are too old.

        This should be called in a cron task.

        '''
        self._cr.execute('''
           WITH aged AS (
              SELECT id, ((NOW() at time zone 'UTC') - create_date) AS age
              FROM xopgi_verp_record
           ) SELECT id FROM aged WHERE age >= %s
        ''', ('10 days', ))
        elders = [row[0] for row in self._cr.fetchall()]
        if elders:
            self.sudo().browse(elders).unlink()
class AccountAnalyticAccount(models.Model):
    _inherit = 'account.analytic.account'

    # The formulae for the commission to salesmen is based on a simple
    # projection from the desired sales margin (required_margin, max_margin)
    # to the commission margins salesmen earn per sale.
    #
    # The projection is simply a geometrical projection: required_margin
    # yields the minimal commission margin, the max_margin yields the maximal
    # commission margin.  Accounts that fail to achieve the required_margin
    # yield 0% commission, accounts that surpass the max_margin yields just
    # the maximal commission margin.
    #
    # The projection follows the scheme below:
    #
    #     min margin ----------X------------------ max margin
    #                          |
    #     min com    ----------V------------------ max comm
    #
    # X represents the actual margin, V represents the resultant commission
    # margin.
    #
    # V = min_comm * alpha + max_comm * (1 - alpha), where alpha is given by
    # the formula:
    #
    # alpha = (max_margin - X)/(max_margin - min_margin)
    #
    # if alpha < 0: alpha = 0   # Too much margin
    #
    # if V < min_comm: V = 0
    #
    # Each account may have any desired required_margin and max_margin,
    # provided the required_margin is greater or equal of is parent account
    # and the max_margin is less or equal than the parent account.
    #
    # The account may have any desired min commission and max commission, no
    # restrictions are placed.
    #
    # By default, accounts take all these parameters from its parent. So it's
    # wise to only establish them at accounts that consolidate operational
    # branches.
    #

    required_margin = fields.Float(
        string='Required margin',
        help=('This is the minimum margin required for an operation. If 0 '
              'no commission will be calculated.'),
        required=False,
        default=0,
        track_visibility='onchange',
        digits=dp.get_precision('Account'),
    )
    max_margin = fields.Float(
        string='Maximum margin',
        help='This is maximum margin allowed for an operation.',
        required=False,
        default=0,
        track_visibility='onchange',
        digits=dp.get_precision('Account'),
    )
    min_commission_margin = fields.Float(
        string='Minimum commission margin',
        help='This is minimum margin for commissions.',
        required=False,
        default=0,
        track_visibility='onchange',
        digits=dp.get_precision('Account'),
    )
    max_commission_margin = fields.Float(
        string='Maximum commission margin',
        help='This is maximum margin for commissions.',
        required=False,
        default=0,
        track_visibility='onchange',
        digits=dp.get_precision('Account'),
    )

    current_required_margin = fields.Float(
        compute=_compute_from_branch('required_margin',
                                     'current_required_margin', default=0),
        digits=dp.get_precision('Account'),
    )
    current_max_margin = fields.Float(
        compute=_compute_from_branch('max_margin',
                                     'current_max_margin', default=0),
        digits=dp.get_precision('Account'),
    )
    current_min_comm = fields.Float(
        compute=_compute_from_branch('min_commission_margin',
                                     'current_min_comm', default=0),
        digits=dp.get_precision('Account'),
    )
    current_max_comm = fields.Float(
        compute=_compute_from_branch('max_commission_margin',
                                     'current_max_comm', default=0),
        digits=dp.get_precision('Account'),
    )

    percentage_margin = fields.Float(
        string='Margin %', help='Percentage margin related to credit.',
        compute='_compute_commission',
        digits=dp.get_precision('Account'),
    )

    percentage_commission = fields.Float(
        string='Commission %',
        help='Percentage commission related to profit.',
        compute='_compute_commission',
        digits=dp.get_precision('Account'),
    )

    commission = fields.Float(
        string='Commission', help='Commission related to profit.',
        compute='_compute_commission',
        digits=dp.get_precision('Account'),
    )

    invoiced = fields.Float(
        string='Invoiced',
        help=('The amount invoiced for this account, '
              'discounting refunds.'),
        compute='_compute_invoiced',
        digits=dp.get_precision('Account'),
        store=True,
        compute_sudo=True
    )

    expended = fields.Float(
        string='Expended',
        help=('The amount expended for this account, '
              'discounting refunds.'),
        compute='_compute_invoiced',
        digits=dp.get_precision('Account'),
        store=True,
        compute_sudo=True
    )

    amount_undefined = fields.Float(
        string='Undefined',
        help=('Total that cannot be accounted as invoiced or expended because '
              'it is not attached to an invoice'),
        compute='_compute_invoiced',
        digits=dp.get_precision('Account'),
        store=True,
        compute_sudo=True
    )

    self_balance = fields.Float(
        string='Balance',
        help=('Self balance'),
        compute='_compute_invoiced',
        digits=dp.get_precision('Account'),
        store=True,
        compute_sudo=True
    )

    primary_salesperson_id = fields.Many2one(
        "res.users", string="Salesperson",
        help="Primary salesperson in operation",
        compute="_compute_primary_salesperson",
        store=True
    )

    supplier_invoice_id = fields.Many2one('account.invoice',
                                          ondelete='set null')

    # TODO: Ensure only groups='base.group_sale_manager' can update the
    # commission margins.  So far, only the goodwill of ignorance may save us.
    @api.depends('line_ids')
    def _compute_invoiced(self):
        for record in self:
            invoiced = expended = undefined = 0
            for line in record.line_ids:
                if line.move_id and line.move_id.invoice_id:
                    if line.move_id.invoice_id.type in INCOME_INVOICE_TYPES:
                        invoiced += line.amount
                    else:
                        expended -= line.amount
                else:
                    undefined += line.amount
            record.invoiced = invoiced
            record.expended = expended
            record.amount_undefined = undefined
            record.self_balance = invoiced - expended + undefined

    @api.depends('invoiced', 'self_balance')
    def _compute_commission(self):
        for record in self:
            margin, comm_percent, balance = _compute_margin_commission(record)
            record.percentage_margin = margin * 100
            record.percentage_commission = comm_percent * 100
            record.commission = comm_percent * balance

    @api.depends('line_ids.move_id.invoice_id.user_id')
    def _compute_primary_salesperson(self):
        for account in self:
            if not account.active:
                account.primary_salesperson_id = False
            else:
                user_id = next(
                    (line.move_id.invoice_id.user_id
                     for line in account.line_ids
                     if line and line.move_id and line.move_id.invoice_id
                     if line.move_id.invoice_id.type in INCOME_INVOICE_TYPES
                     if line.move_id.invoice_id.user_id),
                    None
                )
                if user_id:
                    account.primary_salesperson_id = user_id
                else:
                    account.primary_salesperson_id = False

    @api.constrains('required_margin', 'max_margin')
    def _validate_margins(self):
        for record in self:
            required_margin = record.required_margin
            max_margin = record.max_margin
            if bool(required_margin) != bool(max_margin):
                # Either both are set or both are unset
                raise ValidationError('You must set both required_margin '
                                      'and max_margin or none.')
            if required_margin < 0 or required_margin > 100:
                raise_validation_error('required minimum margin')
            if max_margin < 0 or max_margin > 100:
                raise_validation_error('maximum allowed margin')
            if required_margin and max_margin and \
               required_margin >= max_margin:
                raise ValidationError('required margin must be less that '
                                      'max margin.')
            parent = record.parent_id
            if parent and parent.id:
                if parent.required_margin and required_margin and \
                   required_margin < parent.required_margin:
                    raise ValidationError(
                        'You cannot lower the required margin')
                if parent.max_margin and max_margin and \
                   max_margin > parent.max_margin:
                    raise ValidationError(
                        'You cannot raise the maximum margin')
            # TODO: If the children enter in violation what to do?
            # for child in record.complete_child_ids:
            #     child._validate_margins()

    @api.constrains('min_commission_margin', 'max_commission_margin')
    def _validate_commission_margins(self):
        for record in self:
            min_comm = record.min_commission_margin
            max_comm = record.max_commission_margin
            if bool(min_comm) != bool(max_comm):
                # Either both are set or both are unset
                    raise ValidationError('You must set both min commission '
                                          'and max commission or none.')
            if min_comm < 0 or min_comm > 100:
                raise_validation_error('minimum commission margin')
            if max_comm < 0 or max_comm > 100:
                raise_validation_error('maximum commission margin')
            if min_comm and max_comm and min_comm >= max_comm:
                raise ValidationError('min commission must be less that '
                                      'max commission.')