Odoo Community Association

Purchase Order Line Import

Beta License: AGPL-3 OCA/purchase-workflow Translate me on Weblate Try me on Runboat

This module adds an Import Lines button to requests for quotation. It opens a wizard that reads a spreadsheet (XLSX, XLS, ODS or CSV) and adds one purchase order line per row to the current request for quotation.

The spreadsheet supports these columns:

Column Required Purchase order line field
Internal Reference Yes product_id, by the product internal reference
Quantity Yes product_qty
Description No name
Unit Price No price_unit
Analytic Account No analytic_distribution, 100% to the account

An empty optional cell keeps the value Odoo computes for the line: the default description of the product and the vendor price. The analytic account is searched by code or by name.

All the rows are checked before any line is created: if any of them has an error, nothing is imported and every error is reported with its row number.

Table of contents

Usage

  1. Open a request for quotation in the RFQ or RFQ Sent state.
  2. Click Import Lines.
  3. Click Download Template to get an empty spreadsheet with the supported columns. The required columns are highlighted, and each header has a comment that describes it.
  4. Fill in one row per line, upload the file and click Import.

The first row of the file must hold the column names. A column is recognized by its name in the language of the user, in English, or by its technical key (default_code, product_qty, name, price_unit, analytic_account), regardless of the case. The order of the columns does not matter.

The imported lines are added after the existing ones, and a message on the order records the file they come from.

Development

The wizard purchase.order.line.import is built to be extended. Each row of the file reaches the hooks as a dictionary that maps the column keys to the cell values, already stripped, and only holds the columns present in the file. A hook that raises a UserError reports its message as the error of that row, and the import goes on checking the next rows.

  • _get_import_columns(): the columns the file accepts. Each one is a dictionary with key, label, help and required.
  • _prepare_order_line_vals(row): the values of the purchase order line created for a row.
  • _create_order_lines(vals_list): creates the lines of all the rows once every row is valid, after the existing lines of the order.
  • _get_product_domain(row) and _get_product(row): the search of the product of a row.
  • _get_product_qty(row): the quantity of a row.
  • _get_analytic_account_domain(row) and _get_analytic_distribution(row): the search of the analytic account of a row, and the analytic distribution it gives.
  • _parse_float(value, label): converts a cell to a number, with the separators inferred by base_import.

A new column is added by extending _get_import_columns(), and its value is used in _prepare_order_line_vals(). It is then part of the downloaded template and recognized in the uploaded files, by its label or by its key:

from odoo import _, models


class PurchaseOrderLineImport(models.TransientModel):
    _inherit = "purchase.order.line.import"

    def _get_import_columns(self):
        columns = super()._get_import_columns()
        columns.append(
            {
                "key": "discount",
                "label": _("Discount"),
                "help": _("Discount in %."),
                "required": False,
            }
        )
        return columns

    def _prepare_order_line_vals(self, row):
        vals = super()._prepare_order_line_vals(row)
        if row.get("discount"):
            vals["discount"] = self._parse_float(row["discount"], _("Discount"))
        return vals

The search of an existing column is changed through its domain method, for instance to accept only the products the vendor of the order sells:

def _get_product_domain(self, row):
    domain = super()._get_product_domain(row)
    partner = self.order_id.partner_id.commercial_partner_id
    return domain + [("seller_ids.partner_id", "=", partner.id)]

Bug Tracker

Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us to smash it by providing a detailed and welcomed feedback.

Do not contact contributors directly about support or help with technical issues.

Credits

Authors

  • ForgeFlow

Maintainers

This module is maintained by the OCA.

Odoo Community Association

OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use.

This module is part of the OCA/purchase-workflow project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.