def create(self, values): if 'employee_id' not in values or 'contract_id' not in values: payslip = self.env['hr.payslip'].browse(values.get('slip_id')) values['employee_id'] = values.get( 'employee_id') or payslip.employee_id.id values['contract_id'] = values.get( 'contract_id' ) or payslip.contract_id and payslip.contract_id.id if not values['contract_id']: raise UserError( _('You must set a contract to create a payslip line.')) return super(HrPayslipLine, self).create(values)
def _compute_rule(self, localdict): """ :param localdict: dictionary containing the environement in which to compute the rule :return: returns a tuple build as the base/amount computed, the quantity and the rate :rtype: (float, float, float) """ self.ensure_one() if self.amount_select == 'fix': try: return self.amount_fix, float( safe_eval(self.quantity, localdict)), 100.0 except: raise UserError( _('Wrong quantity defined for salary rule %s (%s).') % (self.name, self.code)) elif self.amount_select == 'percentage': try: return (float(safe_eval(self.amount_percentage_base, localdict)), float(safe_eval(self.quantity, localdict)), self.amount_percentage) except: raise UserError( _('Wrong percentage base or quantity defined for salary rule %s (%s).' ) % (self.name, self.code)) else: try: safe_eval(self.amount_python_compute, localdict, mode='exec', nocopy=True) return float( localdict['result'] ), 'result_qty' in localdict and localdict[ 'result_qty'] or 1.0, 'result_rate' in localdict and localdict[ 'result_rate'] or 100.0 except: raise UserError( _('Wrong python code defined for salary rule %s (%s).') % (self.name, self.code))
def print_checks(self): """ Check that the recordset is valid, set the payments state to sent and call print_checks() """ # Since this method can be called via a client_action_multi, we need to make sure the received records are what we expect self = self.filtered(lambda r: r.payment_method_id.code == 'check_printing' and r.state != 'reconciled') if len(self) == 0: raise UserError( _("Payments to print as a checks must have 'Check' selected as payment method and " "not have already been reconciled")) if any(payment.journal_id != self[0].journal_id for payment in self): raise UserError( _("In order to print multiple checks at once, they must belong to the same bank journal." )) if not self[0].journal_id.check_manual_sequencing: # The wizard asks for the number printed on the first pre-printed check # so payments are attributed the number of the check the'll be printed on. last_printed_check = self.search( [('journal_id', '=', self[0].journal_id.id), ('check_number', '!=', 0)], order="check_number desc", limit=1) next_check_number = last_printed_check and last_printed_check.check_number + 1 or 1 return { 'name': _('Print Pre-numbered Checks'), 'type': 'ir.actions.act_window', 'res_model': 'print.prenumbered.checks', 'view_type': 'form', 'view_mode': 'form', 'target': 'new', 'context': { 'payment_ids': self.ids, 'default_next_check_number': next_check_number, } } else: self.filtered(lambda r: r.state == 'draft').post() return self.do_print_checks()
def _print_report(self, data): res = {} data = self.pre_print_report(data) data['form'].update(self.read(['period_length'])[0]) period_length = data['form']['period_length'] if period_length<=0: raise UserError(_('You must set a period length greater than 0.')) if not data['form']['date_from']: raise UserError(_('You must set a start date.')) start = datetime.strptime(data['form']['date_from'], "%Y-%m-%d") for i in range(5)[::-1]: stop = start - relativedelta(days=period_length - 1) res[str(i)] = { 'name': (i!=0 and (str((5-(i+1)) * period_length) + '-' + str((5-i) * period_length)) or ('+'+str(4 * period_length))), 'stop': start.strftime('%Y-%m-%d'), 'start': (i!=0 and stop.strftime('%Y-%m-%d') or False), } start = stop - relativedelta(days=1) data['form'].update(res) return self.env.ref('account.action_report_aged_partner_balance').with_context(landscape=True).report_action(self, data=data)
def print_report(self): self.ensure_one() [data] = self.read() if not data.get('depts'): raise UserError( _('You have to select at least one Department. And try again.') ) departments = self.env['hr.department'].browse(data['depts']) datas = {'ids': [], 'model': 'hr.department', 'form': data} return self.env.ref( 'hr_holidays.action_report_holidayssummary').with_context( from_transient_model=True).report_action(departments, data=datas)
def _check_location_constraint(self): '''checks that all quants in a package are stored in the same location. This function cannot be used as a constraint because it needs to be checked on pack operations (they may not call write on the package) ''' for pack in self: locations = pack.get_content().filtered( lambda quant: quant.qty > 0.0).mapped('location_id') if len(locations) != 1: raise UserError( _('Everything inside a package should be in the same location' )) return True
def _get_move_vals(self, journal=None): """ Return dict to create the payment move """ journal = journal or self.journal_id if not journal.sequence_id: raise UserError( _('Configuration Error !'), _('The journal %s does not have a sequence, please specify one.' ) % journal.name) if not journal.sequence_id.active: raise UserError( _('Configuration Error !'), _('The sequence of journal %s is deactivated.') % journal.name) name = self.move_name or journal.with_context( ir_sequence_date=self.payment_date).sequence_id.next_by_id() return { 'name': name, 'date': self.payment_date, 'ref': self.communication or '', 'company_id': self.company_id.id, 'journal_id': journal.id, }
def create_membership_invoice(self, product_id=None, datas=None): """ Create Customer Invoice of Membership for partners. @param datas: datas has dictionary value which consist Id of Membership product and Cost Amount of Membership. datas = {'membership_product_id': None, 'amount': None} """ product_id = product_id or datas.get('membership_product_id') amount = datas.get('amount', 0.0) invoice_list = [] for partner in self: addr = partner.address_get(['invoice']) if partner.free_member: raise UserError(_("Partner is a free Member.")) if not addr.get('invoice', False): raise UserError( _("Partner doesn't have an address to make the invoice.")) invoice = self.env['account.invoice'].create({ 'partner_id': partner.id, 'account_id': partner.property_account_receivable_id.id, 'fiscal_position_id': partner.property_account_position_id.id }) line_values = { 'product_id': product_id, 'price_unit': amount, 'invoice_id': invoice.id, } # create a record in cache, apply onchange then revert back to a dictionnary invoice_line = self.env['account.invoice.line'].new(line_values) invoice_line._onchange_product_id() line_values = invoice_line._convert_to_write( {name: invoice_line[name] for name in invoice_line._cache}) line_values['price_unit'] = amount invoice.write({'invoice_line_ids': [(0, 0, line_values)]}) invoice_list.append(invoice.id) invoice.compute_taxes() return invoice_list
def update_notification(self, cron_mode=True): """ Send a message to izi's publisher warranty server to check the validity of the contracts, get notifications, etc... @param cron_mode: If true, catch all exceptions (appropriate for usage in a cron). @type cron_mode: boolean """ try: try: result = self._get_sys_logs() except Exception: if cron_mode: # we don't want to see any stack trace in cron return False _logger.debug("Exception while sending a get logs messages", exc_info=1) raise UserError( _("Error during communication with the publisher warranty server." )) # old behavior based on res.log; now on mail.message, that is not necessarily installed user = self.env['res.users'].sudo().browse(SUPERUSER_ID) poster = self.sudo().env.ref('mail.channel_all_employees') if not (poster and poster.exists()): if not user.exists(): return True poster = user for message in result["messages"]: try: poster.message_post(body=message, subtype='mt_comment', partner_ids=[user.partner_id.id]) except Exception: pass if result.get('enterprise_info'): # Update expiration date set_param = self.env['ir.config_parameter'].sudo().set_param set_param('database.expiration_date', result['enterprise_info'].get('expiration_date')) set_param( 'database.expiration_reason', result['enterprise_info'].get('expiration_reason', 'trial')) set_param('database.enterprise_code', result['enterprise_info'].get('enterprise_code')) except Exception: if cron_mode: return False # we don't want to see any stack trace in cron else: raise return True
def get_config_warning(self, msg): """ Helper: return a Warning exception with the given message where the %(field:xxx)s and/or %(menu:yyy)s are replaced by the human readable field's name and/or menuitem's full path. Usage: ------ Just include in your error message %(field:model_name.field_name)s to obtain the human readable field's name, and/or %(menu:module_name.menuitem_xml_id)s to obtain the menuitem's full path. Example of use: --------------- from izi.addons.base.res.res_config import get_warning_config raise get_warning_config(cr, _("Error: this action is prohibited. You should check the field %(field:sale.config.settings.fetchmail_lead)s in %(menu:sales_team.menu_sale_config)s."), context=context) This will return an exception containing the following message: Error: this action is prohibited. You should check the field Create leads from incoming mails in Settings/Configuration/Sales. What if there is another substitution in the message already? ------------------------------------------------------------- You could have a situation where the error message you want to upgrade already contains a substitution. Example: Cannot find any account journal of %s type for this company.\n\nYou can create one in the menu: \nConfiguration\Journals\Journals. What you want to do here is simply to replace the path by %menu:account.menu_account_config)s, and leave the rest alone. In order to do that, you can use the double percent (%%) to escape your new substitution, like so: Cannot find any account journal of %s type for this company.\n\nYou can create one in the %%(menu:account.menu_account_config)s. """ self = self.sudo() # Process the message # 1/ find the menu and/or field references, put them in a list regex_path = r'%\(((?:menu|field):[a-z_\.]*)\)s' references = re.findall(regex_path, msg, flags=re.I) # 2/ fetch the menu and/or field replacement values (full path and # human readable field's name) and the action_id if any values = {} action_id = None for item in references: ref_type, ref = item.split(':') if ref_type == 'menu': values[item], action_id = self.get_option_path(ref) elif ref_type == 'field': values[item] = self.get_option_name(ref) # 3/ substitute and return the result if (action_id): return RedirectWarning(msg % values, action_id, _('Go to the configuration panel')) return UserError(msg % values)
def default_get(self, fields): if len(self.env.context.get('active_ids', list())) > 1: raise UserError("You may only return one picking at a time!") res = super(ReturnPicking, self).default_get(fields) move_dest_exists = False product_return_moves = [] picking = self.env['stock.picking'].browse(self.env.context.get('active_id')) if picking: res.update({'picking_id': picking.id}) if picking.state != 'done': raise UserError(_("You may only return Done pickings")) for move in picking.move_lines: if move.scrapped: continue if move.move_dest_ids: move_dest_exists = True quantity = move.product_qty - sum(move.move_dest_ids.filtered(lambda m: m.state in ['partially_available', 'assigned', 'done']).\ mapped('move_line_ids').mapped('product_qty')) quantity = float_round(quantity, precision_rounding=move.product_uom.rounding) product_return_moves.append((0, 0, {'product_id': move.product_id.id, 'quantity': quantity, 'move_id': move.id, 'uom_id': move.product_id.uom_id.id})) if not product_return_moves: raise UserError(_("No products to return (only lines in Done state and not fully returned yet can be returned)!")) if 'product_return_moves' in fields: res.update({'product_return_moves': product_return_moves}) if 'move_dest_exists' in fields: res.update({'move_dest_exists': move_dest_exists}) if 'parent_location_id' in fields and picking.location_id.usage == 'internal': res.update({'parent_location_id': picking.picking_type_id.warehouse_id and picking.picking_type_id.warehouse_id.view_location_id.id or picking.location_id.location_id.id}) if 'original_location_id' in fields: res.update({'original_location_id': picking.location_id.id}) if 'location_id' in fields: location_id = picking.location_id.id if picking.picking_type_id.return_picking_type_id.default_location_dest_id.return_location: location_id = picking.picking_type_id.return_picking_type_id.default_location_dest_id.id res['location_id'] = location_id return res
def action_cancel(self): """ Cancels production order, unfinished stock moves and set procurement orders in exception """ if any(workorder.state == 'progress' for workorder in self.mapped('workorder_ids')): raise UserError(_('You can not cancel production order, a work order is still in progress.')) for production in self: production.workorder_ids.filtered(lambda x: x.state != 'cancel').action_cancel() finish_moves = production.move_finished_ids.filtered(lambda x: x.state not in ('done', 'cancel')) raw_moves = production.move_raw_ids.filtered(lambda x: x.state not in ('done', 'cancel')) (finish_moves | raw_moves)._action_cancel() self.write({'state': 'cancel', 'is_locked': True}) return True
def action_sheet_move_create(self): if any(sheet.state != 'approve' for sheet in self): raise UserError( _("You can only generate accounting entry for approved expense(s)." )) if any(not sheet.journal_id for sheet in self): raise UserError( _("Expenses must have an expense journal specified to generate accounting entries." )) expense_line_ids = self.mapped('expense_line_ids')\ .filtered(lambda r: not float_is_zero(r.total_amount, precision_rounding=(r.currency_id or self.env.user.company_id.currency_id).rounding)) res = expense_line_ids.action_move_create() if not self.accounting_date: self.accounting_date = self.account_move_id.date if self.payment_mode == 'own_account' and expense_line_ids: self.write({'state': 'post'}) else: self.write({'state': 'done'}) return res
def _calculate_values_for_statement_line(self, record): if not record.journal_id.company_id.transfer_account_id: raise UserError( _("You should have defined an 'Internal Transfer Account' in your cash register's journal!" )) amount = self.amount or 0.0 return { 'date': record.date, 'statement_id': record.id, 'journal_id': record.journal_id.id, 'amount': -amount if amount > 0.0 else amount, 'account_id': record.journal_id.company_id.transfer_account_id.id, 'name': self.name, }
def unlink(self): aliases = self.mapped('alias_id') # Delete mail.channel try: all_emp_group = self.env.ref('mail.channel_all_employees') except ValueError: all_emp_group = None if all_emp_group and all_emp_group in self: raise UserError(_('You cannot delete those groups, as the Whole Company group is required by other modules.')) res = super(Channel, self).unlink() # Cascade-delete mail aliases as well, as they should not exist without the mail.channel. aliases.sudo().unlink() return res
def _action_done(self): self.product_price_update_before_done() res = super(StockMove, self)._action_done() for move in res: # Apply restrictions on the stock move to be able to make # consistent accounting entries. if move._is_in() and move._is_out(): raise UserError(_("The move lines are not in a consistent state: some are entering and other are leaving the company. ")) company_src = move.mapped('move_line_ids.location_id.company_id') company_dst = move.mapped('move_line_ids.location_dest_id.company_id') try: if company_src: company_src.ensure_one() if company_dst: company_dst.ensure_one() except ValueError: raise UserError(_("The move lines are not in a consistent states: they do not share the same origin or destination company.")) if company_src and company_dst and company_src.id != company_dst.id: raise UserError(_("The move lines are not in a consistent states: they are doing an intercompany in a single step while they should go through the intercompany transit location.")) move._run_valuation() for move in res.filtered(lambda m: m.product_id.valuation == 'real_time' and (m._is_in() or m._is_out() or m._is_dropshipped())): move._account_entry_move() return res
def write(self, values): res = super(Product, self).write(values) if 'active' in values and not values['active']: products = self.mapped('orderpoint_ids').filtered( lambda r: r.active).mapped('product_id') if products: msg = _( 'You still have some active reordering rules on this product. Please archive or delete them first.' ) msg += '\n\n' for product in products: msg += '- %s \n' % product.display_name raise UserError(msg) return res
def open_website_url(self): self.ensure_one() if not self.carrier_tracking_url: raise UserError( _("Your delivery method has no redirect on courier provider's website to track this order." )) client_action = { 'type': 'ir.actions.act_url', 'name': "Shipment Tracking Page", 'target': 'new', 'url': self.carrier_tracking_url, } return client_action
def _state_update(self, newstate, states_to_update, level=100): if level < 1: raise UserError(_('Recursion error in modules dependencies !')) # whether some modules are installed with demo data demo = False for module in self: # determine dependency modules to update/others update_mods, ready_mods = self.browse(), self.browse() for dep in module.dependencies_id: if dep.state == 'unknown': raise UserError( _("You try to install module '%s' that depends on module '%s'.\nBut the latter module is not available in your system." ) % ( module.name, dep.name, )) if dep.depend_id.state == newstate: ready_mods += dep.depend_id else: update_mods += dep.depend_id # update dependency modules that require it, and determine demo for module update_demo = update_mods._state_update(newstate, states_to_update, level=level - 1) module_demo = module.demo or update_demo or any( mod.demo for mod in ready_mods) demo = demo or module_demo # check dependencies and update module itself self.check_external_dependencies(module.name, newstate) if module.state in states_to_update: module.write({'state': newstate, 'demo': module_demo}) return demo
def _update_raw_move(self, bom_line, line_data): quantity = line_data['qty'] self.ensure_one() move = self.move_raw_ids.filtered(lambda x: x.bom_line_id.id == bom_line.id and x.state not in ('done', 'cancel')) if move: if quantity > 0: move[0].write({'product_uom_qty': quantity}) elif quantity < 0: # Do not remove 0 lines if move[0].quantity_done > 0: raise UserError(_('Lines need to be deleted, but can not as you still have some quantities to consume in them. ')) move[0]._action_cancel() move[0].unlink() return move else: self._generate_raw_move(bom_line, line_data)
def _alter_sequence(cr, seq_name, number_increment=None, number_next=None): """ Alter a PostreSQL sequence. """ if number_increment == 0: raise UserError(_("Step must not be zero.")) cr.execute("SELECT relname FROM pg_class WHERE relkind=%s AND relname=%s", ('S', seq_name)) if not cr.fetchone(): # sequence is not created yet, we're inside create() so ignore it, will be set later return statement = "ALTER SEQUENCE %s" % (seq_name, ) if number_increment is not None: statement += " INCREMENT BY %d" % (number_increment, ) if number_next is not None: statement += " RESTART WITH %d" % (number_next, ) cr.execute(statement)
def button_upgrade(self): Dependency = self.env['ir.module.module.dependency'] self.update_list() todo = list(self) i = 0 while i < len(todo): module = todo[i] i += 1 if module.state not in ('installed', 'to upgrade'): raise UserError( _("Can not upgrade module '%s'. It is not installed.") % (module.name, )) self.check_external_dependencies(module.name, 'to upgrade') for dep in Dependency.search([('name', '=', module.name)]): if dep.module_id.state == 'installed' and dep.module_id not in todo: todo.append(dep.module_id) self.browse(module.id for module in todo).write({'state': 'to upgrade'}) to_install = [] for module in todo: for dep in module.dependencies_id: if dep.state == 'unknown': raise UserError( _('You try to upgrade the module %s that depends on the module: %s.\nBut this module is not available in your system.' ) % ( module.name, dep.name, )) if dep.state == 'uninstalled': to_install += self.search([('name', '=', dep.name)]).ids self.browse(to_install).button_install() return dict(ACTION_DICT, name=_('Apply Schedule Upgrade'))
def _confirm_orders(self): for session in self: company_id = session.config_id.journal_id.company_id.id orders = session.order_ids.filtered(lambda order: order.state == 'paid') journal_id = self.env['ir.config_parameter'].sudo().get_param( 'pos.closing.journal_id_%s' % company_id, default=session.config_id.journal_id.id) if not journal_id: raise UserError(_("You have to set a Sale Journal for the POS:%s") % (session.config_id.name,)) move = self.env['pos.order'].with_context(force_company=company_id)._create_account_move(session.start_at, session.name, int(journal_id), company_id) orders.with_context(force_company=company_id)._create_account_move_line(session, move) for order in session.order_ids.filtered(lambda o: o.state not in ['done', 'invoiced']): if order.state not in ('paid'): raise UserError( _("You cannot confirm all orders of this session, because they have not the 'paid' status.\n" "{reference} is in state {state}, total amount: {total}, paid: {paid}").format( reference=order.pos_reference or order.name, state=order.state, total=order.amount_total, paid=order.amount_paid, )) order.action_pos_order_done() orders = session.order_ids.filtered(lambda order: order.state in ['invoiced', 'done']) orders.sudo()._reconcile_payments()
def change_password(self, old_passwd, new_passwd): """Change current user password. Old password must be provided explicitly to prevent hijacking an existing user session, or for cases where the cleartext password is not used to authenticate requests. :return: True :raise: izi.exceptions.AccessDenied when old password is wrong :raise: izi.exceptions.UserError when new password is not set or empty """ self.check(self._cr.dbname, self._uid, old_passwd) if new_passwd: # use self.env.user here, because it has uid=SUPERUSER_ID return self.env.user.write({'password': new_passwd}) raise UserError( _("Setting empty passwords is not allowed for security reasons!"))
def _inverse_password(self): for user in self: if not user.new_password: # Do not update the password if no value is provided, ignore silently. # For example web client submits False values for all empty fields. continue if user == self.env.user: # To change their own password, users must use the client-specific change password wizard, # so that the new password is immediately used for further RPC requests, otherwise the user # will face unexpected 'Access Denied' exceptions. raise UserError( _('Please use the change password wizard (in User Preferences or User menu) to change your own password.' )) else: user.password = user.new_password
def _check_twitter_authorization(self): try: self.website_id.fetch_favorite_tweets() except requests.HTTPError as e: _logger.info("%s - %s" % (e.response.status_code, e.response.reason), exc_info=True) raise UserError( "%s - %s" % (e.response.status_code, e.response.reason) + ':' + self._get_twitter_exception_message(e.response.status_code)) except IOError: _logger.info(_('We failed to reach a twitter server.'), exc_info=True) raise UserError( _('Internet connection refused') + ' ' + _('We failed to reach a twitter server.')) except Exception: _logger.info( _('Please double-check your Twitter API Key and Secret!'), exc_info=True) raise UserError( _('Twitter authorization error!') + ' ' + _('Please double-check your Twitter API Key and Secret!'))
def _update_line_quantity(self, values): if self.mapped('qty_delivered') and values['product_uom_qty'] < max( self.mapped('qty_delivered')): raise UserError( 'You cannot decrease the ordered quantity below the delivered quantity.\n' 'Create a return first.') for line in self: pickings = line.order_id.picking_ids.filtered( lambda p: p.state not in ('done', 'cancel')) for picking in pickings: picking.message_post( "The quantity of %s has been updated from %d to %d in %s" % (line.product_id.display_name, line.product_uom_qty, values['product_uom_qty'], line.order_id.name)) super(SaleOrderLine, self)._update_line_quantity(values)
def get_report_values(self, docids, data=None): PosOrder = self.env['pos.order'] ids_to_print = [] invoiced_posorders_ids = [] selected_orders = PosOrder.browse(docids) for order in selected_orders.filtered(lambda o: o.invoice_id): ids_to_print.append(order.invoice_id.id) invoiced_posorders_ids.append(order.id) not_invoiced_orders_ids = list(set(docids) - set(invoiced_posorders_ids)) if not_invoiced_orders_ids: not_invoiced_posorders = PosOrder.browse(not_invoiced_orders_ids) not_invoiced_orders_names = [a.name for a in not_invoiced_posorders] raise UserError(_('No link to an invoice for %s.') % ', '.join(not_invoiced_orders_names)) return {'docs': self.env['account.invoice'].sudo().browse(ids_to_print)}
def _set_odometer(self): for record in self: if not record.odometer: raise UserError( _('Emptying the odometer value of a vehicle is not allowed.' )) odometer = self.env['fleet.vehicle.odometer'].create({ 'value': record.odometer, 'date': record.date or fields.Date.context_today(record), 'vehicle_id': record.vehicle_id.id }) self.odometer_id = odometer
def action_repair_end(self): """ Writes repair order state to 'To be invoiced' if invoice method is After repair else state is set to 'Ready'. @return: True """ if self.filtered(lambda repair: repair.state != 'under_repair'): raise UserError( _("Repair must be under repair in order to end reparation.")) for repair in self: repair.write({'repaired': True}) vals = {'state': 'done'} vals['move_id'] = repair.action_repair_done().get(repair.id) if not repair.invoiced and repair.invoice_method == 'after_repair': vals['state'] = '2binvoiced' repair.write(vals) return True