Odoo - 使用代码而不是 ID 搜索产品

Odoo - Search products with code instead of id

我正在使用 odoo 10,我有两个型号 Order_Line 和产品。

订单行

class OrderLine(models.Model):
_name = 'order_line'
_description = 'Order Lines'

name = fields.Char()
products = fields.Many2one('amgl.products', String='Products')

产品

class Products(models.Model):
_name = 'products'
_description = 'Products'
    _sql_constraints = [
    ('uniq_poduct_code', 'unique(product_code)', 'Product Code already exists!')
]

name = fields.Char()
product_code = Char()

现在我正在尝试从 csv 文件创建 order_line,而在 csv 文件中,客户向我提供 'Product Code' 而不是 Id。如何处理,我们使用产品代码,系统自动填写与该产品代码关联的产品。

注:

Product Code在产品table中也是唯一的,所以没有重复的机会。

CSV 模板:

customer/account_number,customer/first_name,customer/last_name,customer/account_type,order/transaction_id,order/products/product_code,order/quantity,order/customer_id/id

您可以按 product_code 搜索:

@api.model
def search_by_code(self, code):
    result = self.env['products'].search([('product_code', '=', code)])

案例 1:数据库中没有存储有客户给您的任何产品代码的产品

如果尚未在数据库中创建产品代码,您应该有两个 CSV 文件(Products.csvOrderLine.csv)。第一个必须有三列(idnameproduct_code)。第二个也必须有三列(idnameproducts/id)。因此,您只需在 Products.csv 中的 id 列下创建一个 XML ID,并从文件 OrderLine.csv.

列的相应行 products/id

案例2:客户给你的产品编码属于数据库中已有的产品

现在,客户给了你数据库中已经存在的产品的产品代码。在这种情况下,您不必创建 Products.csv 文件。您需要知道哪些产品的 XML ID 具有客户提供给您的产品代码。为此,您可以通过 Odoo 的界面进入模型的树视图 products(如果此视图不存在,则必须创建它)。然后,您必须 select 所有记录(如果需要,单击右上角的数字 80 可在每页显示更多记录)。一旦所有这些都被 selected,点击 More 按钮,然后点击 Export。 Select 列 product_codename 然后继续。例如,将生成的 CSV 文件保存为 Products.csv。打开它,你会看到所有出口产品的 XML ID(如果他们没有 XML ID,出口后他们会做 - 出口生成 XML ID每个导出的记录,如果它没有任何人-)。现在,我猜客户给了你一些类似文件的东西,其中包含列 订单行名称 产品代码 ,因此请替换 产品代码 列值与您刚刚导出的产品各自的 XML ID。所以最后你应该有一个文件要导入,OrderLine.csv,包含 idnameproducts/id 列。

案例3:数据库中有一些属于现有产品的产品代码,还有一些还不存在

在这种情况下,您必须结合案例 1 和案例 2,首先,按照案例 2 中的描述导出产品,然后,使用其代码尚不存在的产品创建一个新产品,如所述在案例1中,然后将客户给你的产品代码替换为案例2中描述的相应产品代码。

注意:如果您有数千条记录要导入并手动替换它们,此过程将为您节省很多时间。在这种情况下,必须在 CSV 编辑器中创建一个宏来执行替换(使用搜索和替换)。例如,在 LibreOffice 中,您可以使用 Python.

执行宏

Example (Case 3)

The customer has given you a file of order lines, with two lines:

  • Name: OL A, Product Code: AAA
  • Name: OL B, Product Code: BBB

You export products from Odoo interface and you get a file with one line:

id,name,product_code __export__.products_a,"Product A","AAA"

You look for the coincidences of the product codes in both files, and do the replacements in a copy of the customer file, so now you have this:

  • Name: OL A, Product Code: __export__.products_a
  • Name: OL B, Product Code: BBB

Then you create a new CSV Products.csv and put in there the products whose product code don't exist yet:

id,name,product_code __import__.products_b,"Product B","BBB"

Now apply the replacements again comparing this new file with the one we had, and you will get this:

  • Name: OL A, Product Code: __export__.products_a
  • Name: OL B, Product Code: __import__.products_b

Convert this file to a right CSV format for Odoo, and save it as OrderLine.csv:

id,name,products/id __import__.order_line_1,"OL A",__export__.products_a __import__.order_line_2,"OL B",__import__.products_b

And finally, import the files, and take into account: import Products.csv before OrderLine.csv.

编辑

我认为最好花点时间为您的 CSV 编辑器(Excel、LibreOffice、Open Office 或其他)编写一个宏,但如果您感到绝望并且需要仅通过 Odoo 执行此操作,我想出了一个糟糕的解决方法,但至少,它也应该有效。

