Exemple #1
0
 def _get_default_from(self):
     if self.env.user.email:
         return formataddr((self.env.user.name, self.env.user.email))
     raise UserError(_("Unable to send email, please configure the sender's email address."))
Exemple #2
0
 def create(self, vals):
     if 'name' not in vals or vals['name'] == _('New'):
         vals['name'] = self.env['ir.sequence'].next_by_code('stock.scrap') or _('New')
     scrap = super(StockScrap, self).create(vals)
     return scrap
Exemple #3
0
class StockScrap(models.Model):
    _name = 'stock.scrap'
    _order = 'id desc'

    def _get_default_scrap_location_id(self):
        return self.env['stock.location'].search([('scrap_location', '=', True), ('company_id', 'in', [self.env.user.company_id.id, False])], limit=1).id

    def _get_default_location_id(self):
        company_user = self.env.user.company_id
        warehouse = self.env['stock.warehouse'].search([('company_id', '=', company_user.id)], limit=1)
        if warehouse:
            return warehouse.lot_stock_id.id
        return None

    name = fields.Char(
        'Reference',  default=lambda self: _('New'),
        copy=False, readonly=True, required=True,
        states={'done': [('readonly', True)]})
    origin = fields.Char(string='Source Document')
    product_id = fields.Many2one(
        'product.product', 'Product',
        required=True, states={'done': [('readonly', True)]})
    product_uom_id = fields.Many2one(
        'product.uom', 'Unit of Measure',
        required=True, states={'done': [('readonly', True)]})
    tracking = fields.Selection('Product Tracking', readonly=True, related="product_id.tracking")
    lot_id = fields.Many2one(
        'stock.production.lot', 'Lot',
        states={'done': [('readonly', True)]}, domain="[('product_id', '=', product_id)]")
    package_id = fields.Many2one(
        'stock.quant.package', 'Package',
        states={'done': [('readonly', True)]})
    owner_id = fields.Many2one('res.partner', 'Owner', states={'done': [('readonly', True)]})
    move_id = fields.Many2one('stock.move', 'Scrap Move', readonly=True)
    picking_id = fields.Many2one('stock.picking', 'Picking', states={'done': [('readonly', True)]})
    location_id = fields.Many2one(
        'stock.location', 'Location', domain="[('usage', '=', 'internal')]",
        required=True, states={'done': [('readonly', True)]}, default=_get_default_location_id)
    scrap_location_id = fields.Many2one(
        'stock.location', 'Scrap Location', default=_get_default_scrap_location_id,
        domain="[('scrap_location', '=', True)]", required=True, states={'done': [('readonly', True)]})
    scrap_qty = fields.Float('Quantity', default=1.0, required=True, states={'done': [('readonly', True)]})
    state = fields.Selection([
        ('draft', 'Draft'),
        ('done', 'Done')], string='Status', default="draft")
    date_expected = fields.Datetime('Expected Date', default=fields.Datetime.now)

    @api.onchange('picking_id')
    def _onchange_picking_id(self):
        if self.picking_id:
            self.location_id = (self.picking_id.state == 'done') and self.picking_id.location_dest_id.id or self.picking_id.location_id.id

    @api.onchange('product_id')
    def onchange_product_id(self):
        if self.product_id:
            self.product_uom_id = self.product_id.uom_id.id

    @api.model
    def create(self, vals):
        if 'name' not in vals or vals['name'] == _('New'):
            vals['name'] = self.env['ir.sequence'].next_by_code('stock.scrap') or _('New')
        scrap = super(StockScrap, self).create(vals)
        return scrap

    def unlink(self):
        if 'done' in self.mapped('state'):
            raise UserError(_('You cannot delete a scrap which is done.'))
        return super(StockScrap, self).unlink()

    def _get_origin_moves(self):
        return self.picking_id and self.picking_id.move_lines.filtered(lambda x: x.product_id == self.product_id)

    def _prepare_move_values(self):
        self.ensure_one()
        return {
            'name': self.name,
            'origin': self.origin or self.picking_id.name or self.name,
            'product_id': self.product_id.id,
            'product_uom': self.product_uom_id.id,
            'product_uom_qty': self.scrap_qty,
            'location_id': self.location_id.id,
            'scrapped': True,
            'location_dest_id': self.scrap_location_id.id,
            'move_line_ids': [(0, 0, {'product_id': self.product_id.id,
                                           'product_uom_id': self.product_uom_id.id, 
                                           'qty_done': self.scrap_qty,
                                           'location_id': self.location_id.id, 
                                           'location_dest_id': self.scrap_location_id.id,
                                           'package_id': self.package_id.id, 
                                           'owner_id': self.owner_id.id,
                                           'lot_id': self.lot_id.id, })],
#             'restrict_partner_id': self.owner_id.id,
            'picking_id': self.picking_id.id
        }

    @api.multi
    def do_scrap(self):
        for scrap in self:
            move = self.env['stock.move'].create(scrap._prepare_move_values())
            move._action_done()
            scrap.write({'move_id': move.id, 'state': 'done'})
        return True

    def action_get_stock_picking(self):
        action = self.env.ref('stock.action_picking_tree_all').read([])[0]
        action['domain'] = [('id', '=', self.picking_id.id)]
        return action

    def action_get_stock_move_lines(self):
        action = self.env.ref('stock.stock_move_line_action').read([])[0]
        action['domain'] = [('move_id', '=', self.move_id.id)]
        return action

    def action_validate(self):
        self.ensure_one()
        if self.product_id.type != 'product':
            return self.do_scrap()
        precision = self.env['decimal.precision'].precision_get('Product Unit of Measure')
        available_qty = sum(self.env['stock.quant']._gather(self.product_id,
                                                            self.location_id,
                                                            self.lot_id,
                                                            self.package_id,
                                                            self.owner_id,
                                                            strict=True).mapped('quantity'))
        if float_compare(available_qty, self.scrap_qty, precision_digits=precision) >= 0:
            return self.do_scrap()
        else:
            return {
                'name': _('Insufficient Quantity'),
                'view_type': 'form',
                'view_mode': 'form',
                'res_model': 'stock.warn.insufficient.qty.scrap',
                'view_id': self.env.ref('stock.stock_warn_insufficient_qty_scrap_form_view').id,
                'type': 'ir.actions.act_window',
                'context': {
                    'default_product_id': self.product_id.id,
                    'default_location_id': self.location_id.id,
                    'default_scrap_id': self.id
                },
                'target': 'new'
            }
