def create_membership_invoice(self, product, amount): """ Create Customer Invoice of Membership for partners. """ invoice_vals_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_vals_list.append({ 'type': 'out_invoice', 'partner_id': partner.id, 'invoice_line_ids': [(0, None, { 'product_id': product.id, 'quantity': 1, 'price_unit': amount, 'tax_ids': [(6, 0, product.taxes_id.ids)] })] }) return self.env['account.move'].create(invoice_vals_list)
def attachment_remove(self, attachment_id, access_token=None): """Remove the given `attachment_id`, only if it is in a "pending" state. The user must have access right on the attachment or provide a valid `access_token`. """ try: attachment_sudo = self._document_check_access( 'ir.attachment', int(attachment_id), access_token=access_token) except (AccessError, MissingError) as e: raise UserError( _("The attachment does not exist or you do not have the rights to access it." )) if attachment_sudo.res_model != 'mail.compose.message' or attachment_sudo.res_id != 0: raise UserError( _("The attachment %s cannot be removed because it is not in a pending state." ) % attachment_sudo.name) if attachment_sudo.env['mail.message'].search([('attachment_ids', 'in', attachment_sudo.ids)]): raise UserError( _("The attachment %s cannot be removed because it is linked to a message." ) % attachment_sudo.name) return attachment_sudo.unlink()
def _show_report(self, model, report_type, report_ref, download=False): if report_type not in ('html', 'pdf', 'text'): raise UserError(_("Invalid report type: %s") % report_type) report_sudo = request.env.ref(report_ref).sudo() if not isinstance(report_sudo, type(request.env['ir.actions.report'])): raise UserError( _("%s is not the reference of a report") % report_ref) method_name = 'render_qweb_%s' % (report_type) report = getattr(report_sudo, method_name)([model.id], data={ 'report_type': report_type })[0] reporthttpheaders = [ ('Content-Type', 'application/pdf' if report_type == 'pdf' else 'text/html'), ('Content-Length', len(report)), ] if report_type == 'pdf' and download: filename = "%s.pdf" % (re.sub('\W+', '-', model._get_report_base_filename())) reporthttpheaders.append( ('Content-Disposition', content_disposition(filename))) return request.make_response(report, headers=reporthttpheaders)
def action_repair_cancel(self): if self.filtered(lambda repair: repair.state == 'done'): raise UserError(_("Cannot cancel completed repairs.")) if any(repair.invoiced for repair in self): raise UserError(_('The repair order is already invoiced.')) self.mapped('operations').write({'state': 'cancel'}) return self.write({'state': 'cancel'})
def act_update(self): ''' Function called by the wizard. ''' flag, gengo = self.gengo_authentication() if not flag: raise UserError(gengo) for wizard in self: supported_langs = self.env[ 'ir.translation']._get_all_supported_languages() language = self.env[ 'ir.translation']._get_gengo_corresponding_language( wizard.lang_id.code) if language not in supported_langs: raise UserError( _('This language is not supported by the Gengo translation services.' )) ctx = self.env.context.copy() ctx['gengo_language'] = wizard.lang_id.id if wizard.sync_limit > 200 or wizard.sync_limit < 1: raise UserError( _('The number of terms to sync should be between 1 to 200 to work with Gengo translation services.' )) if wizard.sync_type in ['send', 'both']: self.with_context(ctx)._sync_request(wizard.sync_limit) if wizard.sync_type in ['receive', 'both']: self.with_context(ctx)._sync_response(wizard.sync_limit) return {'type': 'ir.actions.act_window_close'}
def action_in_progress(self): self.ensure_one() if not all(obj.line_ids for obj in self): raise UserError( _("You cannot confirm agreement '%s' because there is no product line." ) % self.name) if self.type_id.quantity_copy == 'none' and self.vendor_id: for requisition_line in self.line_ids: if requisition_line.price_unit <= 0.0: raise UserError( _('You cannot confirm the blanket order without price.' )) if requisition_line.product_qty <= 0.0: raise UserError( _('You cannot confirm the blanket order without quantity.' )) requisition_line.create_supplier_info() self.write({'state': 'ongoing'}) else: self.write({'state': 'in_progress'}) # Set the sequence number regarding the requisition type if self.name == 'New': if self.is_quantity_copy != 'none': self.name = self.env['ir.sequence'].next_by_code( 'purchase.requisition.purchase.tender') else: self.name = self.env['ir.sequence'].next_by_code( 'purchase.requisition.blanket.order')
def action_create_sale_order(self): # if project linked to SO line or at least on tasks with SO line, then we consider project as billable. if self.project_id.sale_line_id: raise UserError(_("The project is already linked to a sales order item.")) if self.billable_type == 'employee_rate': # at least one line if not self.line_ids: raise UserError(_("At least one line should be filled.")) # all employee having timesheet should be in the wizard map timesheet_employees = self.env['account.analytic.line'].search([('task_id', 'in', self.project_id.tasks.ids)]).mapped('employee_id') map_employees = self.line_ids.mapped('employee_id') missing_meployees = timesheet_employees - map_employees if missing_meployees: raise UserError(_('The Sales Order cannot be created because you did not enter some employees that entered timesheets on this project. Please list all the relevant employees before creating the Sales Order.\nMissing employee(s): %s') % (', '.join(missing_meployees.mapped('name')))) # check here if timesheet already linked to SO line timesheet_with_so_line = self.env['account.analytic.line'].search_count([('task_id', 'in', self.project_id.tasks.ids), ('so_line', '!=', False)]) if timesheet_with_so_line: raise UserError(_('The sales order cannot be created because some timesheets of this project are already linked to another sales order.')) # create SO according to the chosen billable type sale_order = self._create_sale_order() view_form_id = self.env.ref('sale.view_order_form').id action = self.env.ref('sale.action_orders').read()[0] action.update({ 'views': [(view_form_id, 'form')], 'view_mode': 'form', 'name': sale_order.name, 'res_id': sale_order.id, }) return action
def _update_move_lines(self): """ update a move line to save the workorder line data""" self.ensure_one() if self.lot_id: move_lines = self.move_id.move_line_ids.filtered( lambda ml: ml.lot_id == self.lot_id and not ml.lot_produced_ids ) else: move_lines = self.move_id.move_line_ids.filtered( lambda ml: not ml.lot_id and not ml.lot_produced_ids) # Sanity check: if the product is a serial number and `lot` is already present in the other # consumed move lines, raise. if self.product_id.tracking != 'none' and not self.lot_id: raise UserError( _('Please enter a lot or serial number for %s !' % self.product_id.display_name)) if self.lot_id and self.product_id.tracking == 'serial' and self.lot_id in self.move_id.move_line_ids.filtered( lambda ml: ml.qty_done).mapped('lot_id'): raise UserError( _('You cannot consume the same serial number twice. Please correct the serial numbers encoded.' )) # Update reservation and quantity done for ml in move_lines: rounding = ml.product_uom_id.rounding if float_compare(self.qty_done, 0, precision_rounding=rounding) <= 0: break quantity_to_process = min(self.qty_done, ml.product_uom_qty - ml.qty_done) self.qty_done -= quantity_to_process new_quantity_done = (ml.qty_done + quantity_to_process) # if we produce less than the reserved quantity to produce the finished products # in different lots, # we create different component_move_lines to record which one was used # on which lot of finished product if float_compare(new_quantity_done, ml.product_uom_qty, precision_rounding=rounding) >= 0: ml.write({ 'qty_done': new_quantity_done, 'lot_produced_ids': self._get_produced_lots(), }) else: new_qty_reserved = ml.product_uom_qty - new_quantity_done default = { 'product_uom_qty': new_quantity_done, 'qty_done': new_quantity_done, 'lot_produced_ids': self._get_produced_lots(), } ml.copy(default=default) ml.with_context(bypass_reservation_update=True).write({ 'product_uom_qty': new_qty_reserved, 'qty_done': 0 })
def write(self, vals): if self and ('is_mail_thread' in vals or 'is_mail_activity' in vals or 'is_mail_blacklist' in vals): if not all(rec.state == 'manual' for rec in self): raise UserError(_('Only custom models can be modified.')) if 'is_mail_thread' in vals and not all( rec.is_mail_thread <= vals['is_mail_thread'] for rec in self): raise UserError( _('Field "Mail Thread" cannot be changed to "False".')) if 'is_mail_activity' in vals and not all( rec.is_mail_activity <= vals['is_mail_activity'] for rec in self): raise UserError( _('Field "Mail Activity" cannot be changed to "False".')) if 'is_mail_blacklist' in vals and not all( rec.is_mail_blacklist <= vals['is_mail_blacklist'] for rec in self): raise UserError( _('Field "Mail Blacklist" cannot be changed to "False".')) res = super(IrModel, self).write(vals) self.flush() # setup models; this reloads custom models in registry self.pool.setup_models(self._cr) # update database schema of models models = self.pool.descendants(self.mapped('model'), '_inherits') self.pool.init_models( self._cr, models, dict(self._context, update_custom_fields=True)) else: res = super(IrModel, self).write(vals) return res
def open_statement(self): self.ensure_one() BankStatement = self.env['account.bank.statement'] journals = self.env['account.journal'].search([('journal_user', '=', True)]) if not journals: raise UserError( _('You have to define which payment method must be available in the point of sale by reusing existing bank and cash through "Accounting / Configuration / Journals / Journals". Select a journal and check the field "PoS Payment Method" from the "Point of Sale" tab. You can also create new payment methods directly from menu "PoS Backend / Configuration / Payment Methods".' )) for journal in journals: if journal.sequence_id: number = journal.sequence_id.next_by_id() else: raise UserError(_("No sequence defined on the journal")) BankStatement += BankStatement.create({ 'journal_id': journal.id, 'user_id': self.env.uid, 'name': number }) tree_id = self.env.ref('account.view_bank_statement_tree').id form_id = self.env.ref('account.view_bank_statement_form').id search_id = self.env.ref('account.view_bank_statement_search').id return { 'type': 'ir.actions.act_window', 'name': _('List of Cash Registers'), 'view_mode': 'tree,form', 'res_model': 'account.bank.statement', 'domain': str([('id', 'in', BankStatement.ids)]), 'views': [(tree_id, 'tree'), (form_id, 'form')], 'search_view_id': search_id, }
def write(self, values): """Override to: - Add constraints to prevent doing changes that are not supported such as modifying the template or the attribute of existing lines. - Clean up related values and related variants when archiving or when updating `value_ids`. """ if 'product_tmpl_id' in values: for ptal in self: if ptal.product_tmpl_id.id != values['product_tmpl_id']: raise UserError( _("You cannot move the attribute %s from the product %s to the product %s.") % (ptal.attribute_id.display_name, ptal.product_tmpl_id.display_name, values['product_tmpl_id']) ) if 'attribute_id' in values: for ptal in self: if ptal.attribute_id.id != values['attribute_id']: raise UserError( _("On the product %s you cannot transform the attribute %s into the attribute %s.") % (ptal.product_tmpl_id.display_name, ptal.attribute_id.display_name, values['attribute_id']) ) # Remove all values while archiving to make sure the line is clean if it # is ever activated again. if not values.get('active', True): values['value_ids'] = [(5, 0, 0)] res = super(ProductTemplateAttributeLine, self).write(values) if 'active' in values: self.flush() self.env['product.template'].invalidate_cache(fnames=['attribute_line_ids']) # If coming from `create`, no need to update the values and the variants # before all lines are created. if self.env.context.get('update_product_template_attribute_values', True): self._update_product_template_attribute_values() return res
def get_access_token(self, scope=None): Config = self.env['ir.config_parameter'].sudo() google_drive_refresh_token = Config.get_param('google_drive_refresh_token') user_is_admin = self.env.is_admin() if not google_drive_refresh_token: if user_is_admin: dummy, action_id = self.env['ir.model.data'].get_object_reference('base_setup', 'action_general_configuration') msg = _("There is no refresh code set for Google Drive. You can set it up from the configuration panel.") raise RedirectWarning(msg, action_id, _('Go to the configuration panel')) else: raise UserError(_("Google Drive is not yet configured. Please contact your administrator.")) google_drive_client_id = Config.get_param('google_drive_client_id') google_drive_client_secret = Config.get_param('google_drive_client_secret') #For Getting New Access Token With help of old Refresh Token data = { 'client_id': google_drive_client_id, 'refresh_token': google_drive_refresh_token, 'client_secret': google_drive_client_secret, 'grant_type': "refresh_token", 'scope': scope or 'https://www.googleapis.com/auth/drive' } headers = {"Content-type": "application/x-www-form-urlencoded"} try: req = requests.post(GOOGLE_TOKEN_ENDPOINT, data=data, headers=headers, timeout=TIMEOUT) req.raise_for_status() except requests.HTTPError: if user_is_admin: dummy, action_id = self.env['ir.model.data'].get_object_reference('base_setup', 'action_general_configuration') msg = _("Something went wrong during the token generation. Please request again an authorization code .") raise RedirectWarning(msg, action_id, _('Go to the configuration panel')) else: raise UserError(_("Google Drive is not yet configured. Please contact your administrator.")) return req.json().get('access_token')
def _portal_post_check_attachments(self, attachment_ids, attachment_tokens): if len(attachment_tokens) != len(attachment_ids): raise UserError(_("An access token must be provided for each attachment.")) for (attachment_id, access_token) in zip(attachment_ids, attachment_tokens): try: CustomerPortal._document_check_access(self, 'ir.attachment', attachment_id, access_token) except (AccessError, MissingError): raise UserError(_("The attachment %s does not exist or you do not have the rights to access it.") % attachment_id)
def _find_additional_data(self, currency_code, account_number): """ Look for a res.currency and account.journal using values extracted from the statement and make sure it's consistent. """ company_currency = self.env.company.currency_id journal_obj = self.env['account.journal'] currency = None sanitized_account_number = sanitize_account_number(account_number) if currency_code: currency = self.env['res.currency'].search( [('name', '=ilike', currency_code)], limit=1) if not currency: raise UserError( _("No currency found matching '%s'.") % currency_code) if currency == company_currency: currency = False journal = journal_obj.browse(self.env.context.get('journal_id', [])) if account_number: # No bank account on the journal : create one from the account number of the statement if journal and not journal.bank_account_id: journal.set_bank_account(account_number) # No journal passed to the wizard : try to find one using the account number of the statement elif not journal: journal = journal_obj.search([ ('bank_account_id.sanitized_acc_number', '=', sanitized_account_number) ]) # Already a bank account on the journal : check it's the same as on the statement else: if not self._check_journal_bank_account( journal, sanitized_account_number): raise UserError( _('The account of this statement (%s) is not the same as the journal (%s).' ) % (account_number, journal.bank_account_id.acc_number)) # If importing into an existing journal, its currency must be the same as the bank statement if journal: journal_currency = journal.currency_id if currency is None: currency = journal_currency if currency and currency != journal_currency: statement_cur_code = not currency and company_currency.name or currency.name journal_cur_code = not journal_currency and company_currency.name or journal_currency.name raise UserError( _('The currency of the bank statement (%s) is not the same as the currency of the journal (%s).' ) % (statement_cur_code, journal_cur_code)) # If we couldn't find / can't create a journal, everything is lost if not journal and not account_number: raise UserError( _('Cannot find in which journal import this statement. Please manually select a journal.' )) return currency, journal
def done(self): self._check_company() pickings = self.mapped('picking_ids').filtered( lambda picking: picking.state not in ('cancel', 'done')) if any(picking.state not in ('assigned') for picking in pickings): raise UserError( _('Some transfers are still waiting for goods. Please check or force their availability before setting this batch to done.' )) for picking in pickings: picking.message_post( body= "<b>%s:</b> %s <a href=#id=%s&view_type=form&model=stock.picking.batch>%s</a>" % (_("Transferred by"), _("Batch Transfer"), picking.batch_id.id, picking.batch_id.name)) picking_to_backorder = self.env['stock.picking'] picking_without_qty_done = self.env['stock.picking'] for picking in pickings: if all([x.qty_done == 0.0 for x in picking.move_line_ids]): # If no lots when needed, raise error picking_type = picking.picking_type_id if (picking_type.use_create_lots or picking_type.use_existing_lots): for ml in picking.move_line_ids: if ml.product_id.tracking != 'none': raise UserError( _('Some products require lots/serial numbers.') ) # Check if we need to set some qty done. picking_without_qty_done |= picking elif picking._check_backorder(): picking_to_backorder |= picking else: picking.action_done() if picking_without_qty_done: view = self.env.ref('stock.view_immediate_transfer') wiz = self.env['stock.immediate.transfer'].create({ 'pick_ids': [(4, p.id) for p in picking_without_qty_done], 'pick_to_backorder_ids': [(4, p.id) for p in picking_to_backorder], }) return { 'name': _('Immediate Transfer?'), 'type': 'ir.actions.act_window', '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 picking_to_backorder: return picking_to_backorder.action_generate_backorder_wizard() # Change the state only if there is no other action (= wizard) waiting. self.write({'state': 'done'}) return True
def test_smtp_connection(self): for server in self: smtp = False try: smtp = self.connect(mail_server_id=server.id) # simulate sending an email from current user's address - without sending it! email_from, email_to = self.env.user.email, '*****@*****.**' if not email_from: raise UserError( _('Please configure an email on the current user to simulate ' 'sending an email message via this outgoing server')) # Testing the MAIL FROM step should detect sender filter problems (code, repl) = smtp.mail(email_from) if code != 250: raise UserError( _('The server refused the sender address (%(email_from)s) ' 'with error %(repl)s') % locals()) # Testing the RCPT TO step should detect most relaying problems (code, repl) = smtp.rcpt(email_to) if code not in (250, 251): raise UserError( _('The server refused the test recipient (%(email_to)s) ' 'with error %(repl)s') % locals()) # Beginning the DATA step should detect some deferred rejections # Can't use self.data() as it would actually send the mail! smtp.putcmd("data") (code, repl) = smtp.getreply() if code != 354: raise UserError( _('The server refused the test connection ' 'with error %(repl)s') % locals()) except UserError as e: # let UserErrors (messages) bubble up raise e except Exception as e: raise UserError( _("Connection Test Failed! Here is what we got instead:\n %s" ) % ustr(e)) finally: try: if smtp: smtp.close() except Exception: # ignored, just a consequence of the previous exception pass title = _("Connection Test Succeeded!") message = _("Everything seems properly set up!") return { 'type': 'ir.actions.client', 'tag': 'display_notification', 'params': { 'title': title, 'message': message, 'sticky': False, } }
def _check_approval_update(self, state): """ Check if target state is achievable. """ if self.env.is_superuser(): return current_employee = self.env.user.employee_id if not current_employee: return is_officer = self.env.user.has_group( 'hr_holidays.group_hr_holidays_user') is_manager = self.env.user.has_group( 'hr_holidays.group_hr_holidays_manager') for holiday in self: val_type = holiday.holiday_status_id.sudo().validation_type if state == 'confirm': continue if state == 'draft': if holiday.employee_id != current_employee and not is_manager: raise UserError( _('Only a time off Manager can reset other people allocation.' )) continue if not is_officer and self.env.user != holiday.employee_id.leave_manager_id: raise UserError( _('Only a time off Officer/Responsible or Manager can approve or refuse time off requests.' )) if is_officer or self.env.user == holiday.employee_id.leave_manager_id: # use ir.rule based first access check: department, members, ... (see security.xml) holiday.check_access_rule('write') if holiday.employee_id == current_employee and not is_manager: raise UserError( _('Only a time off Manager can approve its own requests.')) if (state == 'validate1' and val_type == 'both') or (state == 'validate' and val_type == 'manager'): if self.env.user == holiday.employee_id.leave_manager_id and self.env.user != holiday.employee_id.user_id: continue manager = holiday.employee_id.parent_id or holiday.employee_id.department_id.manager_id if (manager and manager != current_employee ) and not self.env.user.has_group( 'hr_holidays.group_hr_holidays_manager'): raise UserError( _('You must be either %s\'s manager or time off manager to approve this time off' ) % (holiday.employee_id.name)) if state == 'validate' and val_type == 'both': if not self.env.user.has_group( 'hr_holidays.group_hr_holidays_manager'): raise UserError( _('Only a Time off Manager can apply the second approval on allocation requests.' ))
def process_coupon(self): """ Apply the entered coupon code if valid, raise an UserError otherwise. """ sales_order = self.env['sale.order'].browse( self.env.context.get('active_id')) error_status = self.apply_coupon(sales_order, self.coupon_code) if error_status.get('error', False): raise UserError(error_status.get('error', False)) if error_status.get('not_found', False): raise UserError(error_status.get('not_found', False))
def _check_general_rights(self, vals={}): post = self.post_id if vals.get('post_id'): post = self.env['forum.post'].browse(vals.get('post_id')) if not self.env.is_admin(): # own post check if self._uid == post.create_uid.id: raise UserError(_('It is not allowed to vote for its own post.')) # own vote check if self._uid != self.user_id.id: raise UserError(_('It is not allowed to modify someone else\'s vote.'))
def button_uninstall(self): if 'base' in self.mapped('name'): raise UserError(_("The `base` module cannot be uninstalled")) if not all(state in ('installed', 'to upgrade') for state in self.mapped('state')): raise UserError( _("One or more of the selected modules have already been uninstalled, if you " "believe this to be an error, you may try again later or contact support." )) deps = self.downstream_dependencies() (self + deps).write({'state': 'to remove'}) return dict(ACTION_DICT, name=_('Uninstall'))
def _run(self, records): for box in self: for record in records: if not record.journal_id: raise UserError( _("Please check that the field 'Journal' is set on the Bank Statement" )) if not record.journal_id.company_id.transfer_account_id: raise UserError( _("Please check that the field 'Transfer Account' is set on the company." )) box._create_bank_statement_line(record) return {}
def button_reg_close(self): """ Close Registration """ for registration in self: today = fields.Datetime.now() if registration.event_id.date_begin <= today and registration.event_id.state == 'confirm': registration.write({'state': 'done', 'date_closed': today}) elif registration.event_id.state == 'draft': raise UserError( _("You must wait the event confirmation before doing this action." )) else: raise UserError( _("You must wait the event starting day before doing this action." ))
def write(self, vals): if 'company_id' in vals: for lot in self: if lot.company_id.id != vals['company_id']: raise UserError(_("Changing the company of this record is forbidden at this point, you should rather archive it and create a new one.")) if 'product_id' in vals and any([vals['product_id'] != lot.product_id.id for lot in self]): move_lines = self.env['stock.move.line'].search([('lot_id', 'in', self.ids), ('product_id', '!=', vals['product_id'])]) if move_lines: raise UserError(_( 'You are not allowed to change the product linked to a serial or lot number ' + 'if some stock moves have already been created with that number. ' + 'This would lead to inconsistencies in your stock.' )) return super(ProductionLot, self).write(vals)
def create_invoices(self): sale_orders = self.env['sale.order'].browse( self._context.get('active_ids', [])) if self.advance_payment_method == 'delivered': sale_orders._create_invoices(final=self.deduct_down_payments) else: # Create deposit product if necessary if not self.product_id: vals = self._prepare_deposit_product() self.product_id = self.env['product.product'].create(vals) self.env['ir.config_parameter'].sudo().set_param( 'sale.default_deposit_product_id', self.product_id.id) sale_line_obj = self.env['sale.order.line'] for order in sale_orders: amount, name = self._get_advance_details(order) if self.product_id.invoice_policy != 'order': raise UserError( _('The product used to invoice a down payment should have an invoice policy set to "Ordered quantities". Please update your deposit product to be able to create a deposit invoice.' )) if self.product_id.type != 'service': raise UserError( _("The product used to invoice a down payment should be of type 'Service'. Please use another product or update this product." )) taxes = self.product_id.taxes_id.filtered( lambda r: not order.company_id or r.company_id == order. company_id) if order.fiscal_position_id and taxes: tax_ids = order.fiscal_position_id.map_tax( taxes, self.product_id, order.partner_shipping_id).ids else: tax_ids = taxes.ids context = {'lang': order.partner_id.lang} analytic_tag_ids = [] for line in order.order_line: analytic_tag_ids = [ (4, analytic_tag.id, None) for analytic_tag in line.analytic_tag_ids ] so_line_values = self._prepare_so_line(order, analytic_tag_ids, tax_ids, amount) so_line = sale_line_obj.create(so_line_values) del context self._create_invoice(order, so_line, amount) if self._context.get('open_invoices', False): return sale_orders.action_view_invoice() return {'type': 'ir.actions.act_window_close'}
def _svl_empty_stock_am(self, stock_valuation_layers): move_vals_list = [] product_accounts = { product.id: product.product_tmpl_id.get_product_accounts() for product in stock_valuation_layers.mapped('product_id') } for out_stock_valuation_layer in stock_valuation_layers: product = out_stock_valuation_layer.product_id expense_account = product.property_account_expense_id or product.categ_id.property_account_expense_categ_id if not expense_account: raise UserError( _('Please define an expense account for this product: "%s" (id:%d) - or for its category: "%s".' ) % (product.name, product.id, self.name)) if not product_accounts[product.id].get('stock_valuation'): raise UserError( _('You don\'t have any stock valuation account defined on your product category. You must define one before processing this operation.' )) debit_account_id = expense_account.id credit_account_id = product_accounts[ product.id]['stock_valuation'].id value = out_stock_valuation_layer.value move_vals = { 'journal_id': product_accounts[product.id]['stock_journal'].id, 'company_id': self.env.company.id, 'ref': product.default_code, 'stock_valuation_layer_ids': [(6, None, [out_stock_valuation_layer.id])], 'line_ids': [(0, 0, { 'name': out_stock_valuation_layer.description, 'account_id': debit_account_id, 'debit': abs(value), 'credit': 0, 'product_id': product.id, }), (0, 0, { 'name': out_stock_valuation_layer.description, 'account_id': credit_account_id, 'debit': 0, 'credit': abs(value), 'product_id': product.id, })], 'type': 'entry', } move_vals_list.append(move_vals) return move_vals_list
def write(self, values): if 'production_id' in values: raise UserError( _('You cannot link this work order to another manufacturing order.' )) if 'workcenter_id' in values: for workorder in self: if workorder.workcenter_id.id != values['workcenter_id']: if workorder.state in ('progress', 'done', 'cancel'): raise UserError( _('You cannot change the workcenter of a work order that is in progress or done.' )) workorder.leave_id.resource_id = self.env[ 'mrp.workcenter'].browse( values['workcenter_id']).resource_id if list(values.keys()) != ['time_ids'] and any( workorder.state == 'done' for workorder in self): raise UserError(_('You can not change the finished work order.')) if 'date_planned_start' in values or 'date_planned_finished' in values: for workorder in self: start_date = fields.Datetime.to_datetime( values.get( 'date_planned_start')) or workorder.date_planned_start end_date = fields.Datetime.to_datetime( values.get('date_planned_finished') ) or workorder.date_planned_finished if start_date and end_date and start_date > end_date: raise UserError( _('The planned end date of the work order cannot be prior to the planned start date, please correct this to save the work order.' )) # Update MO dates if the start date of the first WO or the # finished date of the last WO is update. if workorder == workorder.production_id.workorder_ids[ 0] and 'date_planned_start' in values: workorder.production_id.with_context( force_date=True).write({ 'date_planned_start': fields.Datetime.to_datetime( values['date_planned_start']) }) if workorder == workorder.production_id.workorder_ids[ -1] and 'date_planned_finished' in values: workorder.production_id.with_context( force_date=True).write({ 'date_planned_finished': fields.Datetime.to_datetime( values['date_planned_finished']) }) return super(MrpWorkorder, self).write(values)
def _svl_replenish_stock_am(self, stock_valuation_layers): move_vals_list = [] product_accounts = { product.id: product.product_tmpl_id.get_product_accounts() for product in stock_valuation_layers.mapped('product_id') } for out_stock_valuation_layer in stock_valuation_layers: product = out_stock_valuation_layer.product_id if not product_accounts[product.id].get('stock_input'): raise UserError( _('You don\'t have any input valuation account defined on your product category. You must define one before processing this operation.' )) if not product_accounts[product.id].get('stock_valuation'): raise UserError( _('You don\'t have any stock valuation account defined on your product category. You must define one before processing this operation.' )) debit_account_id = product_accounts[ product.id]['stock_valuation'].id credit_account_id = product_accounts[product.id]['stock_input'].id value = out_stock_valuation_layer.value move_vals = { 'journal_id': product_accounts[product.id]['stock_journal'].id, 'company_id': self.env.company.id, 'ref': product.default_code, 'stock_valuation_layer_ids': [(6, None, [out_stock_valuation_layer.id])], 'line_ids': [(0, 0, { 'name': out_stock_valuation_layer.description, 'account_id': debit_account_id, 'debit': abs(value), 'credit': 0, 'product_id': product.id, }), (0, 0, { 'name': out_stock_valuation_layer.description, 'account_id': credit_account_id, 'debit': 0, 'credit': abs(value), 'product_id': product.id, })], 'type': 'entry', } move_vals_list.append(move_vals) return move_vals_list
def default_get(self, fields): result = super(ProjectCreateSalesOrder, self).default_get(fields) active_model = self._context.get('active_model') if active_model != 'project.project': raise UserError(_("You can only apply this action from a project.")) active_id = self._context.get('active_id') if 'project_id' in fields and active_id: project = self.env['project.project'].browse(active_id) if project.billable_type != 'no': raise UserError(_("The project is already billable.")) result['project_id'] = active_id result['partner_id'] = project.partner_id.id return result
def button_start(self): self.ensure_one() # As button_start is automatically called in the new view if self.state in ('done', 'cancel'): return True # Need a loss in case of the real time exceeding the expected timeline = self.env['mrp.workcenter.productivity'] if self.duration < self.duration_expected: loss_id = self.env['mrp.workcenter.productivity.loss'].search( [('loss_type', '=', 'productive')], limit=1) if not len(loss_id): raise UserError( _("You need to define at least one productivity loss in the category 'Productivity'. Create one from the Manufacturing app, menu: Configuration / Productivity Losses." )) else: loss_id = self.env['mrp.workcenter.productivity.loss'].search( [('loss_type', '=', 'performance')], limit=1) if not len(loss_id): raise UserError( _("You need to define at least one productivity loss in the category 'Performance'. Create one from the Manufacturing app, menu: Configuration / Productivity Losses." )) if self.production_id.state != 'progress': self.production_id.write({ 'date_start': datetime.now(), }) timeline.create({ 'workorder_id': self.id, 'workcenter_id': self.workcenter_id.id, 'description': _('Time Tracking: ') + self.env.user.name, 'loss_id': loss_id[0].id, 'date_start': datetime.now(), 'user_id': self.env.user.id, # FIXME sle: can be inconsistent with company_id 'company_id': self.company_id.id, }) if self.state == 'progress': return True else: start_date = datetime.now() vals = { 'state': 'progress', 'date_start': start_date, 'date_planned_start': start_date, } if self.date_planned_finished < start_date: vals['date_planned_finished'] = start_date return self.write(vals)
def _check_with_xsd(tree_or_str, stream, env=None): """Check an XML against an XSD schema. This will raise a UserError if the XML file is not valid according to the XSD file. :param tree_or_str (etree, str): representation of the tree to be checked :param stream (io.IOBase, str): the byte stream used to build the XSD schema. If env is given, it can also be the name of an attachment in the filestore :param env (harpiya.api.Environment): If it is given, it enables resolving the imports of the schema in the filestore with ir.attachments. """ if not isinstance(tree_or_str, etree._Element): tree_or_str = etree.fromstring(tree_or_str) parser = etree.XMLParser() if env: parser.resolvers.add(harpiya_resolver(env)) if isinstance(stream, str) and stream.endswith('.xsd'): attachment = env['ir.attachment'].search([('name', '=', stream)]) if not attachment: raise FileNotFoundError() stream = BytesIO(base64.b64decode(attachment.datas)) xsd_schema = etree.XMLSchema(etree.parse(stream, parser=parser)) try: xsd_schema.assertValid(tree_or_str) except etree.DocumentInvalid as xml_errors: raise UserError('\n'.join(str(e) for e in xml_errors.error_log))