示例#1
0
 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('odoo.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')
     ])
     self.assertEqual(len(translations), 1, "Translations were not duplicated after `translate_fields` call")
     self.assertEqual(translations.value, "Apprenti", "The first translation must stay")
示例#2
0
    def test_proc_rule(self):
        # Create a product route containing a procurement rule that will
        # generate a move from Stock for every procurement created in Output
        product_route = self.env['stock.location.route'].create({
            'name': 'Stock -> output route',
            'product_selectable': True,
            'pull_ids': [(0, 0, {
                'name': 'Stock -> output rule',
                'action': 'move',
                'picking_type_id': self.ref('stock.picking_type_internal'),
                'location_src_id': self.ref('stock.stock_location_stock'),
                'location_id': self.ref('stock.stock_location_output'),
            })],
        })

        # Set this route on `product.product_product_3`
        self.env.ref('product.product_product_3').write({
            'route_ids': [(4, product_route.id)]})

        # Create Delivery Order of 10 `product.product_product_3` from Output -> Customer
        default_get_vals = self.env['stock.picking'].default_get(list(self.env['stock.picking'].fields_get()))
        default_get_vals.update({
            'name': 'Delivery order for procurement',
            'partner_id': self.ref('base.res_partner_2'),
            'picking_type_id': self.ref('stock.picking_type_out'),
            'move_lines': [(0, 0, {
                'product_id': self.ref('product.product_product_3'),
                'product_uom_qty': 10.00,
                'procure_method': 'make_to_order',
            })],
        })
        pick_output = self.env['stock.picking'].new(default_get_vals)
        pick_output.onchange_picking_type()
        pick_output.move_lines.onchange_product_id()
        pick_output.update({
            'location_id': self.ref('stock.stock_location_output'),
            'location_dest_id': self.ref('stock.stock_location_customers'),
        })
        vals = pick_output._convert_to_write(pick_output._cache)
        pick_output = self.env['stock.picking'].create(vals)

        # Confirm delivery order.
        pick_output.action_confirm()

        # I run the scheduler.
        # Note: If purchase if already installed, the method _run_buy will be called due
        # to the purchase demo data. As we update the stock module to run this test, the
        # method won't be an attribute of stock.procurement at this moment. For that reason
        # we mute the logger when running the scheduler.
        with mute_logger('odoo.addons.stock.models.procurement'):
            self.env['procurement.group'].run_scheduler()

        # Check that a picking was created from stock to output.
        moves = self.env['stock.move'].search([
            ('product_id', '=', self.ref('product.product_product_3')),
            ('location_id', '=', self.ref('stock.stock_location_stock')),
            ('location_dest_id', '=', self.ref('stock.stock_location_output')),
            ('move_dest_ids', 'in', [pick_output.move_lines[0].id])
        ])
        self.assertEqual(len(moves.ids), 1, "It should have created a picking from Stock to Output with the original picking as destination")