Exemple #4
0
 def _set_product_qty(self):
     """ The meaning of product_qty field changed lately and is now a functional field computing the quantity
     in the default product UoM. This code has been added to raise an error if a write is made given a value
     for `product_qty`, where the same write should set the `product_uom_qty` field instead, in order to
     detect errors. """
     raise UserError(_('The requested operation cannot be processed because of a programming error setting the `product_qty` field instead of the `product_uom_qty`.'))
Exemple #5
0
 def copy(self, default=None):
     self.ensure_one()
     default = dict(default or {}, name=_('%s (copy)') % self.name)
     return super(IrFilters, self).copy(default)
Exemple #6
0
 def _check_ticket_seats_limit(self):
     for record in self:
         if record.event_ticket_id.seats_max and record.event_ticket_id.seats_available < 0:
             raise ValidationError(
                 _('No more available seats for this ticket'))
Exemple #7
0
 def _check_positive_qty_done(self):
     if any([ml.qty_done < 0 for ml in self]):
         raise ValidationError(_('You can not enter negative quantities!'))
Exemple #8
0
    def anonymize_database(self):
        """Sets the 'anonymized' state to defined fields"""
        # pylint: disable=W0101
        raise UserError(
            """The GECOERP Migration Platform no longer accepts anonymized databases.\n
            If you wish for your data to remain private during migration, please contact us at [email protected]"""
        )
        self.ensure_one()

        # create a new history record:
        history = self.env['ir.model.fields.anonymization.history'].create({
            'date':
            fields.Datetime.now(),
            'state':
            'started',
            'direction':
            'clear -> anonymized'
        })

        # check that all the defined fields are in the 'clear' state
        state = self.env['ir.model.fields.anonymization']._get_global_state()
        error_type = _('Error !')
        if state == 'anonymized':
            raise UserError('%s: %s' % (
                error_type,
                _("The database is currently anonymized, you cannot anonymize it again."
                  )))
        elif state == 'unstable':
            raise UserError('%s: %s' % (
                error_type,
                _("The database anonymization is currently in an unstable state. Some fields are anonymized,"
                  " while some fields are not anonymized. You should try to solve this problem before trying to do anything."
                  )))

        # do the anonymization:
        dirpath = os.environ.get('HOME') or os.getcwd()
        rel_filepath = 'field_anonymization_%s_%s.json' % (self.env.cr.dbname,
                                                           history.id)
        abs_filepath = os.path.abspath(os.path.join(dirpath, rel_filepath))

        ano_fields = self.env['ir.model.fields.anonymization'].search([
            ('state', '!=', 'not_existing')
        ])
        if not ano_fields:
            raise UserError(
                '%s: %s' %
                (error_type, _("No fields are going to be anonymized.")))

        data = []

        for field in ano_fields:
            model_name = field.model_id.model
            field_name = field.field_id.name
            field_type = field.field_id.ttype
            table_name = self.env[model_name]._table

            # get the current value
            self.env.cr.execute('select id, "%s" from "%s"' %
                                (field_name, table_name))
            for record in self.env.cr.dictfetchall():
                data.append({
                    "model_id": model_name,
                    "field_id": field_name,
                    "id": record['id'],
                    "value": record[field_name]
                })

                # anonymize the value:
                anonymized_value = None

                sid = str(record['id'])
                if field_type == 'char':
                    anonymized_value = 'xxx' + sid
                elif field_type == 'selection':
                    anonymized_value = 'xxx' + sid
                elif field_type == 'text':
                    anonymized_value = 'xxx' + sid
                elif field_type == 'html':
                    anonymized_value = 'xxx' + sid
                elif field_type == 'boolean':
                    anonymized_value = random.choice([True, False])
                elif field_type == 'date':
                    anonymized_value = '2011-11-11'
                elif field_type == 'datetime':
                    anonymized_value = '2011-11-11 11:11:11'
                elif field_type in ('float', 'monetary'):
                    anonymized_value = 0.0
                elif field_type == 'integer':
                    anonymized_value = 0
                elif field_type in [
                        'binary', 'many2many', 'many2one', 'one2many',
                        'reference'
                ]:  # cannot anonymize these kind of fields
                    raise UserError('%s: %s' % (
                        error_type,
                        _("Cannot anonymize fields of these types: binary, many2many, many2one, one2many, reference."
                          )))

                if anonymized_value is None:
                    raise UserError(
                        '%s: %s' %
                        (error_type, _("Anonymized value can not be empty.")))

                sql = 'update "%(table)s" set "%(field)s" = %%(anonymized_value)s where id = %%(id)s' % {
                    'table': table_name,
                    'field': field_name,
                }
                self.env.cr.execute(sql, {
                    'anonymized_value': anonymized_value,
                    'id': record['id']
                })

        # save json file:
        with open(abs_filepath, 'w') as fn:
            json.dump(data, fn)

        # update the anonymization fields:
        ano_fields.write({'state': 'anonymized'})

        # add a result message in the wizard:
        msgs = [
            "Anonymization successful.", "",
            "Donot forget to save the resulting file to a safe place because you will not be able to revert the anonymization without this file.",
            "",
            "This file is also stored in the %s directory. The absolute file path is: %s."
        ]
        msg = '\n'.join(msgs) % (dirpath, abs_filepath)

        with open(abs_filepath, 'rb') as fn:
            self.write({
                'msg': msg,
                'file_export': base64.encodestring(fn.read()),
            })

        # update the history record:
        history.write({
            'field_ids': [[6, 0, ano_fields.ids]],
            'msg': msg,
            'filepath': abs_filepath,
            'state': 'done',
        })

        return {
            'res_id':
            self.id,
            'view_id':
            self.env.ref(
                'anonymization.view_ir_model_fields_anonymize_wizard_form').
            ids,
            'view_type':
            'form',
            "view_mode":
            'form',
            'res_model':
            'ir.model.fields.anonymize.wizard',
            'type':
            'ir.actions.act_window',
            'context': {
                'step': 'just_anonymized'
            },
            'target':
            'new'
        }
