def test_journal_sequence(self): self.assertEqual(self.test_move.name, 'MISC/2016/01/0001') self.test_move.action_post() self.assertEqual(self.test_move.name, 'MISC/2016/01/0001') copy1 = self.create_move(date=self.test_move.date) self.assertEqual(copy1.name, '/') copy1.action_post() self.assertEqual(copy1.name, 'MISC/2016/01/0002') copy2 = self.create_move(date=self.test_move.date) new_journal = self.test_move.journal_id.copy() new_journal.code = "MISC2" copy2.journal_id = new_journal self.assertEqual(copy2.name, 'MISC2/2016/01/0001') with Form(copy2) as move_form: # It is editable in the form with mute_logger('flectra.tests.common.onchange'): move_form.name = 'MyMISC/2016/0001' self.assertIn( 'The sequence will restart at 1 at the start of every year', move_form._perform_onchange(['name' ])['warning']['message'], ) move_form.journal_id = self.test_move.journal_id self.assertEqual(move_form.name, '/') move_form.journal_id = new_journal self.assertEqual(move_form.name, 'MISC2/2016/01/0001') with mute_logger('flectra.tests.common.onchange'): move_form.name = 'MyMISC/2016/0001' self.assertIn( 'The sequence will restart at 1 at the start of every year', move_form._perform_onchange(['name' ])['warning']['message'], ) copy2.action_post() self.assertEqual(copy2.name, 'MyMISC/2016/0001') copy3 = self.create_move(date=copy2.date, journal=new_journal) self.assertEqual(copy3.name, '/') with self.assertRaises(AssertionError): with Form(copy2) as move_form: # It is not editable in the form move_form.name = 'MyMISC/2016/0002' copy3.action_post() self.assertEqual(copy3.name, 'MyMISC/2016/0002') copy3.name = 'MISC2/2016/00002' copy4 = self.create_move(date=copy2.date, journal=new_journal) copy4.action_post() self.assertEqual(copy4.name, 'MISC2/2016/00003') copy5 = self.create_move(date=copy2.date, journal=new_journal) copy5.date = '2021-02-02' copy5.action_post() self.assertEqual(copy5.name, 'MISC2/2021/00001') copy5.name = 'N\'importe quoi?' copy6 = self.create_move(date=copy5.date, journal=new_journal) copy6.action_post() self.assertEqual(copy6.name, 'N\'importe quoi?1')
def unlink(self): """Override to: - Clean up the variants that use any of the values in self: - Remove the value from the variant if the value belonged to an attribute line with only one value. - Unlink or archive all related variants. - Archive the value if unlink is not possible. Archiving is typically needed when the value is referenced elsewhere (on a variant that can't be deleted, on a sales order line, ...). """ # Directly remove the values from the variants for lines that had single # value (counting also the values that are archived). single_values = self.filtered(lambda ptav: len( ptav.attribute_line_id.product_template_value_ids) == 1) for ptav in single_values: ptav.ptav_product_variant_ids.write( {'product_template_attribute_value_ids': [(3, ptav.id, 0)]}) # Try to remove the variants before deleting to potentially remove some # blocking references. self.ptav_product_variant_ids._unlink_or_archive() # Now delete or archive the values. ptav_to_archive = self.env['product.template.attribute.value'] for ptav in self: try: with self.env.cr.savepoint(), tools.mute_logger( 'flectra.sql_db'): super(ProductTemplateAttributeValue, ptav).unlink() except Exception: # We catch all kind of exceptions to be sure that the operation # doesn't fail. ptav_to_archive += ptav ptav_to_archive.write({'ptav_active': False}) return True
def test_105_duplicated_translation(self): """ Test synchronizing translations with duplicated source """ # create a category with a French translation padawans = self.env['res.partner.category'].create({'name': 'Padawan'}) self.env['ir.translation'].create({ 'type': 'model', 'name': 'res.partner.category,name', 'module':'base', 'lang': 'fr_FR', 'res_id': padawans.id, 'value': 'Apprenti', 'state': 'translated', }) # change name and insert a duplicate manually padawans.write({'name': 'Padawans'}) with self.assertRaises(IntegrityError), mute_logger('flectra.sql_db'): with self.env.cr.savepoint(): self.env['ir.translation'].create({ 'type': 'model', 'name': 'res.partner.category,name', 'module':'base', 'lang': 'fr_FR', 'res_id': padawans.id, 'value': 'Apprentis', 'state': 'translated', }) self.env['ir.translation'].translate_fields('res.partner.category', padawans.id, 'name') translations = self.env['ir.translation'].search([ ('res_id', '=', padawans.id), ('name', '=', 'res.partner.category,name'), ('lang', '=', 'fr_FR'), ]) self.assertEqual(len(translations), 1, "Translations were not duplicated after `translate_fields` call") self.assertEqual(translations.value, "Apprenti", "The first translation must stay")
def test_events_access_8(self): """Access to an unpublished event with portal user.""" with mute_logger('flectra.addons.http_routing.models.ir_http'): self.authenticate('user_portal', 'user_portal') unpublished_events = self.events.filtered(lambda event: not event.website_published) resp = self.url_open('/event/%i' % unpublished_events[0].id) self.assertEqual(resp.status_code, 403, 'Portal user must not have access to unpublished event.')
def test_80_permission(self): self.action.write({ 'state': 'code', 'code': """record.write({'date': datetime.date.today()})""", }) user_demo = self.env.ref("base.user_demo") self_demo = self.action.with_user(user_demo.id) # can write on contact partner self.test_partner.type = "contact" self.test_partner.with_user(user_demo.id).check_access_rule("write") self_demo.with_context(self.context).run() self.assertEqual(self.test_partner.date, date.today()) # but can not write on private address self.test_partner.type = "private" with self.assertRaises(AccessError): self.test_partner.with_user( user_demo.id).check_access_rule("write") # nor execute a server action on it with self.assertRaises(AccessError), mute_logger( 'flectra.addons.base.models.ir_actions'): self_demo.with_context(self.context).run()
def test_change_login(self): new_test_user(self.env, login='******', website_id=self.website_1.id) user_belle = new_test_user(self.env, login='******', website_id=self.website_1.id) with self.assertRaises(IntegrityError), mute_logger('flectra.sql_db'): user_belle.login = '******'
def update(self, inactivity_period): """ Updates the last_poll and last_presence of the current user :param inactivity_period: duration in milliseconds """ presence = self.search([('user_id', '=', self._uid)], limit=1) # compute last_presence timestamp last_presence = datetime.datetime.now() - datetime.timedelta( milliseconds=inactivity_period) values = { 'last_poll': time.strftime(DEFAULT_SERVER_DATETIME_FORMAT), } # update the presence or a create a new one if not presence: # create a new presence for the user values['user_id'] = self._uid values['last_presence'] = last_presence.strftime( DEFAULT_SERVER_DATETIME_FORMAT) self.create(values) else: # update the last_presence if necessary, and write values if datetime.datetime.strptime( presence.last_presence, DEFAULT_SERVER_DATETIME_FORMAT) < last_presence: values['last_presence'] = last_presence.strftime( DEFAULT_SERVER_DATETIME_FORMAT) # Hide transaction serialization errors, which can be ignored, the presence update is not essential with tools.mute_logger('flectra.sql_db'): presence.write(values) # avoid TransactionRollbackError self.env.cr.commit() # TODO : check if still necessary
def unlink(self): """Override to: - Archive the line if unlink is not possible. - Clean up related values and related variants. Archiving is typically needed when the line has values that can't be deleted because they are referenced elsewhere (on a variant that can't be deleted, on a sales order line, ...). """ # Try to remove the values first to remove some potentially blocking # references, which typically works: # - For single value lines because the values are directly removed from # the variants. # - For values that are present on variants that can be deleted. self.product_template_value_ids._only_active().unlink() # Keep a reference to the related templates before the deletion. templates = self.product_tmpl_id # Now delete or archive the lines. ptal_to_archive = self.env['product.template.attribute.line'] for ptal in self: try: with self.env.cr.savepoint(), tools.mute_logger( 'flectra.sql_db'): super(ProductTemplateAttributeLine, ptal).unlink() except Exception: # We catch all kind of exceptions to be sure that the operation # doesn't fail. ptal_to_archive += ptal ptal_to_archive.write({'active': False}) # For archived lines `_update_product_template_attribute_values` is # implicitly called during the `write` above, but for products that used # unlinked lines `_create_variant_ids` has to be called manually. (templates - ptal_to_archive.product_tmpl_id)._create_variant_ids() return True
def _procure_calculation_orderpoint(self): with api.Environment.manage(): # As this function is in a new thread, I need to open a new cursor, because the old one may be closed new_cr = self.pool.cursor() self = self.with_env(self.env(cr=new_cr)) scheduler_cron = self.sudo().env.ref( 'stock.ir_cron_scheduler_action') # Avoid to run the scheduler multiple times in the same time try: with tools.mute_logger('flectra.sql_db'): self._cr.execute( "SELECT id FROM ir_cron WHERE id = %s FOR UPDATE NOWAIT", (scheduler_cron.id, )) except Exception: _logger.info( 'Attempt to run procurement scheduler aborted, as already running' ) self._cr.rollback() self._cr.close() return {} for company in self.env.user.company_ids: self.env['procurement.group'].run_scheduler( use_new_cursor=self._cr.dbname, company_id=company.id) new_cr.close() return {}
def test_sequence_concurency(self): """Computing the same name in concurent transactions is not allowed.""" with self.env.registry.cursor() as cr0,\ self.env.registry.cursor() as cr1,\ self.env.registry.cursor() as cr2: env0 = api.Environment(cr0, SUPERUSER_ID, {}) env1 = api.Environment(cr1, SUPERUSER_ID, {}) env2 = api.Environment(cr2, SUPERUSER_ID, {}) journal = env0['account.journal'].create({ 'name': 'concurency_test', 'code': 'CT', 'type': 'general', }) account = env0['account.account'].create({ 'code': 'CT', 'name': 'CT', 'user_type_id': env0.ref('account.data_account_type_fixed_assets').id, }) moves = env0['account.move'].create([{ 'journal_id': journal.id, 'date': fields.Date.from_string('2016-01-01'), 'line_ids': [(0, 0, { 'name': 'name', 'account_id': account.id })] }] * 3) moves.name = '/' moves[0].action_post() self.assertEqual(moves.mapped('name'), ['CT/2016/01/0001', '/', '/']) env0.cr.commit() # start the transactions here on cr2 to simulate concurency with cr1 env2.cr.execute('SELECT 1') move = env1['account.move'].browse(moves[1].id) move.action_post() env1.cr.commit() move = env2['account.move'].browse(moves[2].id) with self.assertRaises( psycopg2.OperationalError), env2.cr.savepoint( ), mute_logger('flectra.sql_db'): move.action_post() self.assertEqual(moves.mapped('name'), ['CT/2016/01/0001', 'CT/2016/01/0002', '/']) moves.button_draft() moves.posted_before = False moves.unlink() journal.unlink() account.unlink() env0.cr.commit()
def create_variant_ids(self): Product = self.env["product.product"] AttributeValues = self.env['product.attribute.value'] for tmpl_id in self.with_context(active_test=False): # adding an attribute with only one value should not recreate product # write this attribute on every product to make sure we don't lose them variant_alone = tmpl_id.attribute_line_ids.filtered(lambda line: len(line.value_ids) == 1).mapped('value_ids') for value_id in variant_alone: updated_products = tmpl_id.product_variant_ids.filtered(lambda product: value_id.attribute_id not in product.mapped('attribute_value_ids.attribute_id')) updated_products.write({'attribute_value_ids': [(4, value_id.id)],'default_code': tmpl_id.gt_product_id,'gt_shopify_product':tmpl_id.gt_shopify_product,'gt_shopify_instance_id':tmpl_id.gt_shopify_instance_id.id}) # iterator of n-uple of product.attribute.value *ids* variant_matrix = [ AttributeValues.browse(value_ids) for value_ids in itertools.product(*(line.value_ids.ids for line in tmpl_id.attribute_line_ids if line.value_ids[:1].attribute_id.create_variant)) ] # get the value (id) sets of existing variants existing_variants = {frozenset(variant.attribute_value_ids.ids) for variant in tmpl_id.product_variant_ids} # -> for each value set, create a recordset of values to create a # variant for if the value set isn't already a variant to_create_variants = [ value_ids for value_ids in variant_matrix if set(value_ids.ids) not in existing_variants ] # check product variants_to_activate = self.env['product.product'] variants_to_unlink = self.env['product.product'] for product_id in tmpl_id.product_variant_ids: if not product_id.active and product_id.attribute_value_ids.filtered(lambda r: r.attribute_id.create_variant) in variant_matrix: variants_to_activate |= product_id elif product_id.attribute_value_ids.filtered(lambda r: r.attribute_id.create_variant) not in variant_matrix: variants_to_unlink |= product_id if variants_to_activate: variants_to_activate.write({'active': True}) # create new product for variant_ids in to_create_variants: new_variant = Product.create({ 'product_tmpl_id': tmpl_id.id, 'attribute_value_ids': [(6, 0, variant_ids.ids)], 'default_code': tmpl_id.gt_product_id, 'gt_shopify_product':tmpl_id.gt_shopify_product, 'gt_shopify_instance_id':tmpl_id.gt_shopify_instance_id.id, }) # unlink or inactive product for variant in variants_to_unlink: try: with self._cr.savepoint(), tools.mute_logger('flectra.sql_db'): variant.unlink() # We catch all kind of exception to be sure that the operation doesn't fail. except (psycopg2.Error, except_orm): variant.write({'active': False}) pass return True
def test_00_dropship(self): # Create a vendor supplier_dropship = self.env['res.partner'].create({'name': 'Vendor of Dropshipping test'}) # Create new product without any routes drop_shop_product = self.env['product.product'].create({ 'name': "Pen drive", 'type': "product", 'categ_id': self.env.ref('product.product_category_1').id, 'lst_price': 100.0, 'standard_price': 0.0, 'uom_id': self.env.ref('uom.product_uom_unit').id, 'uom_po_id': self.env.ref('uom.product_uom_unit').id, 'seller_ids': [(0, 0, { 'delay': 1, 'name': supplier_dropship.id, 'min_qty': 2.0 })] }) # Create a sales order with a line of 200 PCE incoming shipment, with route_id drop shipping so_form = Form(self.env['sale.order']) so_form.partner_id = self.env['res.partner'].create({'name': 'My Test Partner'}) so_form.payment_term_id = self.env.ref('account.account_payment_term_end_following_month') with mute_logger('flectra.tests.common.onchange'): # otherwise complains that there's not enough inventory and # apparently that's normal according to @jco and @sle with so_form.order_line.new() as line: line.product_id = drop_shop_product line.product_uom_qty = 200 line.price_unit = 1.00 line.route_id = self.env.ref('stock_dropshipping.route_drop_shipping') sale_order_drp_shpng = so_form.save() # Confirm sales order sale_order_drp_shpng.action_confirm() # Check the sales order created a procurement group which has a procurement of 200 pieces self.assertTrue(sale_order_drp_shpng.procurement_group_id, 'SO should have procurement group') # Check a quotation was created to a certain vendor and confirm so it becomes a confirmed purchase order purchase = self.env['purchase.order'].search([('partner_id', '=', supplier_dropship.id)]) self.assertTrue(purchase, "an RFQ should have been created by the scheduler") purchase.button_confirm() self.assertEqual(purchase.state, 'purchase', 'Purchase order should be in the approved state') self.assertEqual(len(purchase.ids), 1, 'There should be one picking') # Send the 200 pieces purchase.picking_ids.move_lines.quantity_done = purchase.picking_ids.move_lines.product_qty purchase.picking_ids.button_validate() # Check one move line was created in Customers location with 200 pieces move_line = self.env['stock.move.line'].search([ ('location_dest_id', '=', self.env.ref('stock.stock_location_customers').id), ('product_id', '=', drop_shop_product.id)]) self.assertEqual(len(move_line.ids), 1, 'There should be exactly one move line')
def test_sale_transaction_mismatch(self): """Test that a transaction for the incorrect amount does not validate the SO.""" # modify order total self.order.order_line[0].price_unit = 200.0 self.transaction._set_transaction_done() with mute_logger('flectra.addons.sale.models.payment'): self.transaction._post_process_after_done() self.assertEqual( self.order.state, 'draft', 'a transaction for an incorrect amount should not validate a quote' )
def test_remove_badge_with_give_badge(self): self.certification_survey.write({ 'certification_give_badge': True, 'certification_badge_id': self.certification_badge.id }) with mute_logger('flectra.sql_db'): with self.assertRaises(IntegrityError): self.certification_survey.write( {'certification_badge_id': None}) self.certification_survey.flush(['certification_badge_id'])
def test_same_website_message(self): @check # Check decorator, otherwise translation is not applied def check_new_test_user(dbname): new_test_user(self.env(context={'land': 'en_US'}), login='******', website_id=self.website_1.id) new_test_user(self.env, login='******', website_id=self.website_1.id) # Should be a ValidationError (with a nice translated error message), # not an IntegrityError with self.assertRaises(ValidationError), mute_logger('flectra.sql_db'): check_new_test_user(self.env.registry._db.dbname)
def update_records(model, src, field_model='model', field_id='res_id'): Model = self.env[model] if model in self.env else None if Model is None: return records = Model.sudo().search([(field_model, '=', 'res.partner'), (field_id, '=', src.id)]) try: with mute_logger('flectra.sql_db'), self._cr.savepoint(): return records.sudo().write({field_id: dst_partner.id}) except psycopg2.Error: # updating fails, most likely due to a violated unique constraint # keeping record with nonexistent partner_id is useless, better delete it return records.sudo().unlink()
def test_unlink(self): date_range = self.env['date.range'] drt = self.env['date.range.type'].create({ 'name': 'Fiscal year', 'allow_overlap': False }) date_range.create({ 'name': 'FS2016', 'date_start': '2015-01-01', 'date_end': '2016-12-31', 'type_id': drt.id, }) with self.assertRaises(IntegrityError), mute_logger('flectra.sql_db'): drt.unlink()
def send_email(self, message, *args, **kwargs): with this.registry.cursor() as cr, mute_logger('flectra.sql_db'): try: # try ro aquire lock (no wait) on notification (should fail) cr.execute("SELECT notification_status FROM mail_message_res_partner_needaction_rel WHERE id = %s FOR UPDATE NOWAIT", [notif.id]) except psycopg2.OperationalError: # record already locked by send, all good bounce_deferred.append(True) else: # this should trigger psycopg2.extensions.TransactionRollbackError in send(). # Only here to simulate the initial use case # If the record is lock, this line would create a deadlock since we are in the same thread # In practice, the update will wait the end of the send() transaction and set the notif as bounce, as expeced cr.execute("UPDATE mail_message_res_partner_needaction_rel SET notification_status='bounce' WHERE id = %s", [notif.id]) return message['Message-Id']
def test_sale_transaction_partial_delivery(self): """Test that with automatic invoice and invoicing policy based on delivered quantity, a transaction for the partial amount does not validate the SO.""" # set automatic invoice self.env['ir.config_parameter'].sudo().set_param( 'sale.automatic_invoice', 'True') # modify order total self.order.order_line[0].price_unit = 200.0 # invoicing policy is based on delivered quantity self.product_a.invoice_policy = 'delivery' self.transaction._set_transaction_done() with mute_logger('flectra.addons.sale.models.payment'): self.transaction.sudo()._post_process_after_done() self.assertEqual( self.order.state, 'draft', 'a partial transaction with automatic invoice and invoice_policy = delivery should not validate a quote' )
def test_set_same_badge_on_multiple_survey(self): self.certification_survey.write({ 'certification_give_badge': True, 'certification_badge_id': self.certification_badge.id }) # set the same badge on another survey should fail: with mute_logger('flectra.sql_db'): with self.assertRaises(IntegrityError): self.certification_survey_2.write({ 'certification_give_badge': True, 'certification_badge_id': self.certification_badge.id }) self.certification_survey.flush()
def test_constraints(self): """The goal of this test is to make sure constraints are correct.""" with self.assertRaises(UserError, msg="can't change variants creation mode of attribute used on product"): self.ram_attribute.create_variant = 'no_variant' with self.assertRaises(UserError, msg="can't delete attribute used on product"): self.ram_attribute.unlink() with self.assertRaises(UserError, msg="can't change the attribute of an value used on product"): self.ram_32.attribute_id = self.hdd_attribute.id with self.assertRaises(UserError, msg="can't delete value used on product"): self.ram_32.unlink() with self.assertRaises(ValidationError, msg="can't have attribute without value on product"): self.env['product.template.attribute.line'].create({ 'product_tmpl_id': self.computer_case.id, 'attribute_id': self.hdd_attribute.id, 'value_ids': [(6, 0, [])], }) with self.assertRaises(ValidationError, msg="value attribute must match line attribute"): self.env['product.template.attribute.line'].create({ 'product_tmpl_id': self.computer_case.id, 'attribute_id': self.ram_attribute.id, 'value_ids': [(6, 0, [self.ssd_256.id])], }) with self.assertRaises(UserError, msg="can't change the attribute of an attribute line"): self.computer_ssd_attribute_lines.attribute_id = self.hdd_attribute.id with self.assertRaises(UserError, msg="can't change the product of an attribute line"): self.computer_ssd_attribute_lines.product_tmpl_id = self.computer_case.id with self.assertRaises(UserError, msg="can't change the value of a product template attribute value"): self.computer_ram_attribute_lines.product_template_value_ids[0].product_attribute_value_id = self.hdd_1 with self.assertRaises(UserError, msg="can't change the product of a product template attribute value"): self.computer_ram_attribute_lines.product_template_value_ids[0].product_tmpl_id = self.computer_case.id with mute_logger('flectra.sql_db'), self.assertRaises(IntegrityError, msg="can't have two values with the same name for the same attribute"): self.env['product.attribute.value'].create({ 'name': '32 GB', 'attribute_id': self.ram_attribute.id, })
def test_stock_orderpoint_wrong_uom(self): with mute_logger('flectra.sql_db'): with self.assertRaises(ValidationError): self.env['stock.warehouse.orderpoint'].create({ 'warehouse_id': self.warehouse.id, 'location_id': self.location_stock.id, 'product_id': self.productA.id, 'product_max_qty': 24, 'product_min_qty': 12, 'procure_uom_id': self.uom_kg.id, })
def test_remove_badge_with_give_badge_multi(self): self.certification_survey.write({ 'certification_give_badge': True, 'certification_badge_id': self.certification_badge.id }) self.certification_survey_2.write({ 'certification_give_badge': True, 'certification_badge_id': self.certification_badge_2.id }) surveys = self.env['survey.survey'].browse( [self.certification_survey.id, self.certification_survey_2.id]) with mute_logger('flectra.sql_db'): with self.assertRaises(IntegrityError): surveys.write({'certification_badge_id': None}) surveys.flush(['certification_badge_id'])
def _update(self, inactivity_period): presence = self.search([('user_id', '=', self._uid)], limit=1) # compute last_presence timestamp last_presence = datetime.datetime.now() - datetime.timedelta( milliseconds=inactivity_period) values = { 'last_poll': time.strftime(DEFAULT_SERVER_DATETIME_FORMAT), } # update the presence or a create a new one if not presence: # create a new presence for the user values['user_id'] = self._uid values['last_presence'] = last_presence self.create(values) else: # update the last_presence if necessary, and write values if presence.last_presence < last_presence: values['last_presence'] = last_presence # Hide transaction serialization errors, which can be ignored, the presence update is not essential with tools.mute_logger('flectra.sql_db'): presence.write(values) presence.flush()
def _unlink_or_archive(self, check_access=True): """Unlink or archive products. Try in batch as much as possible because it is much faster. Use dichotomy when an exception occurs. """ # Avoid access errors in case the products is shared amongst companies # but the underlying objects are not. If unlink fails because of an # AccessError (e.g. while recomputing fields), the 'write' call will # fail as well for the same reason since the field has been set to # recompute. if check_access: self.check_access_rights('unlink') self.check_access_rule('unlink') self.check_access_rights('write') self.check_access_rule('write') self = self.sudo() to_unlink = self._filter_to_unlink() to_archive = self - to_unlink to_archive.write({'active': False}) self = to_unlink try: with self.env.cr.savepoint(), tools.mute_logger('flectra.sql_db'): self.unlink() except Exception: # We catch all kind of exceptions to be sure that the operation # doesn't fail. if len(self) > 1: self[:len(self) // 2]._unlink_or_archive(check_access=False) self[len(self) // 2:]._unlink_or_archive(check_access=False) else: if self.active: # Note: this can still fail if something is preventing # from archiving. # This is the case from existing stock reordering rules. self.write({'active': False})
def test_crud_rights(self): Post = self.env['forum.post'] Vote = self.env['forum.post.vote'] self.user_portal.karma = 500 self.user_employee.karma = 500 # create some posts self.admin_post = self.post self.portal_post = Post.with_user(self.user_portal).create({ 'name': 'Post from Portal User', 'content': 'I am not a bird.', 'forum_id': self.forum.id, }) self.employee_post = Post.with_user(self.user_employee).create({ 'name': 'Post from Employee User', 'content': 'I am not a bird.', 'forum_id': self.forum.id, }) # vote on some posts self.employee_vote_on_admin_post = Vote.with_user( self.user_employee).create({ 'post_id': self.admin_post.id, 'vote': '1', }) self.portal_vote_on_admin_post = Vote.with_user( self.user_portal).create({ 'post_id': self.admin_post.id, 'vote': '1', }) self.admin_vote_on_portal_post = Vote.create({ 'post_id': self.portal_post.id, 'vote': '1', }) self.admin_vote_on_employee_post = Vote.create({ 'post_id': self.employee_post.id, 'vote': '1', }) # One should not be able to modify someone else's vote with self.assertRaises(UserError): self.admin_vote_on_portal_post.with_user(self.user_employee).write( { 'vote': '-1', }) with self.assertRaises(UserError): self.admin_vote_on_employee_post.with_user(self.user_portal).write( { 'vote': '-1', }) # One should not be able to give his vote to someone else self.employee_vote_on_admin_post.with_user(self.user_employee).write({ 'user_id': 1, }) self.assertEqual( self.employee_vote_on_admin_post.user_id, self.user_employee, 'User employee should not be able to give its vote ownership to someone else' ) # One should not be able to change his vote's post to a post of his own (would be self voting) with self.assertRaises(UserError): self.employee_vote_on_admin_post.with_user( self.user_employee).write({ 'post_id': self.employee_post.id, }) # One should not be able to give his vote to someone else self.portal_vote_on_admin_post.with_user(self.user_portal).write({ 'user_id': 1, }) self.assertEqual( self.portal_vote_on_admin_post.user_id, self.user_portal, 'User portal should not be able to give its vote ownership to someone else' ) # One should not be able to change his vote's post to a post of his own (would be self voting) with self.assertRaises(UserError): self.portal_vote_on_admin_post.with_user(self.user_portal).write({ 'post_id': self.portal_post.id, }) # One should not be able to vote for its own post with self.assertRaises(UserError): Vote.with_user(self.user_employee).create({ 'post_id': self.employee_post.id, 'vote': '1', }) # One should not be able to vote for its own post with self.assertRaises(UserError): Vote.with_user(self.user_portal).create({ 'post_id': self.portal_post.id, 'vote': '1', }) with mute_logger('flectra.sql_db'): with self.assertRaises(IntegrityError): with self.cr.savepoint(): # One should not be able to vote more than once on a same post Vote.with_user(self.user_employee).create({ 'post_id': self.admin_post.id, 'vote': '1', }) with self.assertRaises(IntegrityError): with self.cr.savepoint(): # One should not be able to vote more than once on a same post Vote.with_user(self.user_employee).create({ 'post_id': self.admin_post.id, 'vote': '1', }) # One should not be able to create a vote for someone else new_employee_vote = Vote.with_user(self.user_employee).create({ 'post_id': self.portal_post.id, 'user_id': 1, 'vote': '1', }) self.assertEqual( new_employee_vote.user_id, self.user_employee, 'Creating a vote for someone else should not be allowed. It should create it for yourself instead' ) # One should not be able to create a vote for someone else new_portal_vote = Vote.with_user(self.user_portal).create({ 'post_id': self.employee_post.id, 'user_id': 1, 'vote': '1', }) self.assertEqual( new_portal_vote.user_id, self.user_portal, 'Creating a vote for someone else should not be allowed. It should create it for yourself instead' )
def test_create_from_ui(self): """ Simulation of sales coming from the interface, even after closing the session """ # I click on create a new session button self.pos_config.open_session_cb(check_coa=False) current_session = self.pos_config.current_session_id num_starting_orders = len(current_session.order_ids) untax, atax = self.compute_tax(self.led_lamp, 0.9) carrot_order = { 'data': { 'amount_paid': untax + atax, 'amount_return': 0, 'amount_tax': atax, 'amount_total': untax + atax, 'creation_date': fields.Datetime.to_string(fields.Datetime.now()), 'fiscal_position_id': False, 'pricelist_id': self.pos_config.available_pricelist_ids[0].id, 'lines': [[ 0, 0, { 'discount': 0, 'id': 42, 'pack_lot_ids': [], 'price_unit': 0.9, 'product_id': self.led_lamp.id, 'price_subtotal': 0.9, 'price_subtotal_incl': 1.04, 'qty': 1, 'tax_ids': [(6, 0, self.led_lamp.taxes_id.ids)] } ]], 'name': 'Order 00042-003-0014', 'partner_id': False, 'pos_session_id': current_session.id, 'sequence_number': 2, 'statement_ids': [[ 0, 0, { 'amount': untax + atax, 'name': fields.Datetime.now(), 'payment_method_id': self.cash_payment_method.id } ]], 'uid': '00042-003-0014', 'user_id': self.env.uid }, 'id': '00042-003-0014', 'to_invoice': False } untax, atax = self.compute_tax(self.whiteboard_pen, 1.2) zucchini_order = { 'data': { 'amount_paid': untax + atax, 'amount_return': 0, 'amount_tax': atax, 'amount_total': untax + atax, 'creation_date': fields.Datetime.to_string(fields.Datetime.now()), 'fiscal_position_id': False, 'pricelist_id': self.pos_config.available_pricelist_ids[0].id, 'lines': [[ 0, 0, { 'discount': 0, 'id': 3, 'pack_lot_ids': [], 'price_unit': 1.2, 'product_id': self.whiteboard_pen.id, 'price_subtotal': 1.2, 'price_subtotal_incl': 1.38, 'qty': 1, 'tax_ids': [(6, 0, self.whiteboard_pen.taxes_id.ids)] } ]], 'name': 'Order 00043-003-0014', 'partner_id': False, 'pos_session_id': current_session.id, 'sequence_number': self.pos_config.journal_id.id, 'statement_ids': [[ 0, 0, { 'amount': untax + atax, 'name': fields.Datetime.now(), 'payment_method_id': self.credit_payment_method.id } ]], 'uid': '00043-003-0014', 'user_id': self.env.uid }, 'id': '00043-003-0014', 'to_invoice': False } untax, atax = self.compute_tax(self.newspaper_rack, 1.28) newspaper_rack_order = { 'data': { 'amount_paid': untax + atax, 'amount_return': 0, 'amount_tax': atax, 'amount_total': untax + atax, 'creation_date': fields.Datetime.to_string(fields.Datetime.now()), 'fiscal_position_id': False, 'pricelist_id': self.pos_config.available_pricelist_ids[0].id, 'lines': [[ 0, 0, { 'discount': 0, 'id': 3, 'pack_lot_ids': [], 'price_unit': 1.28, 'product_id': self.newspaper_rack.id, 'price_subtotal': 1.28, 'price_subtotal_incl': 1.47, 'qty': 1, 'tax_ids': [[6, False, self.newspaper_rack.taxes_id.ids]] } ]], 'name': 'Order 00044-003-0014', 'partner_id': False, 'pos_session_id': current_session.id, 'sequence_number': self.pos_config.journal_id.id, 'statement_ids': [[ 0, 0, { 'amount': untax + atax, 'name': fields.Datetime.now(), 'payment_method_id': self.bank_payment_method.id } ]], 'uid': '00044-003-0014', 'user_id': self.env.uid }, 'id': '00044-003-0014', 'to_invoice': False } # I create an order on an open session self.PosOrder.create_from_ui([carrot_order]) self.assertEqual(num_starting_orders + 1, len(current_session.order_ids), "Submitted order not encoded") # I close the session current_session.action_pos_session_closing_control() self.assertEqual(current_session.state, 'closed', "Session was not properly closed") self.assertFalse(self.pos_config.current_session_id, "Current session not properly recomputed") # I keep selling after the session is closed with mute_logger('flectra.addons.point_of_sale.models.pos_order'): self.PosOrder.create_from_ui( [zucchini_order, newspaper_rack_order]) rescue_session = self.PosSession.search([('config_id', '=', self.pos_config.id), ('state', '=', 'opened'), ('rescue', '=', True)]) self.assertEqual( len(rescue_session), 1, "One (and only one) rescue session should be created for orphan orders" ) self.assertIn("(RESCUE FOR %s)" % current_session.name, rescue_session.name, "Rescue session is not linked to the previous one") self.assertEqual(len(rescue_session.order_ids), 2, "Rescue session does not contain both orders") # I close the rescue session rescue_session.action_pos_session_closing_control() self.assertEqual(rescue_session.state, 'closed', "Rescue session was not properly closed")
def test_sale_mrp(self): self.env.ref('stock.route_warehouse0_mto').active = True warehouse0 = self.env.ref('stock.warehouse0') # In order to test the sale_mrp module in OpenERP, I start by creating a new product 'Slider Mobile' # I define product category Mobile Products Sellable. with mute_logger('flectra.tests.common.onchange'): # Suppress warning on "Changing your cost method" when creating a # product category pc = Form(self.env['product.category']) pc.name = 'Mobile Products Sellable' product_category_allproductssellable0 = pc.save() uom_unit = self.env.ref('uom.product_uom_unit') self.assertIn("seller_ids", self.env['product.template'].fields_get()) # I define product for Slider Mobile. product = Form(self.env['product.template']) product.categ_id = product_category_allproductssellable0 product.list_price = 200.0 product.name = 'Slider Mobile' product.type = 'product' product.uom_id = uom_unit product.uom_po_id = uom_unit product.route_ids.clear() product.route_ids.add(warehouse0.manufacture_pull_id.route_id) product.route_ids.add(warehouse0.mto_pull_id.route_id) product_template_slidermobile0 = product.save() product_template_slidermobile0.standard_price = 189 product_component = Form(self.env['product.product']) product_component.name = 'Battery' product_product_bettery = product_component.save() with Form(self.env['mrp.bom']) as bom: bom.product_tmpl_id = product_template_slidermobile0 with bom.bom_line_ids.new() as line: line.product_id = product_product_bettery line.product_qty = 4 # I create a sale order for product Slider mobile so_form = Form(self.env['sale.order']) so_form.partner_id = self.env['res.partner'].create( {'name': 'Another Test Partner'}) with so_form.order_line.new() as line: line.product_id = product_template_slidermobile0.product_variant_ids line.price_unit = 200 line.product_uom_qty = 500.0 line.customer_lead = 7.0 sale_order_so0 = so_form.save() # I confirm the sale order sale_order_so0.action_confirm() # I verify that a manufacturing order has been generated, and that its name and reference are correct mo = self.env['mrp.production'].search( [('origin', 'like', sale_order_so0.name)], limit=1) self.assertTrue(mo, 'Manufacturing order has not been generated')
def _update_foreign_keys(self, src_partners, dst_partner): """ Update all foreign key from the src_partner to dst_partner. All many2one fields will be updated. :param src_partners : merge source res.partner recordset (does not include destination one) :param dst_partner : record of destination res.partner """ _logger.debug( '_update_foreign_keys for dst_partner: %s for src_partners: %s', dst_partner.id, str(src_partners.ids)) # find the many2one relation to a partner Partner = self.env['res.partner'] relations = self._get_fk_on('res_partner') self.flush() for table, column in relations: if 'base_partner_merge_' in table: # ignore two tables continue # get list of columns of current table (exept the current fk column) query = "SELECT column_name FROM information_schema.columns WHERE table_name LIKE '%s'" % ( table) self._cr.execute(query, ()) columns = [] for data in self._cr.fetchall(): if data[0] != column: columns.append(data[0]) # do the update for the current table/column in SQL query_dic = { 'table': table, 'column': column, 'value': columns[0], } if len(columns) <= 1: # unique key treated query = """ UPDATE "%(table)s" as ___tu SET "%(column)s" = %%s WHERE "%(column)s" = %%s AND NOT EXISTS ( SELECT 1 FROM "%(table)s" as ___tw WHERE "%(column)s" = %%s AND ___tu.%(value)s = ___tw.%(value)s )""" % query_dic for partner in src_partners: self._cr.execute( query, (dst_partner.id, partner.id, dst_partner.id)) else: try: with mute_logger('flectra.sql_db'), self._cr.savepoint(): query = 'UPDATE "%(table)s" SET "%(column)s" = %%s WHERE "%(column)s" IN %%s' % query_dic self._cr.execute(query, ( dst_partner.id, tuple(src_partners.ids), )) # handle the recursivity with parent relation if column == Partner._parent_name and table == 'res_partner': query = """ WITH RECURSIVE cycle(id, parent_id) AS ( SELECT id, parent_id FROM res_partner UNION SELECT cycle.id, res_partner.parent_id FROM res_partner, cycle WHERE res_partner.id = cycle.parent_id AND cycle.id != cycle.parent_id ) SELECT id FROM cycle WHERE id = parent_id AND id = %s """ self._cr.execute(query, (dst_partner.id, )) # NOTE JEM : shouldn't we fetch the data ? except psycopg2.Error: # updating fails, most likely due to a violated unique constraint # keeping record with nonexistent partner_id is useless, better delete it query = 'DELETE FROM "%(table)s" WHERE "%(column)s" IN %%s' % query_dic self._cr.execute(query, (tuple(src_partners.ids), )) self.invalidate_cache()
def test_rename_unique(self): """ one cannot create two fields with the same name on a given model """ field1 = self.create_field('x_foo') field2 = self.create_field('x_bar') with self.assertRaises(IntegrityError), mute_logger('flectra.sql_db'): field2.name = field1.name