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."))
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
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' }
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`.'))
def copy(self, default=None): self.ensure_one() default = dict(default or {}, name=_('%s (copy)') % self.name) return super(IrFilters, self).copy(default)
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'))
def _check_positive_qty_done(self): if any([ml.qty_done < 0 for ml in self]): raise ValidationError(_('You can not enter negative quantities!'))
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' }
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' }
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"))
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()
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).'))
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.'))
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))
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))
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
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' ))
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")})
def _onchange_name(self): if self.name: self.event_ticket_ids.filtered( lambda ticket: ticket.name == _('Registration')).update( {'name': _('Registration for %s') % self.name})
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)
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(), })
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).' ))
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.'))
def _check_parent_id(self): if not self._check_recursion(): raise ValidationError( _('Error! You cannot create recursive departments.'))
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, )
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
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 _check_category_recursion(self): if not self._check_recursion(): raise ValidationError( _('Error ! You cannot create recursive categories.')) return True
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
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