Exemple #9
0
    def reverse_anonymize_database(self):
        """Set the 'clear' state to defined fields"""
        self.ensure_one()
        IrModelFieldsAnonymization = self.env['ir.model.fields.anonymization']

        # check that all the defined fields are in the 'anonymized' state
        state = IrModelFieldsAnonymization._get_global_state()
        if state == 'clear':
            raise UserError(
                _("The database is not currently anonymized, you cannot reverse the anonymization."
                  ))
        elif state == 'unstable':
            raise UserError(
                _("The database anonymization is currently in an unstable state. Some fields are anonymized,"
                  " while some fields are not anonymized. You should try to solve this problem before trying to do anything."
                  ))

        if not self.file_import:
            raise UserError('%s: %s' % (
                _('Error !'),
                _("It is not possible to reverse the anonymization process without supplying the anonymization export file."
                  )))

        # reverse the anonymization:
        # load the json/pickle file content into a data structure:
        content = base64.decodestring(self.file_import)
        try:
            data = json.loads(content.decode('utf8'))
        except Exception:
            # backward-compatible mode
            data = pickle.loads(content, encoding='utf8')

        fixes = self.env[
            'ir.model.fields.anonymization.migration.fix'].search_read([
                ('target_version', '=', '.'.join(
                    str(v) for v in version_info[:2]))
            ], ['model_name', 'field_name', 'query', 'query_type', 'sequence'])
        fixes = group(fixes, ('model_name', 'field_name'))

        for line in data:
            queries = []
            table_name = self.env[line['model_id']]._table if line[
                'model_id'] in self.env else None

            # check if custom sql exists:
            key = (line['model_id'], line['field_id'])
            custom_updates = fixes.get(key)
            if custom_updates:
                custom_updates.sort(key=itemgetter('sequence'))
                queries = [(record['query'], record['query_type'])
                           for record in custom_updates
                           if record['query_type']]
            elif table_name:
                queries = [(
                    'update "%(table)s" set "%(field)s" = %%(value)s where id = %%(id)s'
                    % {
                        'table': table_name,
                        'field': line['field_id'],
                    }, 'sql')]

            for query in queries:
                if query[1] == 'sql':
                    self.env.cr.execute(query[0], {
                        'value': line['value'],
                        'id': line['id']
                    })
                elif query[1] == 'python':
                    safe_eval(query[0] % line)
                else:
                    raise Exception(
                        "Unknown query type '%s'. Valid types are: sql, python."
                        % (query['query_type'], ))

        # update the anonymization fields:
        ano_fields = IrModelFieldsAnonymization.search([('state', '!=',
                                                         'not_existing')])
        ano_fields.write({'state': 'clear'})

        # add a result message in the wizard:
        self.msg = '\n'.join(["Successfully reversed the anonymization.", ""])

        # create a new history record:
        history = self.env['ir.model.fields.anonymization.history'].create({
            'date':
            fields.Datetime.now(),
            'field_ids': [[6, 0, ano_fields.ids]],
            'msg':
            self.msg,
            'filepath':
            False,
            'direction':
            'anonymized -> clear',
            'state':
            'done'
        })

        return {
            'res_id':
            self.id,
            'view_id':
            self.env.ref(
                'anonymization.view_ir_model_fields_anonymize_wizard_form').
            ids,
            'view_type':
            'form',
            "view_mode":
            'form',
            'res_model':
            'ir.model.fields.anonymize.wizard',
            'type':
            'ir.actions.act_window',
            'context': {
                'step': 'just_desanonymized'
            },
            'target':
            'new'
        }
Exemple #10
0
 def _check_subtask_project(self):
     for task in self:
         if task.parent_id.project_id and task.project_id != task.parent_id.project_id.subtask_project_id:
             raise UserError(_("You can't define a parent task if its project is not correctly configured. The sub-task's project of the parent task's project should be this task's project"))
Exemple #11
0
 def unlink(self):
     projects = self.env['project.project'].search([('analytic_account_id', 'in', self.ids)])
     has_tasks = self.env['project.task'].search_count([('project_id', 'in', projects.ids)])
     if has_tasks:
         raise UserError(_('Please remove existing tasks in the project linked to the accounts you want to delete.'))
     return super(AccountAnalyticAccount, self).unlink()
Exemple #12
0
 def _check_parent_id(self):
     for task in self:
         if not task._check_recursion():
             raise ValidationError(_('Error! You cannot create recursive hierarchy of task(s).'))
Exemple #13
0
 def _check_employee_related_user(self):
     for badge_user in self:
         if badge_user.employee_id not in badge_user.user_id.employee_ids:
             raise ValidationError(_('The selected employee does not correspond to the selected user.'))