示例#3
0
    def create_variant_ids(self):
        Product = self.env["product.product"]
        AttributeValues = self.env['product.attribute.value']

        variants_to_create = []
        variants_to_activate = []
        variants_to_unlink = []

        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: line.attribute_id.create_variant and 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)]})

            # 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.filtered(lambda r: r.attribute_id.create_variant).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
            for value_ids in variant_matrix:
                if set(value_ids.ids) not in existing_variants:
                    variants_to_create.append({
                        'product_tmpl_id': tmpl_id.id,
                        'attribute_value_ids': [(6, 0, value_ids.ids)]
                    })

            # check 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.append(product_id)
                elif product_id.attribute_value_ids.filtered(lambda r: r.attribute_id.create_variant) not in variant_matrix:
                    variants_to_unlink.append(product_id)

        if variants_to_activate:
            Product.concat(*variants_to_activate).write({'active': True})

        # create new products
        if variants_to_create:
            Product.create(variants_to_create)

        # unlink or inactive product
        for variant in variants_to_unlink:
            try:
                with self._cr.savepoint(), tools.mute_logger('odoo.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
示例#4
0
    def create_variant_ids(self):
        Product = self.env["product.product"]

        for tmpl_id in self.with_context(active_test=False):
            # list of values combination
            existing_variants = [set(variant.attribute_value_ids.ids) for variant in tmpl_id.product_variant_ids]
            variant_matrix = itertools.product(
                *(
                    line.value_ids
                    for line in tmpl_id.attribute_line_ids
                    if line.value_ids and line.value_ids[0].attribute_id.create_variant
                )
            )
            variant_matrix = map(
                lambda record_list: reduce(lambda x, y: x + y, record_list, self.env["product.attribute.value"]),
                variant_matrix,
            )
            to_create_variants = filter(lambda rec_set: set(rec_set.ids) not in existing_variants, variant_matrix)

            # 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)]})

            # 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 in variant_matrix:
                    variants_to_activate |= product_id
                elif product_id.attribute_value_ids 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)]}
                )

            # unlink or inactive product
            for variant in variants_to_activate:
                try:
                    with self._cr.savepoint(), tools.mute_logger("openerp.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
示例#5
0
    def test_sale_mrp(self):
        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('odoo.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.standard_price = 189.0
        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_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.ref('base.res_partner_4')
        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')
示例#6
0
    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.ref('base.res_partner_2')
        so_form.payment_term_id = self.env.ref('account.account_payment_term')
        with mute_logger('odoo.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.assertEquals(purchase.state, 'purchase', 'Purchase order should be in the approved state')
        self.assertEquals(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.assertEquals(len(move_line.ids), 1, 'There should be exactly one move line')
示例#7
0
 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('odoo.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()
示例#8
0
 def send_email(self, message, *args, **kwargs):
     with this.registry.cursor() as cr, mute_logger('odoo.sql_db'):
         try:
             # try ro aquire lock (no wait) on notification (should fail)
             cr.execute("SELECT email_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 email_status='bounce' WHERE id = %s", [notif.id])
     return message['Message-Id']
示例#9
0
 def safe_duplicate_create(self, vals):
     """
     Create benefit but silently abord if it already exists in the database (according to the _unique constraints).
     @return: new record if it didn't exist, empty recordset otherwise.
     """
     try:
         with mute_logger('odoo.sql_db'), self.env.cr.savepoint():
             # Any database call will be run inside a postgresql savepoint which is
             # rolledback if an exception occurs allowing to make subsequent calls in the transaction.
             # (Otherwise InternalError is raised by any subsequent database operations)
             return self.create(vals)
     except IntegrityError as err:
         if 'hr_benefit__unique' not in err.pgerror:
             raise err
         return self.env['hr.benefit']
示例#10
0
 def _cron_plan_compute(self):
     mrp_cron = self.sudo().env.ref('mrp_mrp.ir_cron_azi_mrp_action')
     try:
         with tools.mute_logger('odoo.sql_db'):
             self._cr.execute("SELECT id FROM ir_cron WHERE id = %s FOR UPDATE NOWAIT", (mrp_cron.id,))
     except Exception:
         _logger.info('Attempt to run mrp aborted (may be already running)')
         _logger.info('Cron %s exception: %s' % (mrp_cron.name, Exception.message))
         return {}
     _logger.info('Starting mrp calculation from cron')
     new_cr = self.pool.cursor()
     self._run_mrp_api(
         use_new_cursor=new_cr.dbname,
         company_id=self.env.user.company_id.id)
     return {}
示例#11
0
    def test_defaults(self):
        # Create some default value for some model, for all users.
        ir_values = self.env['ir.values']
        ir_values.set_default('res.partner', 'ref', 'X11')
        ir_values.set_default('res.partner.title', 'shortcut', 'Mr', condition='name=Mister')

        # Retrieve them: ds is a list of triplets (id, name, value)
        ds = ir_values.get_defaults('res.partner')
        d = next((d for d in ds if d[1] == 'ref'), None)
        self.assertTrue(d, "At least one value should be retrieved for this model.")
        self.assertEqual(d[2], 'X11', "Can't retrieve the created default value.")

        ds = ir_values.get_defaults('res.partner.title')
        d = next((d for d in ds if d[1] == 'shortcut'), None)
        self.assertFalse(d, "No value should be retrieved, the condition is not met.")

        ds = ir_values.get_defaults('res.partner.title', condition="name=Miss")
        d = next((d for d in ds if d[1] == 'shortcut'), None)
        self.assertFalse(d, "No value should be retrieved, the condition is not met.")

        ds = ir_values.get_defaults('res.partner.title', condition="name=Mister")
        d = next((d for d in ds if d[1] == 'shortcut'), None)
        self.assertTrue(d, "At least one value should be retrieved.")
        self.assertEqual(d[2], 'Mr', "Can't retrieve the created default value.")

        # Do it again but for a specific user.
        ir_values.set_default('res.partner', 'ref', '007', for_all_users=False)

        # Retrieve it and check it is the one for the current user.
        ds = ir_values.get_defaults('res.partner')
        d = next((d for d in ds if d[1] == 'ref'), None)
        self.assertTrue(d, "At least one value should be retrieved for this model.")
        self.assertEqual(d[2], '007', "Can't retrieve the created default value.")

        # create valid but unusable defaults, a ValidationError should not be thrown
        with tools.mute_logger('odoo.addons.base.ir.ir_values'):
            ir_values.set_default('unknown_model', 'unknown_field', 42)
            ir_values.set_default('res.partner', 'unknown_field', 42)

        # create invalid defaults
        with self.assertRaises(ValidationError):
            ir_values.set_default('res.partner', 'lang', 'some_LANG')
        with self.assertRaises(ValidationError):
            ir_values.set_default('res.partner', 'credit_limit', 'foo')
    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('procurement.ir_cron_scheduler_action')
            # Avoid to run the scheduler multiple times in the same time
            try:
                with tools.mute_logger('odoo.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 {}

            self.env['procurement.order']._procure_orderpoint_confirm(
                use_new_cursor=new_cr.dbname,
                company_id=self.env.user.company_id.id)
            new_cr.close()
            return {}
    def _procure_calculation_all(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 = registry(self._cr.dbname).cursor()
            self = self.with_env(self.env(cr=new_cr))  # TDE FIXME
            scheduler_cron = self.sudo().env.ref('procurement.ir_cron_scheduler_action')
            # Avoid to run the scheduler multiple times in the same time
            try:
                with tools.mute_logger('openerp.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 {}

            Procurement = self.env['procurement.order']
            for company in self.env.user.company_ids:
                Procurement.run_scheduler(use_new_cursor=self._cr.dbname, company_id=company.id)
            # close the new cursor
            self._cr.close()
            return {}
示例#14
0
 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('odoo.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")
示例#15
0
    def _procure_calculation_products(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 = registry(self._cr.dbname).cursor()
            self = self.with_env(self.env(cr=new_cr))  # TDE FIXME

            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('odoo.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 {}

            if self.group_id:
                self.individual_procurement()
                self._cr.commit()
            else:
                products = self.env['product.product']
                for item in self.item_ids:
                    products |= item.product_id

                ProcurementGroup = self.env['procurement.group']
                ProcurementGroup.with_context(
                    product_ids=products.ids).run_scheduler(
                        use_new_cursor=self._cr.dbname,
                        company_id=self.env.user.company_id)

            self._cr.close()
            return {}
示例#16
0
    def split(self, template, clear_attribute_value=False):
        relations = self._shared_table()

        for table, column, column2 in relations:
            query_dic = {'table': table, 'column': column, 'column_t': column2}

            try:
                with mute_logger('odoo.sql_db'), self._cr.savepoint():
                    query = 'UPDATE "%(table)s" SET %(column_t)s = %%s WHERE %(column)s=%%s' % query_dic
                    self._cr.execute(query, (
                        template.id,
                        self.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
                query = 'DELETE FROM "%(table)s" WHERE "%(column)s"=%%s' % query_dic
                self._cr.execute(query, (self.id, ))

        if clear_attribute_value:
            query = 'DELETE FROM product_attribute_value_product_product_rel WHERE product_attribute_value_id=%s AND product_product_id=%s' % (
                clear_attribute_value.id, self.id)
            self._cr.execute(query, ())
示例#17
0
    def run_scheduler(self):
        with api.Environment.manage():
            new_cr = self.pool.cursor()
            self = self.with_env(self.env(cr=new_cr))
            cron = self.sudo().env.ref('auto_rop.ir_cron_calculate_rop_action')
            try:
                with tools.mute_logger('odoo.sql_db'):
                    self._cr.execute(
                        "SELECT id FROM ir_cron WHERE id = %s FOR UPDATE NOWAIT",
                        (cron.id, ))
            except Exception:
                _logger.info(
                    "Attempt to run auto rop scheduler aborted, as already running"
                )
                self._cr.rollback()
                self._cr.close()
                return {}

            self.env['stock.warehouse.orderpoint'].run_scheduler(
                use_new_cursor=self._cr.dbname)

            new_cr.close()
            return {}
示例#18
0
    def test_redirect_form_values(self):
        tx = self.create_transaction(flow="redirect")

        with mute_logger('odoo.addons.payment.models.payment_transaction'):
            processing_values = tx._get_processing_values()
        form_info = self._extract_values_from_html_form(
            processing_values['redirect_form_html'])
        form_inputs = form_info['inputs']

        self.assertEqual(form_info['action'], self.sips.sips_test_url)
        self.assertEqual(form_inputs['InterfaceVersion'],
                         self.sips.sips_version)
        return_url = self._build_url(SipsController._return_url)
        notify_url = self._build_url(SipsController._notify_url)
        self.assertEqual(form_inputs['Data'],
            f'amount=111111|currencyCode=978|merchantId=dummy_mid|normalReturnUrl={return_url}|' \
            f'automaticResponseUrl={notify_url}|transactionReference={self.reference}|' \
            f'statementReference={self.reference}|keyVersion={self.sips.sips_key_version}|' \
            f'returnContext={json.dumps(dict(reference=self.reference))}'
        )
        self.assertEqual(
            form_inputs['Seal'],
            '4d7cc67c0168e8ce11c25fbe1937231c644861e320702ab544022b032b9eb4a2')
示例#19
0
    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('odoo.addons.base.models.ir_actions'):
            self_demo.with_context(self.context).run()
示例#20
0
    def setUpClass(cls):
        super(TestLeadAssignCommon, cls).setUpClass()
        cls._switch_to_multi_membership()
        cls._switch_to_auto_assign()

        # don't mess with existing teams, deactivate them to make tests repeatable
        cls.sales_teams = cls.sales_team_1 + cls.sales_team_convert
        cls.members = cls.sales_team_1_m1 + cls.sales_team_1_m2 + cls.sales_team_1_m3 + cls.sales_team_convert_m1 + cls.sales_team_convert_m2
        cls.env['crm.team'].search([('id', 'not in', cls.sales_teams.ids)
                                    ]).write({'active': False})

        # don't mess with existing leads, unlink those assigned to users used here to make tests
        # repeatable (archive is not sufficient because of lost leads)

        with mute_logger('odoo.models.unlink'):
            cls.env['crm.lead'].with_context(active_test=False).search([
                '|', ('team_id', '=', False),
                ('user_id', 'in', cls.sales_teams.member_ids.ids)
            ]).unlink()
        cls.bundle_size = 50
        cls.env['ir.config_parameter'].set_param(
            'crm.assignment.commit.bundle', '%s' % cls.bundle_size)
        cls.env['ir.config_parameter'].set_param('crm.assignment.delay', '0')
    def _procure_calculation_all2(self):
        with api.Environment.manage():
            _logger.info("hola entre back")
            # As this function is in a new thread, i need to open a new cursor, because the old one may be closed
            new_cr = registry(self._cr.dbname).cursor()
            self = self.with_env(self.env(cr=new_cr))  # TDE FIXME
            scheduler_cron = self.sudo().env.ref('procurement.ir_cron_scheduler_action')
            # Avoid to run the scheduler multiple times in the same time
            try:
                with tools.mute_logger('odoo.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 {}

            Procurement = self.env['procurement.order']
            #for company in self.env.user.company_ids:
            Procurement.run_scheduler(use_new_cursor=self._cr.dbname, company_id=False)
            # close the new cursor
            self._cr.close()
            return {}
示例#22
0
 def test_bad_template_does_not_break(self):
     """Create template with error (obaject) to test error."""
     self.env["mail.message.custom.subject"].create({
         "name":
         "Test bad template 1",
         "model_id":
         self.env.ref("base.model_res_partner").id,
         "subtype_ids": [(6, 0, [self.env.ref("mail.mt_comment").id])],
         "subject_template":
         "${obaject.number_a} and something",
         "position":
         "append_after",
     })
     # Send message in partner
     with mute_logger("odoo.addons.mail.models.mail_render_mixin"):
         mail_message_1 = self.partner_1.message_post(
             body="Test",
             subtype_xmlid="mail.mt_comment",
             subject="Test",
         )
     # Get message and check subject
     # No exception should be raised but subject should remain as original.
     self.assertEqual(mail_message_1.subject, "Test")
示例#23
0
    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('odoo.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:
                cids = (self.env.user.company_id | self.env.user.company_ids).ids
                self.env['procurement.group'].with_context(allowed_company_ids=cids).run_scheduler(
                    use_new_cursor=self._cr.dbname,
                    company_id=company.id)
            new_cr.close()
            return {}
示例#24
0
 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('openerp.sql_db'):
             presence.write(values)
     # avoid TransactionRollbackError
     self.env.cr.commit() # TODO : check if still necessary
示例#25
0
    def test_redirect_form_values(self):
        return_url = self._build_url(BuckarooController._return_url)
        expected_values = {
            'Brq_websitekey': self.buckaroo.buckaroo_website_key,
            'Brq_amount': str(self.amount),
            'Brq_currency': self.currency.name,
            'Brq_invoicenumber': self.reference,
            'Brq_signature': 'dacc220c3087edcc1200a38a6db0191c823e7f69',
            'Brq_return': return_url,
            'Brq_returncancel': return_url,
            'Brq_returnerror': return_url,
            'Brq_returnreject': return_url,
            'Brq_culture': 'en-US',
        }

        tx_sudo = self.create_transaction(flow='redirect')
        with mute_logger('odoo.addons.payment.models.payment_transaction'):
            processing_values = tx_sudo._get_processing_values()
        form_info = self._extract_values_from_html_form(processing_values['redirect_form_html'])

        self.assertEqual(form_info['action'], "https://testcheckout.buckaroo.nl/html/")
        self.assertDictEqual(expected_values, form_info['inputs'],
            "Buckaroo: invalid inputs specified in the redirect form.")
示例#26
0
    def test_01_load(self):
        """ Admin user can import data, but the demo user cannot """
        fields = (
            'id',
            'name',
            'perm_read',
            'perm_write',
            'perm_create',
            'perm_unlink',
        )
        data = [
            ('access_res_users_test', 'res.users test', '1', '0', '0', '0'),
            ('access_res_users_test2', 'res.users test2', '1', '1', '1', '1'),
        ]

        self.has_button_import(user=self.env.user)
        with mute_logger('odoo.sql_db'):
            res = self.Access.load(fields, data)

        self.assertEqual(res['ids'], False)
        self.assertEqual(len(res['messages']), 2)
        self.assertEqual(
            res['messages'][0]['message'],
            "Missing required value for the field 'Object' (model_id)")
        self.assertEqual(
            res['messages'][1]['message'],
            "Missing required value for the field 'Object' (model_id)")

        self.has_button_import(falsify=True, user=self.user_test)
        res2 = self.Access.sudo(self.user_test).load(fields, data)

        self.assertEqual(res2['ids'], None)
        self.assertEqual(len(res2['messages']), 1)
        self.assertEqual(
            res2['messages'][0]['message'],
            'User (ID: %s) is not allowed to import data in '
            'model ir.model.access.' % self.user_test.id)
示例#27
0
    def test_unicity(self):
        """ Archived memberships should be removed when detecting duplicates.
        Creating duplicates should raise unicity constraint.

        Note: redoing the data set to avoid clashing with SavepointCase as
        we expect a db-level assert """
        sales_team_1_m1 = self.env['crm.team.member'].create({
            'user_id':
            self.user_sales_leads.id,
            'crm_team_id':
            self.sales_team_1.id,
        })

        sales_team_1_m1.write({'active': False})
        sales_team_1_m1.flush_recordset()

        sales_team_1_m2 = self.env['crm.team.member'].create({
            'user_id':
            self.user_sales_leads.id,
            'crm_team_id':
            self.sales_team_1.id,
        })

        found = self.env['crm.team.member'].search([
            ('user_id', '=', self.user_sales_leads.id),
            ('crm_team_id', '=', self.sales_team_1.id),
        ])
        self.assertEqual(found, sales_team_1_m2)

        with self.assertRaises(
                exceptions.UserError), mute_logger('odoo.sql_db'):
            self.env['crm.team.member'].create({
                'user_id':
                self.user_sales_leads.id,
                'crm_team_id':
                self.sales_team_1.id,
            })
示例#28
0
文件: product.py 项目: nurefexc/odoo
    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('odoo.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})
示例#29
0
 def test_login_login_is_lowercased(self):
     """ It should verify the login is set to lowercase on login """
     rec_id = self._new_record()
     # We have to commit this cursor, because `_login` uses a fresh cursor
     self.env.cr.commit()
     with mute_logger("odoo.addons.auth_ldap.models.res_company_ldap"):
         res_id = self.model_obj._login(
             self.env.registry.db_name,
             self.login.upper(),
             "password",
             {"interactive": True},
         )
     # Now clean up our mess to preserve idempotence
     with api.Environment.manage():
         with registry(self.env.registry.db_name).cursor() as new_cr:
             new_cr.execute("DELETE FROM res_users WHERE \
                 login='******'" % self.login.lower())
             new_cr.commit()
     self.assertEqual(
         rec_id.id,
         res_id,
         "Login with with uppercase chars was not \
         successful",
     )
示例#30
0
    def test_redirect_form_values(self):
        tx = self.create_transaction(flow="redirect")

        with mute_logger('odoo.addons.payment.models.payment_transaction'):
            processing_values = tx._get_processing_values()
        form_info = self._extract_values_from_html_form(
            processing_values['redirect_form_html'])
        form_inputs = form_info['inputs']

        self.assertEqual(form_info['action'], self.sips.sips_test_url)
        self.assertEqual(form_inputs['InterfaceVersion'],
                         self.sips.sips_version)
        return_url = self._build_url(SipsController._return_url)
        notify_url = self._build_url(SipsController._webhook_url)
        self.assertEqual(
            form_inputs['Data'],
            f'amount=111111|currencyCode=978|merchantId=dummy_mid|normalReturnUrl={return_url}|'
            f'automaticResponseUrl={notify_url}|transactionReference={self.reference}|'
            f'statementReference={self.reference}|keyVersion={self.sips.sips_key_version}|'
            f'returnContext={json.dumps(dict(reference=self.reference))}',
        )
        self.assertEqual(
            form_inputs['Seal'],
            '99d1d2d46a841de7fe313ac0b2d13a9e42cad50b444d35bf901879305818d9b2')
示例#31
0
def _check_xml(self):
    """Mute warnings about views which are common during migration"""
    with mute_logger("odoo.addons.base.models.ir_ui_view"):
        return View._check_xml._original_method(self)
示例#32
0
        """ Only moderators can modify pending messages """
        self.group_public.write({
            'email_send': True,
            'moderation': True,
            'channel_partner_ids': [(4, self.partner_employee.id)],
            'moderator_ids': [(4, self.user_employee.id)],
        })
        self.message.write({'model': 'mail.channel', 'res_id': self.group_public.id, 'moderation_status': 'pending_moderation'})
        self.message.with_user(self.user_employee).write({'moderation_status': 'accepted'})

    def test_mail_message_access_write_crash_moderation(self):
        self.message.write({'model': 'mail.channel', 'res_id': self.group_public.id, 'moderation_status': 'pending_moderation'})
        with self.assertRaises(AccessError):
            self.message.with_user(self.user_employee).write({'moderation_status': 'accepted'})

    @mute_logger('openerp.addons.mail.models.mail_mail')
    def test_mark_all_as_read(self):
        self.user_employee.notification_type = 'inbox'
        emp_partner = self.user_employee.partner_id.with_user(self.user_employee)

        group_private = self.env['mail.channel'].with_context({
            'mail_create_nolog': True,
            'mail_create_nosubscribe': True,
            'mail_channel_noautofollow': True,
        }).create({
            'name': 'Private',
            'description': 'Private James R.',
            'public': 'private',
            'alias_name': 'private',
            'alias_contact': 'followers'}
        ).with_context({'mail_create_nosubscribe': False})
示例#33
0
    def test_create_from_ui(self):
        """
        Simulation of sales coming from the interface, even after closing the session
        """
        def compute_tax(product, price, qty=1, taxes=None):
            if not taxes:
                taxes = product.taxes_id.filtered(
                    lambda t: t.company_id.id == self.env.user.id)
            currency = self.pos_config.pricelist_id.currency_id
            res = taxes.compute_all(price, currency, qty, product=product)
            untax = res['total_excluded']
            return untax, sum(tax.get('amount', 0.0) for tax in res['taxes'])

        # 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 = 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 = 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 = 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('odoo.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")
示例#34
0
    def test_30_stripe_form_management(self):
        stripe = self.env["payment.acquirer"].browse(self.stripe_id)
        self.assertEqual(stripe.environment, "test", "test without test environment")

        # typical data posted by Stripe after client has successfully paid
        stripe_post_data = {
            u"amount": 4700,
            u"amount_refunded": 0,
            u"application_fee": None,
            u"balance_transaction": u"txn_172xfnGMfVJxozLwssrsQZyT",
            u"captured": True,
            u"created": 1446529775,
            u"currency": u"eur",
            u"customer": None,
            u"description": None,
            u"destination": None,
            u"dispute": None,
            u"failure_code": None,
            u"failure_message": None,
            u"fraud_details": {},
            u"id": u"ch_172xfnGMfVJxozLwEjSfpfxD",
            u"invoice": None,
            u"livemode": False,
            u"metadata": {u"reference": u"SO100"},
            u"object": u"charge",
            u"paid": True,
            u"receipt_email": None,
            u"receipt_number": None,
            u"refunded": False,
            u"refunds": {
                u"data": [],
                u"has_more": False,
                u"object": u"list",
                u"total_count": 0,
                u"url": u"/v1/charges/ch_172xfnGMfVJxozLwEjSfpfxD/refunds",
            },
            u"shipping": None,
            u"source": {
                u"address_city": None,
                u"address_country": None,
                u"address_line1": None,
                u"address_line1_check": None,
                u"address_line2": None,
                u"address_state": None,
                u"address_zip": None,
                u"address_zip_check": None,
                u"brand": u"Visa",
                u"country": u"US",
                u"customer": None,
                u"cvc_check": u"pass",
                u"dynamic_last4": None,
                u"exp_month": 2,
                u"exp_year": 2022,
                u"fingerprint": u"9tJA9bUEuvEb3Ell",
                u"funding": u"credit",
                u"id": u"card_172xfjGMfVJxozLw1QO6gYNM",
                u"last4": u"4242",
                u"metadata": {},
                u"name": u"*****@*****.**",
                u"object": u"card",
                u"tokenization_method": None,
            },
            u"statement_descriptor": None,
            u"status": u"succeeded",
        }

        tx = self.env["payment.transaction"].create(
            {
                "amount": 4700,
                "acquirer_id": self.stripe_id,
                "currency_id": self.currency_euro.id,
                "reference": "SO100",
                "partner_name": "Norbert Buyer",
                "partner_country_id": self.country_france_id,
            }
        )

        # validate it
        tx.form_feedback(stripe_post_data, "stripe")
        self.assertEqual(tx.state, "done", "Stripe: validation did not put tx into done state")
        self.assertEqual(tx.acquirer_reference, stripe_post_data.get("id"), "Stripe: validation did not update tx id")
        # reset tx
        tx.write({"state": "draft", "date_validate": False, "acquirer_reference": False})
        # simulate an error
        stripe_post_data["status"] = "error"
        stripe_post_data.update(
            {
                u"error": {
                    u"message": u"Your card's expiration year is invalid.",
                    u"code": u"invalid_expiry_year",
                    u"type": u"card_error",
                    u"param": u"exp_year",
                }
            }
        )
        with mute_logger("openerp.addons.payment_stripe.models.payment_transaction"):
            tx.form_feedback(stripe_post_data, "stripe")
        # check state
        self.assertEqual(tx.state, "error", "Stipe: erroneous validation did not put tx into error state")
示例#35
0
    def test_30_stripe_form_management(self):
        stripe = self.env['payment.acquirer'].browse(self.stripe_id)
        self.assertEqual(stripe.environment, 'test', 'test without test environment')

        # typical data posted by Stripe after client has successfully paid
        stripe_post_data = {
            u'amount': 4700,
            u'amount_refunded': 0,
            u'application_fee': None,
            u'balance_transaction': u'txn_172xfnGMfVJxozLwssrsQZyT',
            u'captured': True,
            u'created': 1446529775,
            u'currency': u'eur',
            u'customer': None,
            u'description': None,
            u'destination': None,
            u'dispute': None,
            u'failure_code': None,
            u'failure_message': None,
            u'fraud_details': {},
            u'id': u'ch_172xfnGMfVJxozLwEjSfpfxD',
            u'invoice': None,
            u'livemode': False,
            u'metadata': {u'reference': u'SO100'},
            u'object': u'charge',
            u'paid': True,
            u'receipt_email': None,
            u'receipt_number': None,
            u'refunded': False,
            u'refunds': {u'data': [],
                         u'has_more': False,
                         u'object': u'list',
                         u'total_count': 0,
                         u'url': u'/v1/charges/ch_172xfnGMfVJxozLwEjSfpfxD/refunds'},
            u'shipping': None,
            u'source': {u'address_city': None,
                        u'address_country': None,
                        u'address_line1': None,
                        u'address_line1_check': None,
                        u'address_line2': None,
                        u'address_state': None,
                        u'address_zip': None,
                        u'address_zip_check': None,
                        u'brand': u'Visa',
                        u'country': u'US',
                        u'customer': None,
                        u'cvc_check': u'pass',
                        u'dynamic_last4': None,
                        u'exp_month': 2,
                        u'exp_year': 2022,
                        u'fingerprint': u'9tJA9bUEuvEb3Ell',
                        u'funding': u'credit',
                        u'id': u'card_172xfjGMfVJxozLw1QO6gYNM',
                        u'last4': u'4242',
                        u'metadata': {},
                        u'name': u'*****@*****.**',
                        u'object': u'card',
                        u'tokenization_method': None},
            u'statement_descriptor': None,
            u'status': u'succeeded'}

        tx = self.env['payment.transaction'].create({
            'amount': 4700,
            'acquirer_id': self.stripe_id,
            'currency_id': self.currency_euro.id,
            'reference': 'SO100',
            'partner_name': 'Norbert Buyer',
            'partner_country_id': self.country_france_id})

        # validate it
        tx.form_feedback(stripe_post_data, 'stripe')
        self.assertEqual(tx.state, 'done', 'Stripe: validation did not put tx into done state')
        self.assertEqual(tx.acquirer_reference, stripe_post_data.get('id'), 'Stripe: validation did not update tx id')
        # reset tx
        tx.write({'state': 'draft', 'date_validate': False, 'acquirer_reference': False})
        # simulate an error
        stripe_post_data['status'] = 'error'
        stripe_post_data.update({u'error': {u'message': u"Your card's expiration year is invalid.", u'code': u'invalid_expiry_year', u'type': u'card_error', u'param': u'exp_year'}})
        with mute_logger('openerp.addons.payment_stripe.models.payment_transaction'):
            tx.form_feedback(stripe_post_data, 'stripe')
        # check state
        self.assertEqual(tx.state, 'error', 'Stipe: erroneous validation did not put tx into error state')
示例#36
0
    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('odoo.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()
示例#37
0
    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')

        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('odoo.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),))
示例#38
0
    def test_pay_logged_in_another_company(self):
        """User pays for an amount in another company."""
        # for another res.partner than the user's one
        route_values = self._prepare_pay_values(
            partner=self.user_company_b.partner_id)

        # Log in as user from Company A
        self.authenticate(self.user_company_a.login, self.user_company_a.login)

        # Pay in company B
        route_values['company_id'] = self.company_b.id

        tx_context = self.get_tx_checkout_context(**route_values)
        for key, val in tx_context.items():
            if key in route_values:
                if key == 'access_token':
                    continue  # access_token was modified due to the change of partner.
                elif key == 'partner_id':
                    # The partner is replaced by the partner of the user paying.
                    self.assertEqual(val, self.user_company_a.partner_id.id)
                else:
                    self.assertEqual(val, route_values[key])

        available_acquirers = self.env['payment.acquirer'].sudo().browse(
            tx_context['acquirer_ids'])
        self.assertIn(self.acquirer_company_b, available_acquirers)
        self.assertEqual(available_acquirers.company_id, self.company_b)

        validation_values = {
            k: tx_context[k]
            for k in [
                'amount',
                'currency_id',
                'reference_prefix',
                'partner_id',
                'access_token',
                'landing_route',
            ]
        }
        validation_values.update({
            'flow': 'direct',
            'payment_option_id': self.acquirer_company_b.id,
            'tokenization_requested': False,
        })
        with mute_logger('odoo.addons.payment.models.payment_transaction'):
            processing_values = self.get_processing_values(**validation_values)
        tx_sudo = self._get_tx(processing_values['reference'])

        # Tx values == given values
        self.assertEqual(tx_sudo.acquirer_id.id, self.acquirer_company_b.id)
        self.assertEqual(tx_sudo.amount, self.amount)
        self.assertEqual(tx_sudo.currency_id.id, self.currency.id)
        self.assertEqual(tx_sudo.partner_id.id,
                         self.user_company_a.partner_id.id)
        self.assertEqual(tx_sudo.reference, self.reference)
        self.assertEqual(tx_sudo.company_id, self.company_b)
        # processing_values == given values
        self.assertEqual(processing_values['acquirer_id'],
                         self.acquirer_company_b.id)
        self.assertEqual(processing_values['amount'], self.amount)
        self.assertEqual(processing_values['currency_id'], self.currency.id)
        self.assertEqual(processing_values['partner_id'],
                         self.user_company_a.partner_id.id)
        self.assertEqual(processing_values['reference'], self.reference)
示例#39
0
    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.sudo(self.user_portal).create({
            'name': 'Post from Portal User',
            'content': 'I am not a bird.',
            'forum_id': self.forum.id,
        })
        self.employee_post = Post.sudo(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.sudo(self.user_employee).create({
            'post_id': self.admin_post.id,
            'vote': '1',
        })
        self.portal_vote_on_admin_post = Vote.sudo(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.sudo(self.user_employee).write({
                'vote': '-1',
            })
        with self.assertRaises(UserError):
            self.admin_vote_on_employee_post.sudo(self.user_portal).write({
                'vote': '-1',
            })

        # One should not be able to give his vote to someone else
        self.employee_vote_on_admin_post.sudo(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.sudo(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.sudo(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.sudo(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.sudo(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.sudo(self.user_portal).create({
                'post_id': self.portal_post.id,
                'vote': '1',
            })

        with mute_logger('odoo.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.sudo(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.sudo(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.sudo(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.sudo(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')
示例#40
0
    def create_variant_ids(self):
        Product = self.env["product.product"]

        variants_to_create = []
        variants_to_activate = []
        variants_to_unlink = []

        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: line.attribute_id.create_variant == 'always' and 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)]})

            # Determine which product variants need to be created based on the attribute
            # configuration. If any attribute is set to generate variants dynamically, skip the
            # process.
            # Technical note: if there is no attribute, a variant is still created because
            # 'not any([])' and 'set([]) not in set([])' are True.
            if not any(attrib.create_variant == 'dynamic' for attrib in tmpl_id.mapped('attribute_line_ids.attribute_id')):
                # Iterator containing all possible attribute values combination
                # The iterator is used to avoid MemoryError in case of a huge number of combination.
                all_variants = itertools.product(*(
                    line.value_ids.ids for line in tmpl_id.attribute_line_ids if line.value_ids[:1].attribute_id.create_variant != 'no_variant'
                ))
                # Set containing existing attribute values combination
                existing_variants = {
                    frozenset(variant.attribute_value_ids.filtered(lambda r: r.attribute_id.create_variant != 'no_variant').ids)
                    for variant in tmpl_id.product_variant_ids
                }
                # For each possible variant, create if it doesn't exist yet.
                for value_ids in all_variants:
                    value_ids = frozenset(value_ids)
                    if value_ids not in existing_variants:
                        variants_to_create.append({
                            'product_tmpl_id': tmpl_id.id,
                            'attribute_value_ids': [(6, 0, list(value_ids))],
                        })
                        if len(variants_to_create) > 1000:
                            raise UserError(_(
                            'The number of variants to generate is too high. '
                            'You should either not generate variants for each combination or generate them on demand from the sales order. '
                            'To do so, open the form view of attributes and change the mode of *Create Variants*.'))

            # Check existing variants if any needs to be activated or unlinked.
            # - if the product is not active and has valid attributes and attribute values, it
            #   should be activated
            # - if the product does not have valid attributes or attribute values, it should be
            #   deleted
            valid_value_ids = tmpl_id.mapped('attribute_line_ids.value_ids').filtered(
                lambda v: v.attribute_id.create_variant != 'no_variant'
            )
            valid_attribute_ids = valid_value_ids.mapped('attribute_id')
            for product_id in tmpl_id.product_variant_ids:
                if product_id._has_valid_attributes(valid_attribute_ids, valid_value_ids):
                    if not product_id.active:
                        variants_to_activate.append(product_id)
                else:
                    variants_to_unlink.append(product_id)

        if variants_to_activate:
            Product.concat(*variants_to_activate).write({'active': True})

        # create new products
        if variants_to_create:
            Product.create(variants_to_create)

        # unlink or inactive product
        for variant in variants_to_unlink:
            try:
                with self._cr.savepoint(), tools.mute_logger('odoo.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
示例#41
0
    def create_variant_ids(self):
        Product = self.env["product.product"]

        for tmpl_id in self.with_context(active_test=False):
            # Handle the variants for each template separately. This will be
            # less efficient when called on a lot of products with few variants
            # but it is better when there's a lot of variants on one template.
            variants_to_create = []
            variants_to_activate = self.env['product.product']
            variants_to_unlink = self.env['product.product']
            # 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._get_valid_product_template_attribute_lines().filtered(lambda line: line.attribute_id.create_variant == 'always' and 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)]})

            # Determine which product variants need to be created based on the attribute
            # configuration. If any attribute is set to generate variants dynamically, skip the
            # process.
            # Technical note: if there is no attribute, a variant is still created because
            # 'not any([])' and 'set([]) not in set([])' are True.
            if not tmpl_id.has_dynamic_attributes():
                # Iterator containing all possible `product.attribute.value` combination
                # The iterator is used to avoid MemoryError in case of a huge number of combination.
                all_variants = itertools.product(*(
                    line.value_ids.ids for line in tmpl_id.valid_product_template_attribute_line_wnva_ids
                ))
                # Set containing existing `product.attribute.value` combination
                existing_variants = {
                    frozenset(variant.attribute_value_ids.ids)
                    for variant in tmpl_id.product_variant_ids
                }
                # For each possible variant, create if it doesn't exist yet.
                for value_ids in all_variants:
                    value_ids = frozenset(value_ids)
                    if value_ids not in existing_variants:
                        variants_to_create.append({
                            'product_tmpl_id': tmpl_id.id,
                            'attribute_value_ids': [(6, 0, list(value_ids))],
                        })
                        if len(variants_to_create) > 1000:
                            raise UserError(_(
                                'The number of variants to generate is too high. '
                                'You should either not generate variants for each combination or generate them on demand from the sales order. '
                                'To do so, open the form view of attributes and change the mode of *Create Variants*.'))

            # Check existing variants if any needs to be activated or unlinked.
            # - if the product is not active and has valid attributes and attribute values, it
            #   should be activated
            # - if the product does not have valid attributes or attribute values, it should be
            #   deleted
            valid_value_ids = tmpl_id.valid_product_attribute_value_wnva_ids
            valid_attribute_ids = tmpl_id.valid_product_attribute_wnva_ids
            for product_id in tmpl_id.product_variant_ids:
                if product_id._has_valid_attributes(valid_attribute_ids, valid_value_ids):
                    if not product_id.active:
                        variants_to_activate += product_id
                else:
                    variants_to_unlink += product_id

            if variants_to_activate:
                variants_to_activate.write({'active': True})

            # create new products
            if variants_to_create:
                Product.create(variants_to_create)

            # unlink or inactive product
            # try in batch first because it is much faster
            try:
                with self._cr.savepoint(), tools.mute_logger('odoo.sql_db'):
                    variants_to_unlink.unlink()
            except Exception:
                # fall back to one by one if batch is not possible
                for variant in variants_to_unlink:
                    try:
                        with self._cr.savepoint(), tools.mute_logger('odoo.sql_db'):
                            variant.unlink()
                    # We catch all kind of exception to be sure that the operation doesn't fail.
                    except Exception:
                        # Note: this can still fail if something is preventing from archiving.
                        # This is the case from existing stock reordering rules.
                        variant.write({'active': False})

        # prefetched o2m have to be reloaded (because of active_test)
        # (eg. product.template: product_variant_ids)
        # We can't rely on existing invalidate_cache because of the savepoint.
        self.invalidate_cache()
        return True
示例#42
0
    def create_variant_ids(self):
        Product = self.env["product.product"]

        variants_to_create = []
        variants_to_activate = []
        variants_to_unlink = []

        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: line.attribute_id.create_variant == 'always' and 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)]})

            # Determine which product variants need to be created based on the attribute
            # configuration. If any attribute is set to generate variants dynamically, skip the
            # process.
            # Technical note: if there is no attribute, a variant is still created because
            # 'not any([])' and 'set([]) not in set([])' are True.
            if not any(attrib.create_variant == 'dynamic' for attrib in tmpl_id.mapped('attribute_line_ids.attribute_id')):
                # Iterator containing all possible attribute values combination
                # The iterator is used to avoid MemoryError in case of a huge number of combination.
                all_variants = itertools.product(*(
                    line.value_ids.ids for line in tmpl_id.attribute_line_ids if line.value_ids[:1].attribute_id.create_variant != 'no_variant'
                ))
                # Set containing existing attribute values combination
                existing_variants = {
                    frozenset(variant.attribute_value_ids.filtered(lambda r: r.attribute_id.create_variant != 'no_variant').ids)
                    for variant in tmpl_id.product_variant_ids
                }
                # For each possible variant, create if it doesn't exist yet.
                for value_ids in all_variants:
                    value_ids = frozenset(value_ids)
                    if value_ids not in existing_variants:
                        variants_to_create.append({
                            'product_tmpl_id': tmpl_id.id,
                            'attribute_value_ids': [(6, 0, list(value_ids))],
                        })
                        if len(variants_to_create) > 1000:
                            raise UserError(_(
                            'The number of variants to generate is too high. '
                            'You should either not generate variants for each combination or generate them on demand from the sales order. '
                            'To do so, open the form view of attributes and change the mode of *Create Variants*.'))

            # Check existing variants if any needs to be activated or unlinked.
            # - if the product is not active and has valid attributes and attribute values, it
            #   should be activated
            # - if the product does not have valid attributes or attribute values, it should be
            #   deleted
            valid_value_ids = tmpl_id.mapped('attribute_line_ids.value_ids').filtered(
                lambda v: v.attribute_id.create_variant != 'no_variant'
            )
            valid_attribute_ids = valid_value_ids.mapped('attribute_id')
            for product_id in tmpl_id.product_variant_ids:
                if product_id._has_valid_attributes(valid_attribute_ids, valid_value_ids):
                    if not product_id.active:
                        variants_to_activate.append(product_id)
                else:
                    variants_to_unlink.append(product_id)

        if variants_to_activate:
            Product.concat(*variants_to_activate).write({'active': True})

        # create new products
        if variants_to_create:
            Product.create(variants_to_create)

        # unlink or inactive product
        for variant in variants_to_unlink:
            try:
                with self._cr.savepoint(), tools.mute_logger('odoo.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
示例#43
0
 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('odoo.sql_db'):
         field2.name = field1.name
示例#44
0
 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('odoo.sql_db'):
         field2.name = field1.name
示例#45
0
 def test01_basic(self):
     """Fails immediately on missing product."""
     doc = self.create_tutorial('order02.csv')
     with tools.mute_logger('odoo.addons.edi.models.edi_issues'):
         self.assertFalse(doc.action_execute())
示例#46
0
    def test_sql_constraint_dates(self):
        # The goal is mainly to verify that a human friendly
        # error message is triggered if the date_from is after
        # date_to. Coming from a bug due to the new ORM 13.0

        holiday_status_paid_time_off = self.env['hr.leave.type'].create({
            'name':
            'Paid Time Off',
            'allocation_type':
            'fixed',
            'validation_type':
            'both',
            'validity_start':
            time.strftime('%Y-%m-01'),
            'responsible_id':
            self.env.ref('base.user_admin').id,
        })

        self.env['hr.leave.allocation'].create({
            'name':
            'Paid Time off for David',
            'holiday_status_id':
            holiday_status_paid_time_off.id,
            'number_of_days':
            20,
            'employee_id':
            self.ref('hr.employee_admin'),
            'state':
            'validate',
        })

        leave_vals = {
            'name': 'Sick Time Off',
            'holiday_status_id': holiday_status_paid_time_off.id,
            'date_from': datetime.today().strftime('%Y-%m-11 19:00:00'),
            'date_to': datetime.today().strftime('%Y-%m-10 10:00:00'),
            'employee_id': self.ref('hr.employee_admin'),
            'number_of_days': 1,
        }
        with mute_logger('odoo.sql_db'):
            with self.assertRaises(IntegrityError):
                with self.cr.savepoint():
                    self.env['hr.leave'].create(leave_vals)

        leave_vals = {
            'name': 'Sick Time Off',
            'holiday_status_id': holiday_status_paid_time_off.id,
            'date_from': datetime.today().strftime('%Y-%m-10 10:00:00'),
            'date_to': datetime.today().strftime('%Y-%m-11 19:00:00'),
            'employee_id': self.ref('hr.employee_admin'),
            'number_of_days': 1,
        }
        leave = self.env['hr.leave'].create(leave_vals)
        with mute_logger('odoo.sql_db'):
            with self.assertRaises(IntegrityError):  # No ValidationError
                with self.cr.savepoint():
                    leave.write({
                        'date_from':
                        datetime.today().strftime('%Y-%m-11 19:00:00'),
                        'date_to':
                        datetime.today().strftime('%Y-%m-10 10:00:00'),
                    })
示例#47
0
    def _test_flow(self, flow):
        """ Simulate the given online payment flow and tests the tx values at each step.

        :param str flow: The online payment flow to test ('direct', 'redirect', or 'token')
        :return: The transaction created by the payment flow
        :rtype: recordset of `payment.transaction`
        """
        self.reference = f"Test Transaction ({flow} - {self.partner.name})"
        route_values = self._prepare_pay_values()

        # /payment/pay
        tx_context = self.get_tx_checkout_context(**route_values)
        for key, val in tx_context.items():
            if key in route_values:
                self.assertEqual(val, route_values[key])

        # Route values are taken from tx_context result of /pay route to correctly simulate the flow
        route_values = {
            k: tx_context[k]
            for k in [
                'amount',
                'currency_id',
                'reference_prefix',
                'partner_id',
                'access_token',
                'landing_route',
            ]
        }
        route_values.update({
            'flow': flow,
            'payment_option_id': self.acquirer.id,
            'tokenization_requested': False,
        })

        if flow == 'token':
            route_values['payment_option_id'] = self.create_token().id

        with mute_logger('odoo.addons.payment.models.payment_transaction'):
            processing_values = self.get_processing_values(**route_values)
        tx_sudo = self._get_tx(processing_values['reference'])

        # Tx values == given values
        self.assertEqual(tx_sudo.acquirer_id.id, self.acquirer.id)
        self.assertEqual(tx_sudo.amount, self.amount)
        self.assertEqual(tx_sudo.currency_id.id, self.currency.id)
        self.assertEqual(tx_sudo.partner_id.id, self.partner.id)
        self.assertEqual(tx_sudo.reference, self.reference)

        # processing_values == given values
        self.assertEqual(processing_values['acquirer_id'], self.acquirer.id)
        self.assertEqual(processing_values['amount'], self.amount)
        self.assertEqual(processing_values['currency_id'], self.currency.id)
        self.assertEqual(processing_values['partner_id'], self.partner.id)
        self.assertEqual(processing_values['reference'], self.reference)

        # Verify computed values not provided, but added during the flow
        self.assertIn("tx_id=", tx_sudo.landing_route)
        self.assertIn("access_token=", tx_sudo.landing_route)

        if flow == 'redirect':
            # In redirect flow, we verify the rendering of the dummy test form
            redirect_form_info = self._extract_values_from_html_form(
                processing_values['redirect_form_html'])

            # Test content of rendered dummy redirect form
            self.assertEqual(redirect_form_info['action'], 'dummy')
            # Public user since we didn't authenticate with a specific user
            self.assertEqual(redirect_form_info['inputs']['user_id'],
                             str(self.user.id))
            self.assertEqual(redirect_form_info['inputs']['view_id'],
                             str(self.dummy_acquirer.redirect_form_view_id.id))

        return tx_sudo
示例#48
0
    def test_00_crossdock(self):

        # Create a supplier
        supplier_crossdock = self.env['res.partner'].create(
            {'name': "Crossdocking supplier"})

        # I first create a warehouse with pick-pack-ship and reception in 2 steps
        wh_pps = self.env['stock.warehouse'].create({
            'name':
            'WareHouse PickPackShip',
            'code':
            'whpps',
            'reception_steps':
            'two_steps',
            'delivery_steps':
            'pick_pack_ship',
        })

        # Check that cross-dock route is active
        self.assertTrue(
            wh_pps.crossdock_route_id.active,
            "Crossdock route should be active when reception_steps is not in 'single_step'"
        )

        p_f = Form(self.env['product.template'])
        p_f.name = 'PCE'
        p_f.detailed_type = 'product'
        p_f.categ_id = self.env.ref('product.product_category_1')
        p_f.list_price = 100.0
        with p_f.seller_ids.new() as seller:
            seller.partner_id = supplier_crossdock
        p_f.route_ids.add(wh_pps.crossdock_route_id)
        cross_shop_product = p_f.save()

        p_f.standard_price = 70.0

        # Create a sales order with a line of 100 PCE incoming shipment with route_id crossdock shipping
        so_form = Form(self.env['sale.order'])
        so_form.partner_id = self.env['res.partner'].create(
            {'name': 'My Test Partner'})
        so_form.warehouse_id = wh_pps

        with mute_logger('odoo.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 = cross_shop_product.product_variant_ids
                line.product_uom_qty = 100.0
            sale_order_crossdock = so_form.save()

        # Confirm sales order
        sale_order_crossdock.action_confirm()

        # Run the scheduler
        self.env['procurement.group'].run_scheduler()

        # Check a quotation was created for the created supplier and confirm it
        po = self.env['purchase.order'].search([('partner_id', '=',
                                                 supplier_crossdock.id),
                                                ('state', '=', 'draft')])
        self.assertTrue(po, "an RFQ should have been created by the scheduler")
        po.button_confirm()
示例#49
0
    def test_mail_server_priorities(self):
        """Test if we choose the right mail server to send an email.

        Priorities are
        1. Forced mail server (e.g.: in mass mailing)
            - If the "from_filter" of the mail server match the notification email
              use the notifications email in the "From header"
            - Otherwise spoof the "From" (because we force the mail server but we don't
              know which email use to send it)
        2. A mail server for which the "from_filter" match the "From" header
        3. A mail server for which the "from_filter" match the domain of the "From" header
        4. The mail server used for notifications
        5. A mail server without "from_filter" (and so spoof the "From" header because we
           do not know for which email address it can be used)
        """
        # sanity checks
        self.assertTrue(self.env['ir.mail_server']._get_default_from_address(),
                        'Notifications email must be set for testing')
        self.assertTrue(
            self.env['ir.mail_server']._get_default_bounce_address(),
            'Bounce email must be set for testing')

        mail_server, mail_from = self.env['ir.mail_server']._find_mail_server(
            email_from='*****@*****.**')
        self.assertEqual(mail_server, self.server_user)
        self.assertEqual(mail_from, '*****@*****.**')

        mail_server, mail_from = self.env['ir.mail_server']._find_mail_server(
            email_from='"Name [email protected]" <*****@*****.**>')
        self.assertEqual(mail_server, self.server_user,
                         'Must extract email from full name')
        self.assertEqual(mail_from,
                         '"Name [email protected]" <*****@*****.**>',
                         'Must keep the given mail from')

        # Should not be case sensitive
        mail_server, mail_from = self.env['ir.mail_server']._find_mail_server(
            email_from='*****@*****.**')
        self.assertEqual(mail_server, self.server_user,
                         'Mail from is case insensitive')
        self.assertEqual(mail_from, '*****@*****.**',
                         'Should not change the mail from')

        mail_server, mail_from = self.env['ir.mail_server']._find_mail_server(
            email_from='*****@*****.**')
        self.assertEqual(mail_server, self.server_domain)
        self.assertEqual(mail_from, '*****@*****.**')

        # Cover a different condition that the "email case insensitive" test
        mail_server, mail_from = self.env['ir.mail_server']._find_mail_server(
            email_from='*****@*****.**')
        self.assertEqual(mail_server, self.server_domain,
                         'Domain is case insensitive')
        self.assertEqual(mail_from, '*****@*****.**',
                         'Domain is case insensitive')

        mail_server, mail_from = self.env['ir.mail_server']._find_mail_server(
            email_from='"Test" <test@unknown_domain.com>')
        self.assertEqual(mail_server, self.server_notification,
                         'Should take the notification email')
        self.assertEqual(mail_from, '*****@*****.**')

        # remove the notifications email to simulate a mis-configured Odoo database
        # so we do not have the choice, we have to spoof the FROM
        # (otherwise we can not send the email)
        self.env['ir.config_parameter'].sudo().set_param(
            'mail.catchall.domain', False)
        with mute_logger('odoo.addons.base.models.ir_mail_server'):
            mail_server, mail_from = self.env[
                'ir.mail_server']._find_mail_server(
                    email_from='test@unknown_domain.com')
            self.assertEqual(
                mail_server.from_filter, False,
                'No notifications email set, must be forced to spoof the FROM')
            self.assertEqual(mail_from, 'test@unknown_domain.com')
示例#50
0
 def test_create_or_update_with_errors(self):
     request = fake_request(form_data={}, method='POST')
     form = self.get_form('cms.form.res.partner', req=request)
     with mute_logger('odoo.sql_db'):
         values = form.form_process_POST({})
     self.assertTrue('_integrity' in values['errors'])
示例#51
0
    def test_create_from_ui(self):
        """
        Simulation of sales coming from the interface, even after closing the session
        """

        def compute_tax(product, price, qty=1, taxes=None):
            if not taxes:
                taxes = product.taxes_id.filtered(lambda t: t.company_id.id == self.env.user.id)
            currency = self.pos_config.pricelist_id.currency_id
            res = taxes.compute_all(price, currency, qty, product=product)
            untax = res['total_excluded']
            return untax, sum(tax.get('amount', 0.0) for tax in res['taxes'])

        # I click on create a new session button
        self.pos_config.open_session_cb()

        current_session = self.pos_config.current_session_id
        num_starting_orders = len(current_session.order_ids)

        untax, atax = 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,
             {'account_id': self.env.user.partner_id.property_account_receivable_id.id,
              'amount': untax + atax,
              'journal_id': self.pos_config.journal_ids[0].id,
              'name': fields.Datetime.now(),
              'statement_id': current_session.statement_ids[0].id}]],
           'uid': '00042-003-0014',
           'user_id': self.env.uid},
          'id': '00042-003-0014',
          'to_invoice': False}

        untax, atax = 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,
             {'account_id': self.env.user.partner_id.property_account_receivable_id.id,
              'amount': untax + atax,
              'journal_id': self.pos_config.journal_ids[0].id,
              'name': fields.Datetime.now(),
              'statement_id': current_session.statement_ids[0].id}]],
           'uid': '00043-003-0014',
           'user_id': self.env.uid},
          'id': '00043-003-0014',
          'to_invoice': False}

        untax, atax = 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,
             {'account_id': self.env.user.partner_id.property_account_receivable_id.id,
              'amount': untax + atax,
              'journal_id': self.pos_config.journal_ids[0].id,
              'name': fields.Datetime.now(),
              'statement_id': current_session.statement_ids[0].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 resubmit the same order
        self.PosOrder.create_from_ui([carrot_order])
        self.assertEqual(num_starting_orders + 1, len(current_session.order_ids), "Resubmitted order was not skipped")

        # 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('odoo.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")