def _remove_reified_groups(self, values): """ The method to return values without reified group fields Args: * values - dict of values Methods: * is_boolean_group * get_boolean_group * is_selection_groups * get_selection_groups Returns: * dict of updated values """ add, rem = [], [] values1 = {} for key, val in values.items(): if is_boolean_group(key): (add if val else rem).append(get_boolean_group(key)) elif is_selection_groups(key): rem += get_selection_groups(key) if val: add.append(val) else: values1[key] = val if 'group_ids' not in values and (add or rem): values1['group_ids'] = list(chain( pycompat.izip(repeat(3), rem), pycompat.izip(repeat(4), add) )) return values1
def test_banxico_currency_update(self): self.company.currency_id = self.mxn # Using self.usd.rate=1 and self.mxn.rate != 1 self.set_rate(self.usd, 1.0) self.assertEqual(self.usd.rate, 1.0) self.set_rate(self.mxn, 10.0) self.assertEqual(self.mxn.rate, 10.0) with patch('suds.client.Client', new=serviceClientMock): self.company.update_currency_rates() self.assertNotEqual(self.usd.rate, 1.0) self.assertNotEqual(self.mxn.rate, 10.0) foreigns1 = [ foreign_currency._convert(1.0, self.mxn, company=self.company, date=fields.Date.today()) for foreign_currency in self.foreign_currencies ] # Using self.mxn.rate=1 and self.usd.rate != 1 self.set_rate(self.mxn, 1.0) self.assertEqual(self.mxn.rate, 1.0) self.set_rate(self.usd, 0.1) self.assertEqual(self.usd.compare_amounts(self.usd.rate, 0.1), 0) with patch('suds.client.Client', new=serviceClientMock): self.company.update_currency_rates() self.assertEqual(self.mxn.rate, 1.0) self.assertNotEqual(self.usd.rate, 1.0 / 10.0) foreigns2 = [ foreign_currency._convert(1.0, self.mxn, company=self.company, date=fields.Date.today()) for foreign_currency in self.foreign_currencies ] for curr, foreign1, foreign2 in pycompat.izip(self.foreign_currencies, foreigns1, foreigns2): self.assertEqual( curr.compare_amounts(foreign1, foreign2), 0, "%s diff rate %s != %s" % (curr.name, foreign1, foreign2)) # Compare expected xml mocked rate values vs real ones for curr, real_rate, expected_rate in pycompat.izip( self.foreign_currencies, foreigns1, self.foreign_expected_rates): self.assertEqual( curr.compare_amounts(real_rate, expected_rate), 0, "%s diff rate %s != %s" % (curr.name, real_rate, expected_rate))
def process_bank_statement_line(self, st_line_ids, data): """ Handles data sent from the bank statement reconciliation widget (and can otherwise serve as an old-API bridge) :param st_line_ids :param list of dicts data: must contains the keys 'counterpart_aml_dicts', 'payment_aml_ids' and 'new_aml_dicts', whose value is the same as described in process_reconciliation except that ids are used instead of recordsets. """ st_lines = self.env['account.bank.statement.line'].browse(st_line_ids) AccountMoveLine = self.env['account.move.line'] ctx = dict(self._context, force_price_include=False) for st_line, datum in pycompat.izip(st_lines, data): payment_aml_rec = AccountMoveLine.browse(datum.get('payment_aml_ids', [])) for aml_dict in datum.get('counterpart_aml_dicts', []): aml_dict['move_line'] = AccountMoveLine.browse(aml_dict['counterpart_aml_id']) del aml_dict['counterpart_aml_id'] if datum.get('partner_id') is not None: st_line.write({'partner_id': datum['partner_id']}) st_line.with_context(ctx).process_reconciliation( datum.get('counterpart_aml_dicts', []), payment_aml_rec, datum.get('new_aml_dicts', []))
def create(self, vals_list): ''' Store the initial standard price in order to be able to retrieve the cost of a product template for a given date''' # TDE FIXME: context brol for vals in vals_list: tools.image_resize_images(vals) templates = super(ProductTemplate, self).create(vals_list) if "create_product_product" not in self._context: templates.with_context(create_from_tmpl=True).create_variant_ids() # This is needed to set given values to first variant after creation for template, vals in pycompat.izip(templates, vals_list): related_vals = {} if vals.get('barcode'): related_vals['barcode'] = vals['barcode'] if vals.get('default_code'): related_vals['default_code'] = vals['default_code'] if vals.get('standard_price'): related_vals['standard_price'] = vals['standard_price'] if vals.get('volume'): related_vals['volume'] = vals['volume'] if vals.get('weight'): related_vals['weight'] = vals['weight'] if related_vals: template.write(related_vals) return templates
def create(self, vals_list): if self.env.context.get('import_file'): self._check_import_consistency(vals_list) for vals in vals_list: if vals.get('website'): vals['website'] = self._clean_website(vals['website']) if vals.get('parent_id'): vals['company_name'] = False if vals.get('customer'): vals['customer'] = False # compute default image in create, because computing gravatar in the onchange # cannot be easily performed if default images are in the way if not vals.get('image'): vals['image'] = self._get_default_image( vals.get('type'), vals.get('is_company'), vals.get('parent_id')) tools.image_resize_images(vals, sizes={'image': (1024, None)}) partners = super(Partner, self).create(vals_list) if self.env.context.get('_partners_skip_fields_sync'): return partners for partner, vals in pycompat.izip(partners, vals_list): partner._fields_sync(vals) partner._handle_first_contact_creation() return partners
def _get_post_karma_rights(self): user = self.env.user is_admin = user.id == SUPERUSER_ID # sudoed recordset instead of individual posts so values can be # prefetched in bulk for post, post_sudo in pycompat.izip(self, self.sudo()): is_creator = post.create_uid == user post.karma_accept = post.forum_id.karma_answer_accept_own if post.parent_id.create_uid == user else post.forum_id.karma_answer_accept_all post.karma_edit = post.forum_id.karma_edit_own if is_creator else post.forum_id.karma_edit_all post.karma_close = post.forum_id.karma_close_own if is_creator else post.forum_id.karma_close_all post.karma_unlink = post.forum_id.karma_unlink_own if is_creator else post.forum_id.karma_unlink_all post.karma_comment = post.forum_id.karma_comment_own if is_creator else post.forum_id.karma_comment_all post.karma_comment_convert = post.forum_id.karma_comment_convert_own if is_creator else post.forum_id.karma_comment_convert_all post.can_ask = is_admin or user.karma >= post.forum_id.karma_ask post.can_answer = is_admin or user.karma >= post.forum_id.karma_answer post.can_accept = is_admin or user.karma >= post.karma_accept post.can_edit = is_admin or user.karma >= post.karma_edit post.can_close = is_admin or user.karma >= post.karma_close post.can_unlink = is_admin or user.karma >= post.karma_unlink post.can_upvote = is_admin or user.karma >= post.forum_id.karma_upvote post.can_downvote = is_admin or user.karma >= post.forum_id.karma_downvote post.can_comment = is_admin or user.karma >= post.karma_comment post.can_comment_convert = is_admin or user.karma >= post.karma_comment_convert post.can_view = is_admin or user.karma >= post.karma_close or (post_sudo.create_uid.karma > 0 and (post_sudo.active or post_sudo.create_uid == user)) post.can_display_biography = is_admin or post_sudo.create_uid.karma >= post.forum_id.karma_user_bio post.can_post = is_admin or user.karma >= post.forum_id.karma_post post.can_flag = is_admin or user.karma >= post.forum_id.karma_flag post.can_moderate = is_admin or user.karma >= post.forum_id.karma_moderate
def test_invoice_with_discount(self): """ Test invoice with a discount and check discount applied on both SO lines and an invoice lines """ # Update discount and delivered quantity on SO lines self.sol_prod_order.write({'discount': 20.0}) self.sol_serv_deliver.write({'discount': 20.0, 'qty_delivered': 4.0}) self.sol_serv_order.write({'discount': -10.0}) self.sol_prod_deliver.write({'qty_delivered': 2.0}) for line in self.sale_order.order_line.filtered(lambda l: l.discount): product_price = line.price_unit * line.product_uom_qty self.assertEquals(line.discount, (product_price - line.price_subtotal) / product_price * 100, 'Discount should be applied on order line') self.sale_order.action_confirm() # Let's do an invoice with invoiceable lines payment = self.env['sale.advance.payment.inv'].with_context(self.context).create({ 'advance_payment_method': 'delivered' }) payment.create_invoices() invoice = self.sale_order.invoice_ids[0] invoice.action_invoice_open() # Check discount appeared on both SO lines and invoice lines for line, inv_line in pycompat.izip(self.sale_order.order_line, invoice.invoice_line_ids): self.assertEquals(line.discount, inv_line.discount, 'Discount on lines of order and invoice should be same')
def _compute_display_name(self): self2 = self.with_context(display_website=False) super(Partner, self2)._compute_display_name() # onchange uses the cache to retrieve value, we need to copy computed_value into the initial env for record, record2 in izip(self, self2): record.display_name = record2.display_name
def create(self, vals_list): result = super(Base, self).create(vals_list) for record, values in pycompat.izip(result, vals_list): if not values: continue write_log(record, 'create', values) return result
def _create_all_specific_views(self, processed_modules): """ When creating a generic child view, we should also create that view under specific view trees (COW'd). Top level view (no inherit_id) do not need that behavior as they will be shared between websites since there is no specific yet. """ # Only for the modules being processed regex = '^(%s)[.]' % '|'.join(processed_modules) # Retrieve the views through a SQl query to avoid ORM queries inside of for loop # Retrieves all the views that are missing their specific counterpart with all the # specific view parent id and their website id in one query query = """ SELECT generic.id, ARRAY[array_agg(spec_parent.id), array_agg(spec_parent.website_id)] FROM ir_ui_view generic INNER JOIN ir_ui_view generic_parent ON generic_parent.id = generic.inherit_id INNER JOIN ir_ui_view spec_parent ON spec_parent.key = generic_parent.key LEFT JOIN ir_ui_view specific ON specific.key = generic.key AND specific.website_id = spec_parent.website_id WHERE generic.type='qweb' AND generic.website_id IS NULL AND generic.key ~ %s AND spec_parent.website_id IS NOT NULL AND specific.id IS NULL GROUP BY generic.id """ self.env.cr.execute(query, (regex, )) result = dict(self.env.cr.fetchall()) for record in self.browse(result.keys()): specific_parent_view_ids, website_ids = result[record.id] for specific_parent_view_id, website_id in pycompat.izip(specific_parent_view_ids, website_ids): record.with_context(website_id=website_id).write({ 'inherit_id': specific_parent_view_id, }) super(View, self)._create_all_specific_views(processed_modules)
def _compute_full_name(self): # Important: value must be stored in environment of group, not group1! for group, group1 in pycompat.izip(self, self.sudo()): if group1.category_id: group.full_name = '%s / %s' % (group1.category_id.name, group1.name) else: group.full_name = group1.name
def _website_price(self): qty = self._context.get('quantity', 1.0) partner = self.env.user.partner_id current_website = self.env['website'].get_current_website() pricelist = current_website.get_current_pricelist() company_id = current_website.company_id context = dict(self._context, pricelist=pricelist.id, partner=partner) self2 = self.with_context( context) if self._context != context else self ret = self.env.user.has_group( 'account.group_show_line_subtotals_tax_excluded' ) and 'total_excluded' or 'total_included' for p, p2 in pycompat.izip(self, self2): taxes = partner.property_account_position_id.map_tax( p.sudo().taxes_id.filtered( lambda x: x.company_id == company_id), p, partner) p.website_price = taxes.compute_all(p2.price, pricelist.currency_id, quantity=qty, product=p2, partner=partner)[ret] price_without_pricelist = taxes.compute_all( p.list_price, pricelist.currency_id)[ret] p.website_price_difference = False if float_is_zero( price_without_pricelist - p.website_price, precision_rounding=pricelist.currency_id.rounding) else True p.website_public_price = taxes.compute_all(p2.lst_price, quantity=qty, product=p2, partner=partner)[ret]
def create(self, vals_list): products = super(ProductProduct, self.with_context(create_product_product=True)).create(vals_list) for product, vals in pycompat.izip(products, vals_list): # When a unique variant is created from tmpl then the standard price is set by _set_standard_price if not (self.env.context.get('create_from_tmpl') and len(product.product_tmpl_id.product_variant_ids) == 1): product._set_standard_price(vals.get('standard_price') or 0.0) return products
def _compute_product_price_rule(self): pricelist_id_or_name = self._context.get('pricelist') pricelist_item = {} if pricelist_id_or_name: pricelist = None partner = self._context.get('partner', False) quantity = self._context.get('quantity', 1.0) # Support context pricelists specified as display_name or ID for compatibility if isinstance(pricelist_id_or_name, pycompat.string_types): pricelist_name_search = self.env['product.pricelist'].name_search(pricelist_id_or_name, operator='=', limit=1) if pricelist_name_search: pricelist = self.env['product.pricelist'].browse([pricelist_name_search[0][0]]) elif isinstance(pricelist_id_or_name, pycompat.integer_types): pricelist = self.env['product.pricelist'].browse(pricelist_id_or_name) if pricelist: quantities = [quantity] * len(self) partners = [partner] * len(self) pricelist_item = pricelist.price_rule_get_multi(list(pycompat.izip(self, quantities, partners))) for product in self: rule_id = pricelist_item.get(product.id) and pricelist_item.get(product.id).get(pricelist.id)[1] if rule_id: pricelist_code = self.env['product.pricelist.item'].browse([rule_id]) if pricelist_code: product.pricelist_code = pricelist_code.code
def _website_price(self): qty = self._context.get('quantity', 1.0) partner = self.env.user.partner_id pricelist = self.env['website'].get_current_website( ).get_current_pricelist() context = dict(self._context, pricelist=pricelist.id, partner=partner) self2 = self.with_context( context) if self._context != context else self ret = self.env.user.has_group( 'sale.group_show_price_subtotal' ) and 'total_excluded' or 'total_included' for p, p2 in pycompat.izip(self, self2): taxes = partner.property_account_position_id.map_tax(p.taxes_id) p.website_price = taxes.compute_all(p2.price, pricelist.currency_id, quantity=qty, product=p2, partner=partner)[ret] p.website_public_price = taxes.compute_all(p2.lst_price, quantity=qty, product=p2, partner=partner)[ret]
def create(self, vals_list): user_ids_list = [vals.pop('users', None) for vals in vals_list] groups = super(GroupsImplied, self).create(vals_list) for group, user_ids in pycompat.izip(groups, user_ids_list): if user_ids: # delegate addition of users to add implied groups group.write({'users': user_ids}) return groups
def _compute_full_name(self): # 仅仅原子权限组name才显示全名 for group, group1 in pycompat.izip(self, self.sudo()): if group1.category_id and group1.atomic: group.full_name = '%s / %s' % (group1.category_id.name, group1.name) else: group.full_name = group1.name
def test_multiple(self): """ With two "concurrent" o2ms, exports the first line combined, then exports the rows for the first o2m, then the rows for the second o2m. """ fields = ['const', 'child1/value', 'child2/value'] child1 = [(0, False, { 'value': v, 'str': 'record%.02d' % index }) for index, v in pycompat.izip(itertools.count(), [4, 42, 36, 4, 13]) ] child2 = [(0, False, { 'value': v, 'str': 'record%.02d' % index }) for index, v in pycompat.izip(itertools.count(10), [8, 12, 8, 55, 33, 13])] self.assertEqual( self.export(child1=child1, child2=False, fields=fields), [ [u'36', u'4', False], ['', u'42', ''], ['', u'36', ''], ['', u'4', ''], ['', u'13', ''], ]) self.assertEqual( self.export(child1=False, child2=child2, fields=fields), [ [u'36', False, u'8'], ['', '', u'12'], ['', '', u'8'], ['', '', u'55'], ['', '', u'33'], ['', '', u'13'], ]) self.assertEqual( self.export(child1=child1, child2=child2, fields=fields), [ [u'36', u'4', u'8'], ['', u'42', ''], ['', u'36', ''], ['', u'4', ''], ['', u'13', ''], ['', '', u'12'], ['', '', u'8'], ['', '', u'55'], ['', '', u'33'], ['', '', u'13'], ])
def _compute_location_description(self): for operation, operation_sudo in izip(self, self.sudo()): operation.from_loc = '%s%s' % ( operation_sudo.location_id.name, operation.product_id and operation_sudo.package_id.name or '') operation.to_loc = '%s%s' % (operation_sudo.location_dest_id.name, operation_sudo.result_package_id.name or '')
def write(self, values): res = super(GroupsImplied, self).write(values) if values.get('users') or values.get('implied_ids'): # add all implied groups (to all users of each group) for group in self: vals = {'users': list(pycompat.izip(repeat(4), group.with_context(active_test=False).users.ids))} super(GroupsImplied, group.trans_implied_ids).write(vals) return res
def _website_price(self): # First filter out the ones that have no variant: # This makes sure that every template below has a corresponding product in the zipped result. self = self.filtered('product_variant_id') # use mapped who returns a recordset with only itself to prefetch (and don't prefetch every product_variant_ids) for template, product in pycompat.izip(self, self.mapped('product_variant_id')): template.website_price = product.website_price template.website_public_price = product.website_public_price
def iter_tax_codes(self): keys = [c.value for c in self.sheet_tax_codes.row(0)] yield keys for i in range(1, self.sheet_tax_codes.nrows): row = (c.value for c in self.sheet_tax_codes.row(i)) d = OrderedDict(pycompat.izip(keys, row)) d['sign'] = int(d['sign']) d['sequence'] = int(d['sequence']) yield d
def create(self, vals_list): products = super(ProductProduct, self.with_context(create_product_product=True)).create(vals_list) for product, vals in pycompat.izip(products, vals_list): # When a unique variant is created from tmpl then the standard price is set by _set_standard_price if not (self.env.context.get('create_from_tmpl') and len(product.product_tmpl_id.product_variant_ids) == 1): product._set_standard_price(vals.get('standard_price') or 0.0) # `_get_variant_id_for_combination` depends on existing variants self.clear_caches() return products
def _website_price(self): # First filter out the ones that have no variant: # This makes sure that every template below has a corresponding product in the zipped result. self = self.filtered('product_variant_id') # use mapped who returns a recordset with only itself to prefetch (and don't prefetch every product_variant_ids) for template, product in pycompat.izip( self, self.mapped('product_variant_id')): template.website_price = product.website_price template.website_public_price = product.website_public_price
def create(self, vals_list): """A function like create of the original in order to track the changing of done moves qty""" mls = super(StockMoveLineInheritProductStockPriceConnector, self).create(vals_list) for ml, vals in izip(mls, vals_list): if ml.state == 'done': if ml.product_id.type == 'product': ml.send_new_stock_data_product_connector_webserver() return mls
def message_post(self, **kwargs): # OVERRIDE # /!\ 'default_res_id' in self._context is used to don't process attachment when using a form view. res = super(AccountInvoice, self).message_post(**kwargs) def _get_attachment_filename(attachment): # Handle both _Attachment namedtuple in mail.thread or ir.attachment. return hasattr(attachment, 'fname') and getattr(attachment, 'fname') or attachment.name def _get_attachment_content(attachment): # Handle both _Attachment namedtuple in mail.thread or ir.attachment. return hasattr(attachment, 'content') and getattr(attachment, 'content') or base64.b64decode(attachment.datas) if 'default_res_id' not in self._context and len(self) == 1 and self.state == 'draft' and self.type in ('in_invoice', 'in_refund'): # Get attachments. # - 'attachments' is a namedtuple defined in mail.thread looking like: # _Attachment = namedtuple('Attachment', ('fname', 'content', 'info')) # - 'attachment_ids' is a list of ir.attachment records ids. attachments = kwargs.get('attachments', []) if kwargs.get('attachment_ids'): attachments += self.env['ir.attachment'].browse(kwargs['attachment_ids']) for attachment in attachments: filename = _get_attachment_filename(attachment) content = _get_attachment_content(attachment) # Check if the attachment is a pdf. if not filename.endswith('.pdf'): continue buffer = io.BytesIO(content) try: reader = PdfFileReader(buffer) # Search for Factur-x embedded file. if reader.trailer['/Root'].get('/Names') and reader.trailer['/Root']['/Names'].get('/EmbeddedFiles'): # N.B: embedded_files looks like: # ['file.xml', {'/Type': '/Filespec', '/F': 'file.xml', '/EF': {'/F': IndirectObject(22, 0)}}] embedded_files = reader.trailer['/Root']['/Names']['/EmbeddedFiles']['/Names'] # '[::2]' because it's a list [fn_1, content_1, fn_2, content_2, ..., fn_n, content_2] for filename_obj, content_obj in list(pycompat.izip(embedded_files, embedded_files[1:]))[::2]: content = content_obj.getObject()['/EF']['/F'].getData() if filename_obj == 'factur-x.xml': try: tree = etree.fromstring(content) except: continue self._import_facturx_invoice(tree) buffer.close() return res except: # Malformed PDF. pass buffer.close() return res
def load_information_from_description_file(module, mod_path=None): """ :param module: The name of the module (sale, purchase, ...) :param mod_path: Physical path of module, if not providedThe name of the module (sale, purchase, ...) """ if not mod_path: mod_path = get_module_path(module, downloaded=True) manifest_file = module_manifest(mod_path) if manifest_file: # default values for descriptor info = { 'application': False, 'author': 'Odoo S.A.', 'auto_install': False, 'category': 'Uncategorized', 'depends': [], 'description': '', 'icon': get_module_icon(module), 'installable': True, 'license': 'LGPL-3', 'post_load': None, 'version': '1.0', 'web': False, 'sequence': 100, 'summary': '', 'website': '', } info.update( pycompat.izip( 'depends data demo test init_xml update_xml demo_xml'.split(), iter(list, None))) f = tools.file_open(manifest_file, mode='rb') try: info.update(ast.literal_eval(pycompat.to_native(f.read()))) finally: f.close() if not info.get('description'): readme_path = [ opj(mod_path, x) for x in README if os.path.isfile(opj(mod_path, x)) ] if readme_path: readme_text = tools.file_open(readme_path[0]).read() info['description'] = readme_text if 'active' in info: # 'active' has been renamed 'auto_install' info['auto_install'] = info['active'] info['version'] = adapt_version(info['version']) return info _logger.debug('module %s: no manifest file found %s', module, MANIFEST_NAMES) return {}
def create(self, vals_list): # Override the original create function for the inherited model templates = super(Custom_ProductTemplate_PedroLara, self).create(vals_list) for template, vals in pycompat.izip(templates, vals_list): # Set udPallet value from form template.udPallet = vals.get('udPallet') or 0 return templates
def create(self, vals_list): templates = super(ProductTemplate, self).create(vals_list) for template, vals in pycompat.izip(templates, vals_list): related_vals = {} if vals.get('fleet_vehicle_model_id'): related_vals['fleet_vehicle_model_id'] = vals[ 'fleet_vehicle_model_id'] if related_vals: template.write(related_vals) return templates
def test_multiple(self): """ With two "concurrent" o2ms, exports the first line combined, then exports the rows for the first o2m, then the rows for the second o2m. """ fields = ['const', 'child1/value', 'child2/value'] child1 = [(0, False, {'value': v, 'str': 'record%.02d' % index}) for index, v in pycompat.izip(itertools.count(), [4, 42, 36, 4, 13])] child2 = [(0, False, {'value': v, 'str': 'record%.02d' % index}) for index, v in pycompat.izip(itertools.count(10), [8, 12, 8, 55, 33, 13])] self.assertEqual( self.export(child1=child1, child2=False, fields=fields), [ [u'36', u'4', False], ['', u'42', ''], ['', u'36', ''], ['', u'4', ''], ['', u'13', ''], ]) self.assertEqual( self.export(child1=False, child2=child2, fields=fields), [ [u'36', False, u'8'], ['', '', u'12'], ['', '', u'8'], ['', '', u'55'], ['', '', u'33'], ['', '', u'13'], ]) self.assertEqual( self.export(child1=child1, child2=child2, fields=fields), [ [u'36', u'4', u'8'], ['', u'42', ''], ['', u'36', ''], ['', u'4', ''], ['', u'13', ''], ['', '', u'12'], ['', '', u'8'], ['', '', u'55'], ['', '', u'33'], ['', '', u'13'], ])
def chk(lst, verbose=False): pvs = [] for v in lst: pv = parse_version(v) pvs.append(pv) if verbose: print(v, pv) for a, b in pycompat.izip(pvs, pvs[1:]): assert a < b, '%s < %s == %s' % (a, b, a < b)
def _website_price(self): qty = self._context.get('quantity', 1.0) partner = self.env.user.partner_id current_website = self.env['website'].get_current_website() pricelist = current_website.get_current_pricelist() company_id = current_website.company_id context = dict(self._context, pricelist=pricelist.id, partner=partner) self2 = self.with_context( context) if self._context != context else self for p, p2 in pycompat.izip(self, self2): has_margin = any(p.sudo().taxes_id.filtered('on_margin')) is_public = self.env.user == request.website.user_id show_total_included = bool(is_public or (has_margin and not is_public)) ret = 'total_included' if show_total_included else 'total_excluded' taxes = partner.property_account_position_id.map_tax( p.sudo().taxes_id.filtered( lambda x: x.company_id == company_id)) taxes_website_price = taxes.compute_all(p2.price, pricelist.currency_id, quantity=qty, product=p2, partner=partner) p.website_price = taxes_website_price[ret] # We must convert the price_without_pricelist in the same currency than the # website_price, otherwise the comparison doesn't make sense. Moreover, we show a price # difference only if the website price is lower price_without_pricelist = p.list_price if company_id.currency_id != pricelist.currency_id: price_without_pricelist = company_id.currency_id._convert( price_without_pricelist, pricelist.currency_id) taxes_price_without_pricelist = taxes.compute_all( price_without_pricelist, pricelist.currency_id) price_without_pricelist = taxes_price_without_pricelist[ret] p.website_price_difference = True if float_compare( price_without_pricelist, p.website_price, precision_rounding=pricelist.currency_id.rounding ) > 0 else False taxes_website_public_price = taxes.compute_all(p2.lst_price, quantity=qty, product=p2, partner=partner) p.website_public_price = taxes_website_public_price[ret]
def _load_records_create(self, vals_list): partners = super(Partner, self.with_context(_partners_skip_fields_sync=True) )._load_records_create(vals_list) # batch up first part of _fields_sync # group partners by commercial_partner_id (if not self) and parent_id (if type == contact) groups = collections.defaultdict(list) for partner, vals in pycompat.izip(partners, vals_list): cp_id = None if vals.get( 'parent_id') and partner.commercial_partner_id != partner: cp_id = partner.commercial_partner_id.id add_id = None if partner.parent_id and partner.type == 'contact': add_id = partner.parent_id.id groups[(cp_id, add_id)].append(partner.id) for (cp_id, add_id), children in groups.items(): # values from parents (commercial, regular) written to their common children to_write = {} # commercial fields from commercial partner if cp_id: to_write = self.browse(cp_id)._update_fields_values( self._commercial_fields()) # address fields from parent if add_id: parent = self.browse(add_id) for f in self._address_fields(): v = parent[f] if v: to_write[f] = v.id if isinstance( v, models.BaseModel) else v if to_write: self.browse(children).write(to_write) # do the second half of _fields_sync the "normal" way for partner, vals in pycompat.izip(partners, vals_list): partner._children_sync(vals) partner._handle_first_contact_creation() return partners
def create_rates(self): # Let's delete rates that could be messy to have self.rate_model.search([]).unlink() dates = (self.today, self.yesterday, self.a_week_ago) rates = (1.25, 1.00, 1/1.25) for date, rate in izip(dates, rates): self.rate_model.create({ 'currency_id': self.usd.id, 'company_id': self.company.id, 'name': date, 'rate': rate})
def get_products_price(self, products, quantities, partners, date=False, uom_id=False): """ For a given pricelist, return price for products Returns: dict{product_id: product price}, in the given pricelist """ self.ensure_one() return { product_id: res_tuple[0] for product_id, res_tuple in pycompat.items(self._compute_price_rule( list(pycompat.izip(products, quantities, partners)), date=date, uom_id=uom_id )) }
def load_information_from_description_file(module, mod_path=None): """ :param module: The name of the module (sale, purchase, ...) :param mod_path: Physical path of module, if not providedThe name of the module (sale, purchase, ...) """ if not mod_path: mod_path = get_module_path(module, downloaded=True) manifest_file = module_manifest(mod_path) if manifest_file: # default values for descriptor info = { 'application': False, 'author': 'Odoo S.A.', 'auto_install': False, 'category': 'Uncategorized', 'depends': [], 'description': '', 'icon': get_module_icon(module), 'installable': True, 'license': 'LGPL-3', 'post_load': None, 'version': '1.0', 'web': False, 'sequence': 100, 'summary': '', 'website': '', } info.update(pycompat.izip( 'depends data demo test init_xml update_xml demo_xml'.split(), iter(list, None))) f = tools.file_open(manifest_file, mode='rb') try: info.update(ast.literal_eval(pycompat.to_native(f.read()))) finally: f.close() if not info.get('description'): readme_path = [opj(mod_path, x) for x in README if os.path.isfile(opj(mod_path, x))] if readme_path: readme_text = tools.file_open(readme_path[0]).read() info['description'] = readme_text if 'active' in info: # 'active' has been renamed 'auto_install' info['auto_install'] = info['active'] info['version'] = adapt_version(info['version']) return info _logger.debug('module %s: no manifest file found %s', module, MANIFEST_NAMES) return {}
def _website_price(self): qty = self._context.get('quantity', 1.0) partner = self.env.user.partner_id pricelist = self.env['website'].get_current_website().get_current_pricelist() context = dict(self._context, pricelist=pricelist.id, partner=partner) self2 = self.with_context(context) if self._context != context else self ret = self.env.user.has_group('sale.group_show_price_subtotal') and 'total_excluded' or 'total_included' for p, p2 in pycompat.izip(self, self2): taxes = partner.property_account_position_id.map_tax(p.taxes_id) p.website_price = taxes.compute_all(p2.price, pricelist.currency_id, quantity=qty, product=p2, partner=partner)[ret] p.website_public_price = taxes.compute_all(p2.lst_price, quantity=qty, product=p2, partner=partner)[ret]
def _inverse_display_name(self): names = self.display_name.split('/') # determine sequence of categories categories = [] for name in names[:-1]: category = self.search([('name', 'ilike', name.strip())]) categories.append(category[0]) categories.append(self) # assign parents following sequence for parent, child in pycompat.izip(categories, categories[1:]): if parent and child: child.parent = parent # assign name of last category, and reassign display_name (to normalize it) self.name = names[-1].strip()
def _are_archs_equal(self, arch1, arch2): # Note that comparing the strings would not be ok as attributes order # must not be relevant if arch1.tag != arch2.tag: return False if arch1.text != arch2.text: return False if arch1.tail != arch2.tail: return False if arch1.attrib != arch2.attrib: return False if len(arch1) != len(arch2): return False return all(self._are_archs_equal(arch1, arch2) for arch1, arch2 in pycompat.izip(arch1, arch2))
def _remove_reified_groups(self, values): """ return `values` without reified group fields """ add, rem = [], [] values1 = {} for key, val in values.items(): if is_boolean_group(key): (add if val else rem).append(get_boolean_group(key)) elif is_selection_groups(key): rem += get_selection_groups(key) if val: add.append(val) else: values1[key] = val if 'groups_id' not in values and (add or rem): # remove group ids in `rem` and add group ids in `add` values1['groups_id'] = list(itertools.chain( pycompat.izip(repeat(3), rem), pycompat.izip(repeat(4), add) )) return values1
def create(self, vals_list): for vals in vals_list: if vals.get('website'): vals['website'] = self._clean_website(vals['website']) if vals.get('parent_id'): vals['company_name'] = False # compute default image in create, because computing gravatar in the onchange # cannot be easily performed if default images are in the way if not vals.get('image'): vals['image'] = self._get_default_image(vals.get('type'), vals.get('is_company'), vals.get('parent_id')) tools.image_resize_images(vals, sizes={'image': (1024, None)}) partners = super(Partner, self).create(vals_list) for partner, vals in pycompat.izip(partners, vals_list): partner._fields_sync(vals) partner._handle_first_contact_creation() return partners
def test_read_group_without_name_get(self): model = self.env['test_performance.base'] expected = self.expected_read_group() # use read_group and check the expected result with self.assertQueryCount(__system__=1, demo=1): model.invalidate_cache() result = model.read_group([], ['partner_id', 'value'], ['partner_id']) self.assertEqual(len(result), len(expected)) for res, exp in pycompat.izip(result, expected): self.assertEqual(res['__domain'], exp['__domain']) self.assertEqual(res['partner_id'][0], exp['partner_id'][0]) self.assertEqual(res['partner_id_count'], exp['partner_id_count']) self.assertEqual(res['value'], exp['value']) # now serialize to json, which should force evaluation with self.assertQueryCount(__system__=1, demo=1): json.dumps(result)
def create(self, values): """ To avoid crash during import due to unique email, return the existing records if any """ sql = '''SELECT id, email FROM mail_blacklist WHERE LOWER(email) = any (array[%s]) ''' % (', '.join(['%s'] * len(values))) params = [value['email'].lower() for value in values] self._cr.execute(sql, params) records = self._cr.fetchall() bl_ids = bl_emails = [] if records: bl_ids, bl_emails = list(izip(*records)) non_blacklisted_records = [value for value in values if value['email'] not in bl_emails] # TODO DBE Fixme : reorder ids according to incoming ids. results = super(MailBlackList, self).create(non_blacklisted_records) return self.env['mail.blacklist'].browse(bl_ids) | results
def get_attribute_value_ids(self, product): res = super(WebsiteSale, self).get_attribute_value_ids(product) variant_ids = [r[0] for r in res] # recordsets conserve the order for r, variant in izip(res, request.env['product.product'].sudo().browse(variant_ids)): r.extend([{ 'virtual_available': variant.virtual_available, 'product_type': variant.type, 'inventory_availability': variant.inventory_availability, 'available_threshold': variant.available_threshold, 'custom_message': variant.custom_message, 'product_template': variant.product_tmpl_id.id, 'cart_qty': variant.cart_qty, 'uom_name': variant.uom_id.name, }]) return res
def _website_price(self): qty = self._context.get('quantity', 1.0) partner = self.env.user.partner_id current_website = self.env['website'].get_current_website() pricelist = current_website.get_current_pricelist() company_id = current_website.company_id context = dict(self._context, pricelist=pricelist.id, partner=partner) self2 = self.with_context(context) if self._context != context else self ret = self.env.user.has_group('sale.group_show_price_subtotal') and 'total_excluded' or 'total_included' for p, p2 in pycompat.izip(self, self2): taxes = partner.property_account_position_id.map_tax(p.taxes_id.filtered(lambda x: x.company_id == company_id)) p.website_price = taxes.compute_all(p2.price, pricelist.currency_id, quantity=qty, product=p2, partner=partner)[ret] price_without_pricelist = taxes.compute_all(p.list_price, pricelist.currency_id)[ret] p.website_price_difference = False if float_is_zero(price_without_pricelist - p.website_price, precision_rounding=pricelist.currency_id.rounding) else True p.website_public_price = taxes.compute_all(p2.lst_price, quantity=qty, product=p2, partner=partner)[ret]
def test_spaces(self): """ Create translations where value has surrounding spaces. """ archf = '<form string="%s"><div>%s</div><div>%s</div></form>' terms_en = ('Knife', 'Fork', 'Spoon') terms_fr = (' Couteau', 'Fourchette ', ' Cuiller ') view0 = self.env['ir.ui.view'].create({ 'name': 'test', 'model': 'res.partner', 'arch': archf % terms_en, }) for src, value in list(pycompat.izip(terms_en, terms_fr)): self.env['ir.translation'].create({ 'type': 'model', 'name': 'ir.ui.view,arch_db', 'lang': 'fr_FR', 'res_id': view0.id, 'src': src, 'value': value, })
def test_copy(self): """ Create a simple view, fill in translations, and copy it. """ env_en = self.env(context={}) env_fr = self.env(context={'lang': 'fr_FR'}) archf = '<form string="%s"><div>%s</div><div>%s</div></form>' terms_en = ('Knife', 'Fork', 'Spoon') terms_fr = ('Couteau', 'Fourchette', 'Cuiller') view0 = self.env['ir.ui.view'].create({ 'name': 'test', 'model': 'res.partner', 'arch': archf % terms_en, }) for src, value in list(pycompat.izip(terms_en, terms_fr)): self.env['ir.translation'].create({ 'type': 'model', 'name': 'ir.ui.view,arch_db', 'lang': 'fr_FR', 'res_id': view0.id, 'src': src, 'value': value, }) # check translated field self.assertEqual(view0.with_env(env_en).arch_db, archf % terms_en) self.assertEqual(view0.with_env(env_fr).arch_db, archf % terms_fr) # copy without lang view1 = view0.with_env(env_en).copy({}) self.assertEqual(view1.with_env(env_en).arch_db, archf % terms_en) self.assertEqual(view1.with_env(env_fr).arch_db, archf % terms_fr) # copy with lang='fr_FR' view2 = view0.with_env(env_fr).copy({}) self.assertEqual(view2.with_env(env_en).arch_db, archf % terms_en) self.assertEqual(view2.with_env(env_fr).arch_db, archf % terms_fr) # copy with lang='fr_FR' and translate=html_translate self.patch(type(self.env['ir.ui.view']).arch_db, 'translate', html_translate) view3 = view0.with_env(env_fr).copy({}) self.assertEqual(view3.with_env(env_en).arch_db, archf % terms_en) self.assertEqual(view3.with_env(env_fr).arch_db, archf % terms_fr)
def test_create_multi(self): """ create for multiple records """ # assumption: 'res.bank' does not override 'create' vals_list = [{'name': name} for name in ('Foo', 'Bar', 'Baz')] vals_list[0]['email'] = '*****@*****.**' for vals in vals_list: record = self.env['res.bank'].create(vals) self.assertEqual(len(record), 1) self.assertEqual(record.name, vals['name']) self.assertEqual(record.email, vals.get('email', False)) records = self.env['res.bank'].create([]) self.assertFalse(records) records = self.env['res.bank'].create(vals_list) self.assertEqual(len(records), len(vals_list)) for record, vals in pycompat.izip(records, vals_list): self.assertEqual(record.name, vals['name']) self.assertEqual(record.email, vals.get('email', False)) # create countries and states vals_list = [{ 'name': 'Foo', 'state_ids': [ (0, 0, {'name': 'North Foo', 'code': 'NF'}), (0, 0, {'name': 'South Foo', 'code': 'SF'}), (0, 0, {'name': 'West Foo', 'code': 'WF'}), (0, 0, {'name': 'East Foo', 'code': 'EF'}), ], }, { 'name': 'Bar', 'state_ids': [ (0, 0, {'name': 'North Bar', 'code': 'NB'}), (0, 0, {'name': 'South Bar', 'code': 'SB'}), ], }] foo, bar = self.env['res.country'].create(vals_list) self.assertEqual(foo.name, 'Foo') self.assertCountEqual(foo.mapped('state_ids.code'), ['NF', 'SF', 'WF', 'EF']) self.assertEqual(bar.name, 'Bar') self.assertCountEqual(bar.mapped('state_ids.code'), ['NB', 'SB'])
def test_invoice_with_discount(self): """ Test invoice with a discount and check discount applied on both SO lines and an invoice lines """ # Update discount and delivered quantity on SO lines self.sol_prod_order.write({'discount': 20.0}) self.sol_serv_deliver.write({'discount': 20.0, 'qty_delivered': 4.0}) self.sol_serv_order.write({'discount': -10.0}) self.sol_prod_deliver.write({'qty_delivered': 2.0}) for line in self.sale_order.order_line.filtered(lambda l: l.discount): product_price = line.price_unit * line.product_uom_qty self.assertEquals(line.discount, (product_price - line.price_subtotal) / product_price * 100, 'Discount should be applied on order line') # lines are in draft for line in self.sale_order.order_line: self.assertTrue(float_is_zero(line.untaxed_amount_to_invoice, precision_digits=2), "The amount to invoice should be zero, as the line is in draf state") self.assertTrue(float_is_zero(line.untaxed_amount_invoiced, precision_digits=2), "The invoiced amount should be zero, as the line is in draft state") self.sale_order.action_confirm() for line in self.sale_order.order_line: self.assertTrue(float_is_zero(line.untaxed_amount_invoiced, precision_digits=2), "The invoiced amount should be zero, as the line is in draft state") self.assertEquals(self.sol_serv_order.untaxed_amount_to_invoice, 297, "The untaxed amount to invoice is wrong") self.assertEquals(self.sol_serv_deliver.untaxed_amount_to_invoice, self.sol_serv_deliver.qty_delivered * self.sol_serv_deliver.price_reduce, "The untaxed amount to invoice should be qty deli * price reduce, so 4 * (180 - 36)") self.assertEquals(self.sol_prod_deliver.untaxed_amount_to_invoice, 140, "The untaxed amount to invoice should be qty deli * price reduce, so 4 * (180 - 36)") # Let's do an invoice with invoiceable lines payment = self.env['sale.advance.payment.inv'].with_context(self.context).create({ 'advance_payment_method': 'delivered' }) payment.create_invoices() invoice = self.sale_order.invoice_ids[0] invoice.action_invoice_open() # Check discount appeared on both SO lines and invoice lines for line, inv_line in pycompat.izip(self.sale_order.order_line, invoice.invoice_line_ids): self.assertEquals(line.discount, inv_line.discount, 'Discount on lines of order and invoice should be same')
def _compute_location_description(self): for operation, operation_sudo in izip(self, self.sudo()): operation.from_loc = '%s%s' % (operation_sudo.location_id.name, operation.product_id and operation_sudo.package_id.name or '') operation.to_loc = '%s%s' % (operation_sudo.location_dest_id.name, operation_sudo.result_package_id.name or '')
def test_rounding_03(self): """ Test rounding methods with 3 digits. """ def try_round(amount, expected, digits=3, method='HALF-UP'): value = float_round(amount, precision_digits=digits, rounding_method=method) result = float_repr(value, precision_digits=digits) self.assertEqual(result, expected, 'Rounding error: got %s, expected %s' % (result, expected)) try_round(2.6745, '2.675') try_round(-2.6745, '-2.675') try_round(2.6744, '2.674') try_round(-2.6744, '-2.674') try_round(0.0004, '0.000') try_round(-0.0004, '-0.000') try_round(357.4555, '357.456') try_round(-357.4555, '-357.456') try_round(457.4554, '457.455') try_round(-457.4554, '-457.455') # Try some rounding value with rounding method UP instead of HALF-UP # We use 8.175 because when normalizing 8.175 with precision_digits=3 it gives # us 8175,0000000001234 as value, and if not handle correctly the rounding UP # value will be incorrect (should be 8,175 and not 8,176) try_round(8.175, '8.175', method='UP') try_round(8.1751, '8.176', method='UP') try_round(-8.175, '-8.175', method='UP') try_round(-8.1751, '-8.176', method='UP') try_round(-6.000, '-6.000', method='UP') try_round(1.8, '2', 0, method='UP') try_round(-1.8, '-2', 0, method='UP') # Try some rounding value with rounding method DOWN instead of HALF-UP # We use 2.425 because when normalizing 2.425 with precision_digits=3 it gives # us 2424.9999999999995 as value, and if not handle correctly the rounding DOWN # value will be incorrect (should be 2.425 and not 2.424) try_round(2.425, '2.425', method='DOWN') try_round(2.4249, '2.424', method='DOWN') try_round(-2.425, '-2.425', method='DOWN') try_round(-2.4249, '-2.424', method='DOWN') try_round(-2.500, '-2.500', method='DOWN') try_round(1.8, '1', 0, method='DOWN') try_round(-1.8, '-1', 0, method='DOWN') # Extended float range test, inspired by Cloves Almeida's test on bug #882036. fractions = [.0, .015, .01499, .675, .67499, .4555, .4555, .45555] expecteds = ['.00', '.02', '.01', '.68', '.67', '.46', '.456', '.4556'] precisions = [2, 2, 2, 2, 2, 2, 3, 4] # Note: max precision for double floats is 53 bits of precision or # 17 significant decimal digits for magnitude in range(7): for frac, exp, prec in pycompat.izip(fractions, expecteds, precisions): for sign in [-1,1]: for x in range(0, 10000, 97): n = x * 10 ** magnitude f = sign * (n + frac) f_exp = ('-' if f != 0 and sign == -1 else '') + str(n) + exp try_round(f, f_exp, digits=prec) def try_zero(amount, expected): self.assertEqual(float_is_zero(amount, precision_digits=3), expected, "Rounding error: %s should be zero!" % amount) try_zero(0.0002, True) try_zero(-0.0002, True) try_zero(0.00034, True) try_zero(0.0005, False) try_zero(-0.0005, False) try_zero(0.0008, False) try_zero(-0.0008, False) def try_compare(amount1, amount2, expected): self.assertEqual(float_compare(amount1, amount2, precision_digits=3), expected, "Rounding error, compare_amounts(%s,%s) should be %s" % (amount1, amount2, expected)) try_compare(0.0003, 0.0004, 0) try_compare(-0.0003, -0.0004, 0) try_compare(0.0002, 0.0005, -1) try_compare(-0.0002, -0.0005, 1) try_compare(0.0009, 0.0004, 1) try_compare(-0.0009, -0.0004, -1) try_compare(557.4555, 557.4556, 0) try_compare(-557.4555, -557.4556, 0) try_compare(657.4444, 657.445, -1) try_compare(-657.4444, -657.445, 1) # Rounding to unusual rounding units (e.g. coin values) def try_round(amount, expected, precision_rounding=None, method='HALF-UP'): value = float_round(amount, precision_rounding=precision_rounding, rounding_method=method) result = float_repr(value, precision_digits=2) self.assertEqual(result, expected, 'Rounding error: got %s, expected %s' % (result, expected)) try_round(-457.4554, '-457.45', precision_rounding=0.05) try_round(457.444, '457.50', precision_rounding=0.5) try_round(457.3, '455.00', precision_rounding=5) try_round(457.5, '460.00', precision_rounding=5) try_round(457.1, '456.00', precision_rounding=3) try_round(2.5, '2.50', precision_rounding=0.05, method='DOWN') try_round(-2.5, '-2.50', precision_rounding=0.05, method='DOWN')