Exemple #14
0
    def check_access_rule(self, operation):
        """ Access rules of mail.message:
            - read: if
                - author_id == pid, uid is the author OR
                - uid is in the recipients (partner_ids) OR
                - uid has been notified (needaction) OR
                - uid is member of a listern channel (channel_ids.partner_ids) OR
                - uid have read access to the related document if model, res_id
                - otherwise: raise
            - create: if
                - no model, no res_id (private message) OR
                - pid in message_follower_ids if model, res_id OR
                - uid can read the parent OR
                - uid have write or create access on the related document if model, res_id, OR
                - otherwise: raise
            - write: if
                - author_id == pid, uid is the author, OR
                - uid is in the recipients (partner_ids) OR
                - uid has write or create access on the related document if model, res_id
                - otherwise: raise
            - unlink: if
                - uid has write or create access on the related document if model, res_id
                - otherwise: raise

        Specific case: non employee users see only messages with subtype (aka do
        not see internal logs).
        """
        def _generate_model_record_ids(msg_val, msg_ids):
            """ :param model_record_ids: {'model': {'res_id': (msg_id, msg_id)}, ... }
                :param message_values: {'msg_id': {'model': .., 'res_id': .., 'author_id': ..}}
            """
            model_record_ids = {}
            for id in msg_ids:
                vals = msg_val.get(id, {})
                if vals.get('model') and vals.get('res_id'):
                    model_record_ids.setdefault(vals['model'], set()).add(vals['res_id'])
            return model_record_ids

        if self._uid == SUPERUSER_ID:
            return
        # Non employees see only messages with a subtype (aka, not internal logs)
        if not self.env['res.users'].has_group('base.group_user'):
            self._cr.execute('''SELECT DISTINCT message.id, message.subtype_id, subtype.internal
                                FROM "%s" AS message
                                LEFT JOIN "mail_message_subtype" as subtype
                                ON message.subtype_id = subtype.id
                                WHERE message.message_type = %%s AND (message.subtype_id IS NULL OR subtype.internal IS TRUE) AND message.id = ANY (%%s)''' % (self._table), ('comment', self.ids,))
            if self._cr.fetchall():
                raise AccessError(
                    _('The requested operation cannot be completed due to security restrictions. Please contact your system administrator.\n\n(Document type: %s, Operation: %s)') %
                    (self._description, operation))

        # Read mail_message.ids to have their values
        message_values = dict((res_id, {}) for res_id in self.ids)

        if operation in ['read', 'write']:
            self._cr.execute("""
                SELECT DISTINCT m.id, m.model, m.res_id, m.author_id, m.parent_id,
                                COALESCE(partner_rel.res_partner_id, needaction_rel.res_partner_id),
                                channel_partner.channel_id as channel_id
                FROM "%s" m
                LEFT JOIN "mail_message_res_partner_rel" partner_rel
                ON partner_rel.mail_message_id = m.id AND partner_rel.res_partner_id = %%(pid)s
                LEFT JOIN "mail_message_res_partner_needaction_rel" needaction_rel
                ON needaction_rel.mail_message_id = m.id AND needaction_rel.res_partner_id = %%(pid)s
                LEFT JOIN "mail_message_mail_channel_rel" channel_rel
                ON channel_rel.mail_message_id = m.id
                LEFT JOIN "mail_channel" channel
                ON channel.id = channel_rel.mail_channel_id
                LEFT JOIN "mail_channel_partner" channel_partner
                ON channel_partner.channel_id = channel.id AND channel_partner.partner_id = %%(pid)s
                WHERE m.id = ANY (%%(ids)s)""" % self._table, dict(pid=self.env.user.partner_id.id, ids=self.ids))
            for mid, rmod, rid, author_id, parent_id, partner_id, channel_id in self._cr.fetchall():
                message_values[mid] = {
                    'model': rmod,
                    'res_id': rid,
                    'author_id': author_id,
                    'parent_id': parent_id,
                    'notified': any((message_values[mid].get('notified'), partner_id, channel_id))
                }
        else:
            self._cr.execute("""SELECT DISTINCT id, model, res_id, author_id, parent_id FROM "%s" WHERE id = ANY (%%s)""" % self._table, (self.ids,))
            for mid, rmod, rid, author_id, parent_id in self._cr.fetchall():
                message_values[mid] = {'model': rmod, 'res_id': rid, 'author_id': author_id, 'parent_id': parent_id}

        # Author condition (READ, WRITE, CREATE (private))
        author_ids = []
        if operation == 'read' or operation == 'write':
            author_ids = [mid for mid, message in message_values.items()
                          if message.get('author_id') and message.get('author_id') == self.env.user.partner_id.id]
        elif operation == 'create':
            author_ids = [mid for mid, message in message_values.items()
                          if not message.get('model') and not message.get('res_id')]

        # Parent condition, for create (check for received notifications for the created message parent)
        notified_ids = []
        if operation == 'create':
            # TDE: probably clean me
            parent_ids = [message.get('parent_id') for message in message_values.values()
                          if message.get('parent_id')]
            self._cr.execute("""SELECT DISTINCT m.id, partner_rel.res_partner_id, channel_partner.partner_id FROM "%s" m
                LEFT JOIN "mail_message_res_partner_rel" partner_rel
                ON partner_rel.mail_message_id = m.id AND partner_rel.res_partner_id = (%%s)
                LEFT JOIN "mail_message_mail_channel_rel" channel_rel
                ON channel_rel.mail_message_id = m.id
                LEFT JOIN "mail_channel" channel
                ON channel.id = channel_rel.mail_channel_id
                LEFT JOIN "mail_channel_partner" channel_partner
                ON channel_partner.channel_id = channel.id AND channel_partner.partner_id = (%%s)
                WHERE m.id = ANY (%%s)""" % self._table, (self.env.user.partner_id.id, self.env.user.partner_id.id, parent_ids,))
            not_parent_ids = [mid[0] for mid in self._cr.fetchall() if any([mid[1], mid[2]])]
            notified_ids += [mid for mid, message in message_values.items()
                             if message.get('parent_id') in not_parent_ids]

        # Recipients condition, for read and write (partner_ids) and create (message_follower_ids)
        other_ids = set(self.ids).difference(set(author_ids), set(notified_ids))
        model_record_ids = _generate_model_record_ids(message_values, other_ids)
        if operation in ['read', 'write']:
            notified_ids = [mid for mid, message in message_values.items() if message.get('notified')]
        elif operation == 'create':
            for doc_model, doc_ids in model_record_ids.items():
                followers = self.env['mail.followers'].sudo().search([
                    ('res_model', '=', doc_model),
                    ('res_id', 'in', list(doc_ids)),
                    ('partner_id', '=', self.env.user.partner_id.id),
                    ])
                fol_mids = [follower.res_id for follower in followers]
                notified_ids += [mid for mid, message in message_values.items()
                                 if message.get('model') == doc_model and message.get('res_id') in fol_mids]

        # CRUD: Access rights related to the document
        other_ids = other_ids.difference(set(notified_ids))
        model_record_ids = _generate_model_record_ids(message_values, other_ids)
        document_related_ids = []
        for model, doc_ids in model_record_ids.items():
            DocumentModel = self.env[model]
            mids = DocumentModel.browse(doc_ids).exists()
            if hasattr(DocumentModel, 'check_mail_message_access'):
                DocumentModel.check_mail_message_access(mids.ids, operation)  # ?? mids ?
            else:
                self.env['mail.thread'].check_mail_message_access(mids.ids, operation, model_name=model)
            document_related_ids += [mid for mid, message in message_values.items()
                                     if message.get('model') == model and message.get('res_id') in mids.ids]

        # Calculate remaining ids: if not void, raise an error
        other_ids = other_ids.difference(set(document_related_ids))
        if not (other_ids and self.browse(other_ids).exists()):
            return
        raise AccessError(
            _('The requested operation cannot be completed due to security restrictions. Please contact your system administrator.\n\n(Document type: %s, Operation: %s)') %
            (self._description, operation))