1.Create order_line 模型中名为 product_code 的新 Char 字段(暂时存在)。

2.Modify该模型的ORM创建方法:

@api.model
def create(self, vals):
    product_id = False
    product_code = vals.get('product_code', False)
    if product_code:
        product = self.env['products'].search([
            ('product_code', '=', product_code)
        ])
        if product:
            product_id = product[0].id
    vals.update({
        'products': product_id,
    })
    return super(OrderLine, self).create(vals)

3.Copy 客户发给你的文件,将headers 正确命名,并将order/products/product_code 列重命名为product_code。导入 CSV 文件。每次导入记录都会调用order_line模型的ORM创建方法。

导入后,您将在数据库中拥有与产品正确相关的订单行。

完成后,您必须记住删除已添加的代码(并从数据库的 order_line 模型中删除列 product_code,以便删除垃圾)。

解决方案 1

您可以使用您在 CSV 中使用的字段创建瞬态模型。并应用@forvas的想法:

class ImportOrderLines(models.TransientModel):
    _name = 'import.order.lines'

    product_code = Char()

    @api.model
    def create(self, vals):
        product_id = False
        product_code = vals.get('product_code', False)
        if product_code:
            product = self.env['products'].search([
                ('product_code', '=', product_code)
            ])
            if product:
                product_id = product[0].id

        self.env['order_line'].create({
            'products': product_id,
        })
        return False   # you don't need to create the record in the transient model

您可以使用 base_import 视图转到此瞬态模型的列表视图并像在任何其他模型中一样导入。

解决方案 2

您可以创建向导以导入 CSV 以创建订单行。 检查以下源代码。您必须将方法 import_order_lines 分配给向导中的按钮。

import base64
import magic
import csv
from cStringIO import StringIO
import codecs

from openerp import models, fields, api, _
from openerp.exceptions import Warning


class ImportDefaultCodeWizard(models.TransientModel):
    _name = 'import.default_code.wizard'

    name = fields.Char(
        string='File name',
    )

    file = fields.Binary(
        string='ZIP file to import to Odoo',
        required=True,
    )

    @api.multi
    def import_order_lines(self):
        self.ensure_one()
        content = base64.decodestring(self.file)

        if codecs.BOM_UTF8 == content[:3]:  # remove "byte order mark" (windows)
            content = content[3:]

        file_type = magic.from_buffer(content, mime=True)

        if file_type == 'text/plain':
            self._generate_order_line_from_csv(content)
            return self._show_result_wizard()

        raise Warning(
            _('WRONG FILETYPE'),
            _('You should send a CSV file')
        )

    def _show_result_wizard(self):
        return {
            'type': 'ir.actions.act_window',
            'res_model': self._name,
            'view_type': 'form',
            'view_mode': 'form',
            'target': 'new',
            'context': self.env.context,
        }        

    def _generate_order_line_from_csv(self, data):
        try:
            reader = csv.DictReader(StringIO(data))
        except Exception:
            raise Warning(
                _('ERROR getting data from csv file'
                '\nThere was some error trying to get the data from the csv file.'
                '\nMake sure you are using the right format.'))
        n = 1

        for row in reader:
            n += 1
            self._validate_data(n, row)

            default_code = row.get('default_code', False)


            order_line = {
                'default_code': self._get_product_id(default_code),

                # here you should add all the order line fields
            }

            try:
                self.env['order_line'].create(order_line)
            except Exception:
                raise Warning(
                    _('The order line could not be created.'
                    '\nROW: %s') % n
                )

    def _validate_data(self, n, row):
        csv_fields = [
            'default_code',
        ]
        """ here is where you should add the CSV fields in order to validate them
                customer/account_number, customer/first_name, customer/last_name, 
                customer/account_type, order/transaction_id, order/products/product_code ,order/quantity, order/customer_id/id
        """


        for key in row:
            if key not in csv_fields:
                raise Warning(_('ERROR\nThe file format is not right.'
                                '\nCheck the column names and the CSV format'
                                '\nKEY: %s' % key))

        if row.get('default_code', False) == '':
            raise Warning(
                _('ERROR Validating data'),
                _('The product code should be filled.'
                '\nROW: %s') % n
            )

    def _get_product_id(self, default_code):
        if partner_id:
            product_obj = self.env['product.product'].search([
                ('default_code', '=', default_code),
            ])
            if len(product_code_obj) == 1:
                return product_obj.default_code
            else:
                raise Warning(
                    _('ERROR Validating data'),
                    _('The product code should be filled.'
                    '\nROW: %s') % n
                )        
            return False