Exemple #15
0
    def print_receipt_body(self, eprint, receipt):
        def check(string):
            return string != True and bool(string) and string.strip()

        def price(amount):
            return ("{0:." + str(receipt['precision']['price']) +
                    "f}").format(amount)

        def money(amount):
            return ("{0:." + str(receipt['precision']['money']) +
                    "f}").format(amount)

        def quantity(amount):
            if math.floor(amount) != amount:
                return ("{0:." + str(receipt['precision']['quantity']) +
                        "f}").format(amount)
            else:
                return str(amount)

        def printline(left, right='', width=40, ratio=0.5, indent=0):
            lwidth = int(width * ratio)
            rwidth = width - lwidth
            lwidth = lwidth - indent

            left = left[:lwidth]
            if len(left) != lwidth:
                left = left + ' ' * (lwidth - len(left))

            right = right[-rwidth:]
            if len(right) != rwidth:
                right = ' ' * (rwidth - len(right)) + right

            return ' ' * indent + left + right + '\n'

        def print_taxes():
            taxes = receipt['tax_details']
            for tax in taxes:
                eprint.text(
                    printline(tax['tax']['name'],
                              price(tax['amount']),
                              width=40,
                              ratio=0.6))

        # Receipt Header
        if receipt['company']['logo']:
            eprint.set(align='center')
            eprint.print_base64_image(receipt['company']['logo'])
            eprint.text('\n')
        else:
            eprint.set(align='center', type='b', height=2, width=2)
            eprint.text(receipt['company']['name'] + '\n')

        eprint.set(align='center', type='b')
        if check(receipt['company']['contact_address']):
            eprint.text(receipt['company']['contact_address'] + '\n')
        if check(receipt['company']['phone']):
            eprint.text('Tel:' + receipt['company']['phone'] + '\n')
        if check(receipt['company']['vat']):
            eprint.text('VAT:' + receipt['company']['vat'] + '\n')
        if check(receipt['company']['email']):
            eprint.text(receipt['company']['email'] + '\n')
        if check(receipt['company']['website']):
            eprint.text(receipt['company']['website'] + '\n')
        if check(receipt['header']):
            eprint.text(receipt['header'] + '\n')
        if check(receipt['cashier']):
            eprint.text('-' * 32 + '\n')
            eprint.text('Served by ' + receipt['cashier'] + '\n')

        # Orderlines
        eprint.text('\n\n')
        eprint.set(align='center')
        for line in receipt['orderlines']:
            pricestr = price(line['price_display'])
            if line['discount'] == 0 and line[
                    'unit_name'] == 'Unit(s)' and line['quantity'] == 1:
                eprint.text(
                    printline(line['product_name'], pricestr, ratio=0.6))
            else:
                eprint.text(printline(line['product_name'], ratio=0.6))
                if line['discount'] != 0:
                    eprint.text(
                        printline('Discount: ' + str(line['discount']) + '%',
                                  ratio=0.6,
                                  indent=2))
                if line['unit_name'] == 'Unit(s)':
                    eprint.text(
                        printline(quantity(line['quantity']) + ' x ' +
                                  price(line['price']),
                                  pricestr,
                                  ratio=0.6,
                                  indent=2))
                else:
                    eprint.text(
                        printline(quantity(line['quantity']) +
                                  line['unit_name'] + ' x ' +
                                  price(line['price']),
                                  pricestr,
                                  ratio=0.6,
                                  indent=2))

        # Subtotal if the taxes are not included
        taxincluded = True
        if money(receipt['subtotal']) != money(receipt['total_with_tax']):
            eprint.text(printline('', '-------'))
            eprint.text(
                printline(_('Subtotal'),
                          money(receipt['subtotal']),
                          width=40,
                          ratio=0.6))
            print_taxes()
            #eprint.text(printline(_('Taxes'),money(receipt['total_tax']),width=40, ratio=0.6))
            taxincluded = False

        # Total
        eprint.text(printline('', '-------'))
        eprint.set(align='center', height=2)
        eprint.text(
            printline(_('         TOTAL'),
                      money(receipt['total_with_tax']),
                      width=40,
                      ratio=0.6))
        eprint.text('\n\n')

        # Paymentlines
        eprint.set(align='center')
        for line in receipt['paymentlines']:
            eprint.text(
                printline(line['journal'], money(line['amount']), ratio=0.6))

        eprint.text('\n')
        eprint.set(align='center', height=2)
        eprint.text(
            printline(_('        CHANGE'),
                      money(receipt['change']),
                      width=40,
                      ratio=0.6))
        eprint.set(align='center')
        eprint.text('\n')

        # Extra Payment info
        if receipt['total_discount'] != 0:
            eprint.text(
                printline(_('Discounts'),
                          money(receipt['total_discount']),
                          width=40,
                          ratio=0.6))
        if taxincluded:
            print_taxes()
            #eprint.text(printline(_('Taxes'),money(receipt['total_tax']),width=40, ratio=0.6))

        # Footer
        if check(receipt['footer']):
            eprint.text('\n' + receipt['footer'] + '\n\n')
        eprint.text(receipt['name'] + '\n')
        eprint.text(
            str(receipt['date']['date']).zfill(2) + '/' +
            str(receipt['date']['month'] + 1).zfill(2) + '/' +
            str(receipt['date']['year']).zfill(4) + ' ' +
            str(receipt['date']['hour']).zfill(2) + ':' +
            str(receipt['date']['minute']).zfill(2))
Exemple #16
0
 def fields_view_get(self,
                     view_id=None,
                     view_type='form',
                     toolbar=False,
                     submenu=False):
     state = self.env['ir.model.fields.anonymization']._get_global_state()
     step = self.env.context.get('step', 'new_window')
     res = super(IrModelFieldsAnonymizeWizard,
                 self).fields_view_get(view_id=view_id,
                                       view_type=view_type,
                                       toolbar=toolbar,
                                       submenu=submenu)
     eview = etree.fromstring(res['arch'])
     placeholder = eview.xpath("group[@name='placeholder1']")
     if len(placeholder):
         placeholder = placeholder[0]
         if step == 'new_window' and state == 'clear':
             # clicked in the menu and the fields are not anonymized: warn the admin that backuping the db is very important
             placeholder.addnext(
                 etree.Element('field', {
                     'name': 'msg',
                     'colspan': '4',
                     'nolabel': '1'
                 }))
             placeholder.addnext(etree.Element('newline'))
             placeholder.addnext(
                 etree.Element('label', {'string': 'Warning'}))
             eview.remove(placeholder)
         elif step == 'new_window' and state == 'anonymized':
             # clicked in the menu and the fields are already anonymized
             placeholder.addnext(etree.Element('newline'))
             placeholder.addnext(
                 etree.Element('field', {
                     'name': 'file_import',
                     'required': "1"
                 }))
             placeholder.addnext(
                 etree.Element('label', {'string': 'Anonymization file'}))
             eview.remove(placeholder)
         elif step == 'just_anonymized':
             # we just ran the anonymization process, we need the file export field
             placeholder.addnext(etree.Element('newline'))
             placeholder.addnext(
                 etree.Element('field', {'name': 'file_export'}))
             # we need to remove the button:
             buttons = eview.xpath("button")
             for button in buttons:
                 eview.remove(button)
             # and add a message:
             placeholder.addnext(
                 etree.Element('field', {
                     'name': 'msg',
                     'colspan': '4',
                     'nolabel': '1'
                 }))
             placeholder.addnext(etree.Element('newline'))
             placeholder.addnext(
                 etree.Element('label', {'string': 'Result'}))
             # remove the placeholer:
             eview.remove(placeholder)
         elif step == 'just_desanonymized':
             # we just reversed the anonymization process, we don't need any field
             # we need to remove the button
             buttons = eview.xpath("button")
             for button in buttons:
                 eview.remove(button)
             # and add a message
             placeholder.addnext(
                 etree.Element('field', {
                     'name': 'msg',
                     'colspan': '4',
                     'nolabel': '1'
                 }))
             placeholder.addnext(etree.Element('newline'))
             placeholder.addnext(
                 etree.Element('label', {'string': 'Result'}))
             # remove the placeholer:
             eview.remove(placeholder)
         else:
             raise UserError(
                 _("The database anonymization is currently in an unstable state. Some fields are anonymized,"
                   " while some fields are not anonymized. You should try to solve this problem before trying to do anything else."
                   ))
         res['arch'] = etree.tostring(eview, encoding='unicode')
     return res
Exemple #17
0
 def _constrains_event(self):
     if any(ticket.event_type_id and ticket.event_id for ticket in self):
         raise UserError(
             _('Ticket should belong to either event category or event but not both'
               ))
Exemple #18
0
 def _compute_dashboard_button_name(self):
     super(CrmTeam, self)._compute_dashboard_button_name()
     self.filtered(lambda team: team.use_opportunities and team.team_type in
                   ('sales', 'website')).update(
                       {'dashboard_button_name': _("Pipeline")})
Exemple #19
0
 def _onchange_name(self):
     if self.name:
         self.event_ticket_ids.filtered(
             lambda ticket: ticket.name == _('Registration')).update(
                 {'name': _('Registration for %s') % self.name})
Exemple #20
0
 def copy(self, default=None):
     self.ensure_one()
     default = dict(default or {})
     if 'name' not in default:
         default['name'] = _("%s (copy)") % (self.name)
     return super(Job, self).copy(default=default)
Exemple #21
0
    def _action_done(self):
        """ This method is called during a move's `action_done`. It'll actually move a quant from
        the source location to the destination location, and unreserve if needed in the source
        location.

        This method is intended to be called on all the move lines of a move. This method is not
        intended to be called when editing a `done` move (that's what the override of `write` here
        is done.
        """

        # First, we loop over all the move lines to do a preliminary check: `qty_done` should not
        # be negative and, according to the presence of a picking type or a linked inventory
        # adjustment, enforce some rules on the `lot_id` field. If `qty_done` is null, we unlink
        # the line. It is mandatory in order to free the reservation and correctly apply
        # `action_done` on the next move lines.
        ml_to_delete = self.env['stock.move.line']
        for ml in self:
            # Check here if `ml.qty_done` respects the rounding of `ml.product_uom_id`.
            uom_qty = float_round(ml.qty_done, precision_rounding=ml.product_uom_id.rounding, rounding_method='HALF-UP')
            precision_digits = self.env['decimal.precision'].precision_get('Product Unit of Measure')
            qty_done = float_round(ml.qty_done, precision_digits=precision_digits, rounding_method='HALF-UP')
            if float_compare(uom_qty, qty_done, precision_digits=precision_digits) != 0:
                raise UserError(_('The quantity done for the product "%s" doesn\'t respect the rounding precision \
                                  defined on the unit of measure "%s". Please change the quantity done or the \
                                  rounding precision of your unit of measure.') % (ml.product_id.display_name, ml.product_uom_id.name))

            qty_done_float_compared = float_compare(ml.qty_done, 0, precision_rounding=ml.product_uom_id.rounding)
            if qty_done_float_compared > 0:
                if ml.product_id.tracking != 'none':
                    picking_type_id = ml.move_id.picking_type_id
                    if picking_type_id:
                        if picking_type_id.use_create_lots:
                            # If a picking type is linked, we may have to create a production lot on
                            # the fly before assigning it to the move line if the user checked both
                            # `use_create_lots` and `use_existing_lots`.
                            if ml.lot_name and not ml.lot_id:
                                lot = self.env['stock.production.lot'].create(
                                    {'name': ml.lot_name, 'product_id': ml.product_id.id}
                                )
                                ml.write({'lot_id': lot.id})
                        elif not picking_type_id.use_create_lots and not picking_type_id.use_existing_lots:
                            # If the user disabled both `use_create_lots` and `use_existing_lots`
                            # checkboxes on the picking type, he's allowed to enter tracked
                            # products without a `lot_id`.
                            continue
                    elif ml.move_id.inventory_id:
                        # If an inventory adjustment is linked, the user is allowed to enter
                        # tracked products without a `lot_id`.
                        continue

                    if not ml.lot_id:
                        raise UserError(_('You need to supply a lot/serial number for %s.') % ml.product_id.name)
            elif qty_done_float_compared < 0:
                raise UserError(_('No negative quantities allowed'))
            else:
                ml_to_delete |= ml
        ml_to_delete.unlink()

        # Now, we can actually move the quant.
        done_ml = self.env['stock.move.line']
        for ml in self - ml_to_delete:
            if ml.product_id.type == 'product':
                Quant = self.env['stock.quant']
                rounding = ml.product_uom_id.rounding

                # if this move line is force assigned, unreserve elsewhere if needed
                if not ml.location_id.should_bypass_reservation() and float_compare(ml.qty_done, ml.product_qty, precision_rounding=rounding) > 0:
                    extra_qty = ml.qty_done - ml.product_qty
                    ml._free_reservation(ml.product_id, ml.location_id, extra_qty, lot_id=ml.lot_id, package_id=ml.package_id, owner_id=ml.owner_id, ml_to_ignore=done_ml)
                # unreserve what's been reserved
                if not ml.location_id.should_bypass_reservation() and ml.product_id.type == 'product' and ml.product_qty:
                    try:
                        Quant._update_reserved_quantity(ml.product_id, ml.location_id, -ml.product_qty, lot_id=ml.lot_id, package_id=ml.package_id, owner_id=ml.owner_id, strict=True)
                    except UserError:
                        Quant._update_reserved_quantity(ml.product_id, ml.location_id, -ml.product_qty, lot_id=False, package_id=ml.package_id, owner_id=ml.owner_id, strict=True)

                # move what's been actually done
                quantity = ml.product_uom_id._compute_quantity(ml.qty_done, ml.move_id.product_id.uom_id, rounding_method='HALF-UP')
                available_qty, in_date = Quant._update_available_quantity(ml.product_id, ml.location_id, -quantity, lot_id=ml.lot_id, package_id=ml.package_id, owner_id=ml.owner_id)
                if available_qty < 0 and ml.lot_id:
                    # see if we can compensate the negative quants with some untracked quants
                    untracked_qty = Quant._get_available_quantity(ml.product_id, ml.location_id, lot_id=False, package_id=ml.package_id, owner_id=ml.owner_id, strict=True)
                    if untracked_qty:
                        taken_from_untracked_qty = min(untracked_qty, abs(quantity))
                        Quant._update_available_quantity(ml.product_id, ml.location_id, -taken_from_untracked_qty, lot_id=False, package_id=ml.package_id, owner_id=ml.owner_id)
                        Quant._update_available_quantity(ml.product_id, ml.location_id, taken_from_untracked_qty, lot_id=ml.lot_id, package_id=ml.package_id, owner_id=ml.owner_id)
                Quant._update_available_quantity(ml.product_id, ml.location_dest_id, quantity, lot_id=ml.lot_id, package_id=ml.result_package_id, owner_id=ml.owner_id, in_date=in_date)
            done_ml |= ml
        # Reset the reserved quantity as we just moved it to the destination location.
        (self - ml_to_delete).with_context(bypass_reservation_update=True).write({
            'product_uom_qty': 0.00,
            'date': fields.Datetime.now(),
        })
Exemple #22
0
 def _check_parent_id(self):
     for employee in self:
         if not employee._check_recursion():
             raise ValidationError(
                 _('Error! You cannot create recursive hierarchy of Employee(s).'
                   ))
Exemple #23
0
 def check_reserved_done_quantity(self):
     for move_line in self:
         if move_line.state == 'done' and not float_is_zero(move_line.product_uom_qty, precision_digits=self.env['decimal.precision'].precision_get('Product Unit of Measure')):
             raise ValidationError(_('A done move line should never have a reserved quantity.'))
Exemple #24
0
 def _check_parent_id(self):
     if not self._check_recursion():
         raise ValidationError(
             _('Error! You cannot create recursive departments.'))
Exemple #25
0
 def render_sale_button(self, order, return_url, submit_txt=None, render_values=None):
     values = {
         'return_url': return_url,
         'partner_id': order.partner_shipping_id.id or order.partner_invoice_id.id,
         'billing_partner_id': order.partner_invoice_id.id,
     }
     if render_values:
         values.update(render_values)
     return self.acquirer_id.with_context(submit_class='btn btn-primary', submit_txt=submit_txt or _('Pay Now')).sudo().render(
         self.reference,
         order.amount_total,
         order.pricelist_id.currency_id.id,
         values=values,
     )
Exemple #26
0
 def view_header_get(self, view_id, view_type):
     res = super(ProductProduct, self).view_header_get(view_id, view_type)
     if self._context.get('categ_id'):
         return _('Products: ') + self.env['product.category'].browse(
             self._context['categ_id']).name
     return res
Exemple #27
0
 def unlink(self):
     if 'done' in self.mapped('state'):
         raise UserError(_('You cannot delete a scrap which is done.'))
     return super(StockScrap, self).unlink()
Exemple #28
0
 def _check_category_recursion(self):
     if not self._check_recursion():
         raise ValidationError(
             _('Error ! You cannot create recursive categories.'))
     return True
Exemple #29
0
    def button_validate(self):
        self.ensure_one()
        if not self.move_lines and not self.move_line_ids:
            raise UserError(_('Please add some lines to move'))

        # If no lots when needed, raise error
        picking_type = self.picking_type_id
        precision_digits = self.env['decimal.precision'].precision_get('Product Unit of Measure')
        no_quantities_done = all(float_is_zero(move_line.qty_done, precision_digits=precision_digits) for move_line in self.move_line_ids)
        no_reserved_quantities = all(float_is_zero(move_line.product_qty, precision_rounding=move_line.product_uom_id.rounding) for move_line in self.move_line_ids)
        if no_reserved_quantities and no_quantities_done:
            raise UserError(_('You cannot validate a transfer if you have not processed any quantity. You should rather cancel the transfer.'))

        if picking_type.use_create_lots or picking_type.use_existing_lots:
            lines_to_check = self.move_line_ids
            if not no_quantities_done:
                lines_to_check = lines_to_check.filtered(
                    lambda line: float_compare(line.qty_done, 0,
                                               precision_rounding=line.product_uom_id.rounding)
                )

            for line in lines_to_check:
                product = line.product_id
                if product and product.tracking != 'none':
                    if not line.lot_name and not line.lot_id:
                        raise UserError(_('You need to supply a lot/serial number for %s.') % product.display_name)

        if no_quantities_done:
            view = self.env.ref('stock.view_immediate_transfer')
            wiz = self.env['stock.immediate.transfer'].create({'pick_ids': [(4, self.id)]})
            return {
                'name': _('Immediate Transfer?'),
                'type': 'ir.actions.act_window',
                'view_type': 'form',
                'view_mode': 'form',
                'res_model': 'stock.immediate.transfer',
                'views': [(view.id, 'form')],
                'view_id': view.id,
                'target': 'new',
                'res_id': wiz.id,
                'context': self.env.context,
            }

        if self._get_overprocessed_stock_moves() and not self._context.get('skip_overprocessed_check'):
            view = self.env.ref('stock.view_overprocessed_transfer')
            wiz = self.env['stock.overprocessed.transfer'].create({'picking_id': self.id})
            return {
                'type': 'ir.actions.act_window',
                'view_type': 'form',
                'view_mode': 'form',
                'res_model': 'stock.overprocessed.transfer',
                'views': [(view.id, 'form')],
                'view_id': view.id,
                'target': 'new',
                'res_id': wiz.id,
                'context': self.env.context,
            }

        # Check backorder should check for other barcodes
        if self._check_backorder():
            return self.action_generate_backorder_wizard()
        self.action_done()
        return
Exemple #30
0
    def db_id_for(self, model, field, subfield, value):
        """ Finds a database id for the reference ``value`` in the referencing
        subfield ``subfield`` of the provided field of the provided model.

        :param model: model to which the field belongs
        :param field: relational field for which references are provided
        :param subfield: a relational subfield allowing building of refs to
                         existing records: ``None`` for a name_get/name_search,
                         ``id`` for an external id and ``.id`` for a database
                         id
        :param value: value of the reference to match to an actual record
        :param context: OpenERP request context
        :return: a pair of the matched database identifier (if any), the
                 translated user-readable name for the field and the list of
                 warnings
        :rtype: (ID|None, unicode, list)
        """
        id = None
        warnings = []
        action = {'type': 'ir.actions.act_window', 'target': 'new',
                  'view_mode': 'tree,form', 'view_type': 'form',
                  'views': [(False, 'tree'), (False, 'form')],
                  'help': _(u"See all possible values")}
        if subfield is None:
            action['res_model'] = field.comodel_name
        elif subfield in ('id', '.id'):
            action['res_model'] = 'ir.model.data'
            action['domain'] = [('model', '=', field.comodel_name)]

        RelatedModel = self.env[field.comodel_name]
        if subfield == '.id':
            field_type = _(u"database id")
            try: tentative_id = int(value)
            except ValueError: tentative_id = value
            try:
                if RelatedModel.search([('id', '=', tentative_id)]):
                    id = tentative_id
            except psycopg2.DataError:
                # type error
                raise self._format_import_error(
                    ValueError,
                    _(u"Invalid database id '%s' for the field '%%(field)s'"),
                    value,
                    {'moreinfo': action})
        elif subfield == 'id':
            field_type = _(u"external id")
            if '.' in value:
                xmlid = value
            else:
                xmlid = "%s.%s" % (self._context.get('_import_current_module', ''), value)
            try:
                id = self.env.ref(xmlid).id
            except ValueError:
                pass # leave id is None
        elif subfield is None:
            field_type = _(u"name")
            ids = RelatedModel.name_search(name=value, operator='=')
            if ids:
                if len(ids) > 1:
                    warnings.append(ImportWarning(
                        _(u"Found multiple matches for field '%%(field)s' (%d matches)")
                        % (len(ids))))
                id, _name = ids[0]
        else:
            raise self._format_import_error(
                Exception,
                _(u"Unknown sub-field '%s'"),
                subfield
            )

        if id is None:
            raise self._format_import_error(
                ValueError,
                _(u"No matching record found for %(field_type)s '%(value)s' in field '%%(field)s'"),
                {'field_type': field_type, 'value': value},
                {'moreinfo': action})
        return id, field_type, warnings