From 7ef46a69aaf4e1e7f5ed9a071b5e7f827cb16502 Mon Sep 17 00:00:00 2001 From: Alexis de Lattre Date: Sun, 28 Aug 2016 00:14:42 +0200 Subject: [PATCH 01/91] Initial check-in of module sale_order_import, sale_stock_order_import, sale_order_import_ubl, sale_order_import_csv, sale_order_ubl, sale_commercial_partner --- sale_order_import/__init__.py | 4 + sale_order_import/__openerp__.py | 18 + sale_order_import/models/__init__.py | 3 + sale_order_import/models/sale.py | 27 + sale_order_import/wizard/__init__.py | 3 + sale_order_import/wizard/sale_order_import.py | 462 ++++++++++++++++++ .../wizard/sale_order_import_view.xml | 78 +++ 7 files changed, 595 insertions(+) create mode 100644 sale_order_import/__init__.py create mode 100644 sale_order_import/__openerp__.py create mode 100644 sale_order_import/models/__init__.py create mode 100644 sale_order_import/models/sale.py create mode 100644 sale_order_import/wizard/__init__.py create mode 100644 sale_order_import/wizard/sale_order_import.py create mode 100644 sale_order_import/wizard/sale_order_import_view.xml diff --git a/sale_order_import/__init__.py b/sale_order_import/__init__.py new file mode 100644 index 0000000000..3c4e748f01 --- /dev/null +++ b/sale_order_import/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- + +from . import wizard +from . import models diff --git a/sale_order_import/__openerp__.py b/sale_order_import/__openerp__.py new file mode 100644 index 0000000000..4da4c028c2 --- /dev/null +++ b/sale_order_import/__openerp__.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +# © 2016 Akretion (Alexis de Lattre ) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + 'name': 'Sale Order Import', + 'version': '8.0.1.0.0', + 'category': 'Sales Management', + 'license': 'AGPL-3', + 'summary': 'Import RFQ or sale orders from files', + 'author': 'Akretion,Odoo Community Association (OCA)', + 'website': 'http://www.akretion.com', + 'depends': ['sale_commercial_partner', 'base_business_document_import'], + 'data': [ + 'wizard/sale_order_import_view.xml', + ], + 'installable': True, +} diff --git a/sale_order_import/models/__init__.py b/sale_order_import/models/__init__.py new file mode 100644 index 0000000000..78a9604960 --- /dev/null +++ b/sale_order_import/models/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +from . import sale diff --git a/sale_order_import/models/sale.py b/sale_order_import/models/sale.py new file mode 100644 index 0000000000..1a8123809c --- /dev/null +++ b/sale_order_import/models/sale.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +# © 2016 Akretion (Alexis de Lattre ) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from openerp import models, api, _ + + +class SaleOrder(models.Model): + _inherit = 'sale.order' + + @api.multi + def name_get(self): + """Add amount_untaxed in name_get of sale orders""" + res = super(SaleOrder, self).name_get() + if self._context.get('sale_order_show_amount'): + new_res = [] + for (sale_id, name) in res: + sale = self.browse(sale_id) + # I didn't find a python method to easily display + # a float + currency symbol (before or after) + # depending on lang of context and currency + name += _(' Amount w/o tax: %s %s)') % ( + sale.amount_untaxed, sale.currency_id.name) + new_res.append((sale_id, name)) + return new_res + else: + return res diff --git a/sale_order_import/wizard/__init__.py b/sale_order_import/wizard/__init__.py new file mode 100644 index 0000000000..38fba3b37f --- /dev/null +++ b/sale_order_import/wizard/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +from . import sale_order_import diff --git a/sale_order_import/wizard/sale_order_import.py b/sale_order_import/wizard/sale_order_import.py new file mode 100644 index 0000000000..8df84d9d23 --- /dev/null +++ b/sale_order_import/wizard/sale_order_import.py @@ -0,0 +1,462 @@ +# -*- coding: utf-8 -*- +# © 2016 Akretion (Alexis de Lattre ) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from openerp import models, fields, api, _ +# import openerp.addons.decimal_precision as dp +from openerp.tools import float_compare, float_is_zero +from openerp.exceptions import Warning as UserError +import logging +import mimetypes +from lxml import etree + +logger = logging.getLogger(__name__) + + +class SaleOrderImport(models.TransientModel): + _name = 'sale.order.import' + _inherit = ['business.document.import'] + _description = 'Sale Order Import from Files' + + state = fields.Selection([ + ('import', 'Import'), + ('update', 'Update'), + ], string='State', default="import") + partner_id = fields.Many2one( + 'res.partner', string='Customer', domain=[('customer', '=', True)]) + csv_import = fields.Boolean(default=False, readonly=True) + order_file = fields.Binary( + string='Request for Quotation or Order', required=True, + help="Upload a Request for Quotation or an Order file. Supported " + "formats: CSV, XML and PDF (PDF with an embeded XML file).") + order_filename = fields.Char(string='Filename') + doc_type = fields.Selection([ + ('rfq', 'Request For Quotation'), + ('order', 'Sale Order'), + ], string='Document Type', readonly=True) + price_source = fields.Selection([ + ('pricelist', 'Pricelist'), + ('order', 'Customer Order'), + ], string='Apply Prices From') + # for state = update + commercial_partner_id = fields.Many2one( + 'res.partner', string='Customer', readonly=True) + partner_shipping_id = fields.Many2one( + 'res.partner', string='Shipping Address', readonly=True) + # amount_untaxed = fields.Float( + # string='Total Untaxed', digits=dp.get_precision('Account'), + # readonly=True) + sale_id = fields.Many2one( + 'sale.order', string='Quotation to Update') + + @api.onchange('order_file') + def order_file_change(self): + if self.order_filename and self.order_file: + filetype = mimetypes.guess_type(self.order_filename) + logger.debug('Order file mimetype: %s', filetype) + if filetype and filetype[0] in ('text/csv', 'text/plain'): + self.csv_import = True + self.doc_type = False + elif filetype and filetype[0] == 'application/xml': + self.csv_import = False + try: + xml_root = etree.fromstring( + self.order_file.decode('base64')) + except: + raise UserError(_("This XML file is not XML-compliant")) + doc_type = self.parse_xml_order(xml_root, detect_doc_type=True) + self.doc_type = doc_type + elif filetype and filetype[0] == 'application/pdf': + self.csv_import = False + doc_type = self.parse_pdf_order( + self.order_file.decode('base64'), detect_doc_type=True) + self.doc_type = doc_type + else: + return {'warning': { + 'title': _('Unsupported file format'), + 'message': _( + "This file '%s' is not recognised as a CSV, XML nor " + "PDF file. Please check the file and it's " + "extension.") % self.order_filename + }} + else: + self.csv_import = False + self.doc_type = False + + @api.model + def get_xml_doc_type(self, xml_root): + raise UserError + + @api.model + def parse_xml_order(self, xml_root, detect_doc_type=False): + raise UserError(_( + "This type of XML RFQ/order is not supported. Did you install " + "the module to support this XML format?")) + + @api.model + def parse_csv_order(self, order_file, partner): + assert partner, 'missing partner' + raise UserError(_( + "This type of CSV order is not supported. Did you install " + "the module to support CSV orders?")) + + @api.model + def parse_pdf_order(self, order_file, detect_doc_type=False): + """ + Get PDF attachments, filter on XML files and call import_order_xml + """ + xml_files_dict = self.get_xml_files_from_pdf(order_file) + if not xml_files_dict: + raise UserError(_( + 'There are no embedded XML file in this PDF file.')) + for xml_filename, xml_root in xml_files_dict.iteritems(): + logger.info('Trying to parse XML file %s', xml_filename) + try: + parsed_order = self.parse_xml_order( + xml_root, detect_doc_type=detect_doc_type) + return parsed_order + except: + continue + raise UserError(_( + "This type of XML RFQ/order is not supported. Did you install " + "the module to support this XML format?")) + + # Format of parsed_order + # { + # 'partner': { + # 'vat': 'FR25499247138', + # 'name': 'Camptocamp', + # 'email': 'luc@camptocamp.com', + # }, + # 'ship_to': { + # 'partner': partner_dict, + # 'address': { + # 'country_code': 'FR', + # 'state_code': False, + # 'zip': False, + # } + # 'date': '2016-08-16', # order date + # 'order_ref': 'PO1242', # Customer PO number + # 'currency': {'iso': 'EUR', 'symbol': u'€'}, + # 'incoterm': 'EXW', + # 'note': 'order notes of the customer', + # 'chatter_msg': ['msg1', 'msg2'] + # 'lines': [{ + # 'product': { + # 'code': 'EA7821', + # 'ean13': '2100002000003', + # }, + # 'qty': 2.5, + # 'uom': {'unece_code': 'C62'}, + # 'price_unit': 12.42, # without taxes + # 'doc_type': 'rfq' or 'order', + # }] + + @api.model + def _prepare_order(self, parsed_order, price_source): + soo = self.env['sale.order'] + partner = self._match_partner( + parsed_order['partner'], parsed_order['chatter_msg'], + partner_type='customer') + currency = self._match_currency( + parsed_order.get('currency'), parsed_order['chatter_msg']) + if partner.property_product_pricelist.currency_id != currency: + raise UserError(_( + "The customer '%s' has a pricelist '%s' but the " + "currency of this order is '%s'.") % ( + partner.name_get()[0][1], + partner.property_product_pricelist.name_get()[0][1], + currency.name)) + if parsed_order.get('order_ref'): + existing_orders = soo.search([ + ('client_order_ref', '=', parsed_order['order_ref']), + ('partner_id', '=', partner.id), + ('state', '!=', 'cancel'), + ]) + if existing_orders: + raise UserError(_( + "An order of customer '%s' with reference '%s' " + "already exists: %s (state: %s)") % ( + partner.name_get()[0][1], + parsed_order['order_ref'], + existing_orders[0].name, + existing_orders[0].state)) + partner_change_res = soo.onchange_partner_id(partner.id) + assert 'value' in partner_change_res, 'Error in partner change' + so_vals = { + 'partner_id': partner.id, + 'client_order_ref': parsed_order.get('order_ref'), + 'order_line': [] + } + so_vals.update(partner_change_res['value']) + if parsed_order.get('ship_to'): + shipping_partner = self._match_shipping_partner( + parsed_order['ship_to'], partner, parsed_order['chatter_msg']) + so_vals['partner_shipping_id'] = shipping_partner.id + if parsed_order.get('date'): + so_vals['date_order'] = parsed_order['date'] + for line in parsed_order['lines']: + # partner=False because we don't want to use product.supplierinfo + product = self._match_product( + line['product'], parsed_order['chatter_msg'], seller=False) + uom = self._match_uom( + line.get('uom'), parsed_order['chatter_msg'], product) + line_vals = self._prepare_create_order_line( + product, uom, line, price_source) + # product_id_change is played in the inherit of create() + # of sale.order.line cf odoo/addons/sale/sale.py + so_vals['order_line'].append((0, 0, line_vals)) + return so_vals + + @api.model + def create_order(self, parsed_order, price_source): + soo = self.env['sale.order'] + so_vals = self._prepare_order(parsed_order, price_source) + order = soo.create(so_vals) + self.post_create_or_update(parsed_order, order) + logger.info('Sale Order ID %d created', order.id) + return order + + @api.model + def parse_order(self, order_file, order_filename, partner=False): + assert order_file, 'Missing order file' + assert order_filename, 'Missing order filename' + filetype = mimetypes.guess_type(order_filename)[0] + logger.debug('Order file mimetype: %s', filetype) + if filetype in ('text/csv', 'text/plain'): + if not partner: + raise UserError(_('Missing customer')) + parsed_order = self.parse_csv_order(order_file, partner) + elif filetype == 'application/xml': + try: + xml_root = etree.fromstring(order_file) + except: + raise UserError(_("This XML file is not XML-compliant")) + pretty_xml_string = etree.tostring( + xml_root, pretty_print=True, encoding='UTF-8', + xml_declaration=True) + logger.debug('Starting to import the following XML file:') + logger.debug(pretty_xml_string) + parsed_order = self.parse_xml_order(xml_root) + elif filetype == 'application/pdf': + parsed_order = self.parse_pdf_order(order_file) + else: + raise UserError(_( + "This file '%s' is not recognised as a CSV, XML nor PDF file. " + "Please check the file and it's extension.") % order_filename) + logger.debug('Result of order parsing: %s', parsed_order) + if 'attachments' not in parsed_order: + parsed_order['attachments'] = {} + parsed_order['attachments'][order_filename] =\ + order_file.encode('base64') + if 'chatter_msg' not in parsed_order: + parsed_order['chatter_msg'] = [] + return parsed_order + + @api.multi + def import_order_button(self): + self.ensure_one() + order_file_decoded = self.order_file.decode('base64') + parsed_order = self.parse_order( + order_file_decoded, self.order_filename, self.partner_id) + if not parsed_order.get('lines'): + raise UserError(_( + "This order doesn't have any line !")) + partner = self._match_partner( + parsed_order['partner'], [], partner_type='customer') + commercial_partner = partner.commercial_partner_id + partner_shipping_id = False + if parsed_order.get('ship_to'): + partner_shipping_id = self._match_shipping_partner( + parsed_order['ship_to'], partner, []).id + existing_quotations = self.env['sale.order'].search([ + ('commercial_partner_id', '=', commercial_partner.id), + ('state', 'in', ('draft', 'sent'))]) + if existing_quotations: + default_sale_id = False + if len(existing_quotations) == 1: + default_sale_id = existing_quotations[0].id + self.write({ + 'commercial_partner_id': commercial_partner.id, + 'partner_shipping_id': partner_shipping_id, + 'state': 'update', + 'sale_id': default_sale_id, + 'doc_type': parsed_order.get('doc_type'), + }) + action = self.env['ir.actions.act_window'].for_xml_id( + 'sale_order_import', 'sale_order_import_action') + action['res_id'] = self.id + return action + else: + return self.create_order_return_action(parsed_order) + + @api.multi + def create_order_button(self): + self.ensure_one() + parsed_order = self.parse_order( + self.order_file.decode('base64'), self.order_filename, + self.partner_id) + return self.create_order_return_action(parsed_order) + + @api.multi + def create_order_return_action(self, parsed_order): + self.ensure_one() + order = self.create_order(parsed_order, self.price_source) + order.message_post(_( + "Created automatically via file import (%s).") + % self.order_filename) + action = self.env['ir.actions.act_window'].for_xml_id( + 'sale', 'action_quotations') + action.update({ + 'view_mode': 'form,tree,calendar,graph', + 'views': False, + 'view_id': False, + 'res_id': order.id, + }) + return action + + @api.model + def _prepare_update_order_vals(self, parsed_order, order, partner): + partner = self._match_partner( + parsed_order['partner'], parsed_order['chatter_msg'], + partner_type='customer') + vals = {'partner_id': partner.id} + if parsed_order.get('ship_to'): + shipping_partner = self._match_shipping_partner( + parsed_order['ship_to'], partner, parsed_order['chatter_msg']) + vals['partner_shipping_id'] = shipping_partner.id + if parsed_order.get('order_ref'): + vals['client_order_ref'] = parsed_order['order_ref'] + return vals + + @api.model + def _prepare_create_order_line( + self, product, uom, import_line, price_source): + vals = { + 'product_id': product.id, + 'product_uom_qty': import_line['qty'], + 'product_uom': uom.id, + } + if price_source == 'order': + vals['price_unit'] = import_line['price_unit'] # TODO : fix + return vals + + @api.multi + def update_order_lines(self, parsed_order, order): + chatter = parsed_order['chatter_msg'] + solo = self.env['sale.order.line'] + dpo = self.env['decimal.precision'] + qty_prec = dpo.precision_get('Product UoS') + price_prec = dpo.precision_get('Product Price') + existing_lines = [] + for oline in order.order_line: + # compute price unit without tax + price_unit = 0.0 + if not float_is_zero( + oline.product_uom_qty, precision_digits=qty_prec): + qty = float(oline.product_uom_qty) + price_unit = oline.price_subtotal / qty + existing_lines.append({ + 'product': oline.product_id or False, + 'name': oline.name, + 'qty': oline.product_uom_qty, + 'uom': oline.product_uom, + 'line': oline, + 'price_unit': price_unit, + }) + compare_res = self.compare_lines( + existing_lines, parsed_order['lines'], chatter, + qty_precision=qty_prec, seller=False) + # NOW, we start to write/delete/create the order lines + for oline, cdict in compare_res['to_update'].iteritems(): + write_vals = {} + # TODO: add support for price_source == order + if cdict.get('qty'): + chatter.append(_( + "The quantity has been updated on the order line " + "with product '%s' from %s to %s %s") % ( + oline.product_id.name_get()[0][1], + cdict['qty'][0], cdict['qty'][1], + oline.product_uom.name)) + write_vals['product_uom_qty'] = cdict['qty'][1] + if self.price_source != 'order': + new_price_unit = order.pricelist_id.with_context( + date=order.date_order, + uom=oline.product_uom.id).price_get( + oline.product_id.id, write_vals['product_uom_qty'], + order.partner_id.id)[order.pricelist_id.id] + if float_compare( + new_price_unit, oline.price_unit, + precision_digits=price_prec): + chatter.append(_( + "The unit price has been updated on the order " + "line with product '%s' from %s to %s %s") % ( + oline.product_id.name_get()[0][1], + oline.price_unit, new_price_unit, + order.currency_id.name)) + write_vals['price_unit'] = new_price_unit + if write_vals: + oline.write(write_vals) + if compare_res['to_remove']: + to_remove_label = [ + '%s %s x %s' % ( + l.product_uom_qty, l.product_uom.name, l.product_id.name) + for l in compare_res['to_remove']] + chatter.append(_( + "%d order line(s) deleted: %s") % ( + len(compare_res['to_remove']), + ', '.join(to_remove_label))) + compare_res['to_remove'].unlink() + if compare_res['to_add']: + to_create_label = [] + for add in compare_res['to_add']: + line_vals = self._prepare_create_order_line( + add['product'], add['uom'], add['import_line'], + self.price_source) + line_vals['order_id'] = order.id + new_line = solo.create(line_vals) + to_create_label.append('%s %s x %s' % ( + new_line.product_uom_qty, + new_line.product_uom.name, + new_line.name)) + chatter.append(_("%d new order line(s) created: %s") % ( + len(compare_res['to_add']), ', '.join(to_create_label))) + return True + + @api.multi + def update_order_button(self): + self.ensure_one() + order = self.sale_id + if not order: + raise UserError(_('You must select a quotation to update.')) + parsed_order = self.parse_order( + self.order_file.decode('base64'), self.order_filename, + self.partner_id) + currency = self._match_currency( + parsed_order.get('currency'), parsed_order['chatter_msg']) + if currency != order.currency_id: + raise UserError(_( + "The currency of the imported order (%s) is different from " + "the currency of the existing order (%s)") % ( + currency.name, order.currency_id.name)) + vals = self._prepare_update_order_vals( + parsed_order, order, self.commercial_partner_id) + if vals: + order.write(vals) + self.update_order_lines(parsed_order, order) + self.post_create_or_update(parsed_order, order) + logger.info( + 'Quotation ID %d updated via import of file %s', order.id, + self.order_filename) + order.message_post(_( + "This quotation has been updated automatically via the import of " + "file %s") % self.order_filename) + action = self.env['ir.actions.act_window'].for_xml_id( + 'sale', 'action_quotations') + action.update({ + 'view_mode': 'form,tree,calendar,graph', + 'views': False, + 'view_id': False, + 'res_id': order.id, + }) + return action diff --git a/sale_order_import/wizard/sale_order_import_view.xml b/sale_order_import/wizard/sale_order_import_view.xml new file mode 100644 index 0000000000..13c42de50c --- /dev/null +++ b/sale_order_import/wizard/sale_order_import_view.xml @@ -0,0 +1,78 @@ + + + + + + + + sale.order.import.form + sale.order.import + +
+ + + + +
+

Upload below the customer order or request for quotation as CSV, XML or PDF file. When you click on the Import button:

+
    +
  1. If it is a CSV file, you will have to manually select the customer. The CSV file should have 2 columns: the product reference or EAN13 (1st col) and then the product quantity (2nd col). It shouldn't have any header line and use semi-colon as field separator. The quantity shouldn't use any thousand separator ; if it is a decimal value, it should use dot as the decimal separator.
  2. +
  3. If it is an XML file, Odoo will parse it if the module that adds support for this XML format is installed. For the Universal Business Language format (UBL), you should install the module sale_order_import_ubl.
  4. +
  5. If it is a PDF file, Odoo will try to find an XML file in the attachments of the PDF file and then use this XML file to create the quotation.
  6. +
+
+
+ +
+

Some quotations have been found for this customer ; one of them may correspond to the order or RFQ that you are importing. You can either select an existing quotation to update or create a new one.

+
+
+ + + + + + + + + + + + + + + +
+
+
+
+
+ + + Import RFQ or Order + sale.order.import + form + new + {'sale_order_show_amount': True} + + + + +
+
From c27761c5e4a9d84179f2fb81e79478d2a3f4da52 Mon Sep 17 00:00:00 2001 From: Alexis de Lattre Date: Fri, 2 Sep 2016 17:09:06 +0200 Subject: [PATCH 02/91] Add 2 README.rst Add another README. --- sale_order_import/README.rst | 65 ++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 sale_order_import/README.rst diff --git a/sale_order_import/README.rst b/sale_order_import/README.rst new file mode 100644 index 0000000000..a48930bf39 --- /dev/null +++ b/sale_order_import/README.rst @@ -0,0 +1,65 @@ +.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 + +================= +Sale Order Import +================= + +This module adds support for the import of electronic RFQ or orders. This module provides the base methods to import electronic orders ; it requires additionnal modules to support specific order formats: + +* module *sale_order_import_csv*: adds support for CSV orders, + +* module *sale_order_import_ubl*: adds support for `Universal Business Language (UBL) `_ RFQs and orders as: + + - XML file, + - PDF file with an embedded XML file. + +Configuration +============= + +No configuration is needed. + +Usage +===== + +This module adds a wizard in the sale menu named *Import RFQ or Order*. This wizard will propose you to select the order or RFQ file. Depending on the format of the file (CSV, XML or PDF) and the type of file (RFQ or order), it may propose you additionnal options. + +When you import an order, if there is a quotation in Odoo for the same customer, the wizard will propose you to either update the existing quotation or create a new order (in fact, it will create a new quotation, so that you are free to make some modifications before you click on the *Confirm Sale* button to convert the quotation to a sale order). + +Once the RFQ/order is imported, you should read the messages in the chatter of the quotation because it may contain important information about the import. + +.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas + :alt: Try me on Runbot + :target: https://runbot.odoo-community.org/runbot/167/8.0 + +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 smashing it by providing a detailed and welcomed feedback. + +Credits +======= + +Contributors +------------ + +* Alexis de Lattre + +Maintainer +---------- + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +This module is maintained by the OCA. + +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. + +To contribute to this module, please visit https://odoo-community.org. From 4058a8ec39bd5b8d7d483b8c4579570c575485db Mon Sep 17 00:00:00 2001 From: Alexis de Lattre Date: Sun, 2 Oct 2016 22:31:38 +0200 Subject: [PATCH 03/91] FIX Don't use _inherit = ['business.document.import'] because we want to have access to the code of the modules that inherit business.document.import --- sale_order_import/wizard/sale_order_import.py | 33 +++++++++++-------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/sale_order_import/wizard/sale_order_import.py b/sale_order_import/wizard/sale_order_import.py index 8df84d9d23..fbdd7a8980 100644 --- a/sale_order_import/wizard/sale_order_import.py +++ b/sale_order_import/wizard/sale_order_import.py @@ -15,7 +15,6 @@ class SaleOrderImport(models.TransientModel): _name = 'sale.order.import' - _inherit = ['business.document.import'] _description = 'Sale Order Import from Files' state = fields.Selection([ @@ -155,10 +154,11 @@ def parse_pdf_order(self, order_file, detect_doc_type=False): @api.model def _prepare_order(self, parsed_order, price_source): soo = self.env['sale.order'] - partner = self._match_partner( + bdio = self.env['business.document.import'] + partner = bdio._match_partner( parsed_order['partner'], parsed_order['chatter_msg'], partner_type='customer') - currency = self._match_currency( + currency = bdio._match_currency( parsed_order.get('currency'), parsed_order['chatter_msg']) if partner.property_product_pricelist.currency_id != currency: raise UserError(_( @@ -190,16 +190,16 @@ def _prepare_order(self, parsed_order, price_source): } so_vals.update(partner_change_res['value']) if parsed_order.get('ship_to'): - shipping_partner = self._match_shipping_partner( + shipping_partner = bdio._match_shipping_partner( parsed_order['ship_to'], partner, parsed_order['chatter_msg']) so_vals['partner_shipping_id'] = shipping_partner.id if parsed_order.get('date'): so_vals['date_order'] = parsed_order['date'] for line in parsed_order['lines']: # partner=False because we don't want to use product.supplierinfo - product = self._match_product( + product = bdio._match_product( line['product'], parsed_order['chatter_msg'], seller=False) - uom = self._match_uom( + uom = bdio._match_uom( line.get('uom'), parsed_order['chatter_msg'], product) line_vals = self._prepare_create_order_line( product, uom, line, price_source) @@ -211,9 +211,10 @@ def _prepare_order(self, parsed_order, price_source): @api.model def create_order(self, parsed_order, price_source): soo = self.env['sale.order'] + bdio = self.env['business.document.import'] so_vals = self._prepare_order(parsed_order, price_source) order = soo.create(so_vals) - self.post_create_or_update(parsed_order, order) + bdio.post_create_or_update(parsed_order, order) logger.info('Sale Order ID %d created', order.id) return order @@ -256,18 +257,19 @@ def parse_order(self, order_file, order_filename, partner=False): @api.multi def import_order_button(self): self.ensure_one() + bdio = self.env['business.document.import'] order_file_decoded = self.order_file.decode('base64') parsed_order = self.parse_order( order_file_decoded, self.order_filename, self.partner_id) if not parsed_order.get('lines'): raise UserError(_( "This order doesn't have any line !")) - partner = self._match_partner( + partner = bdio._match_partner( parsed_order['partner'], [], partner_type='customer') commercial_partner = partner.commercial_partner_id partner_shipping_id = False if parsed_order.get('ship_to'): - partner_shipping_id = self._match_shipping_partner( + partner_shipping_id = bdio._match_shipping_partner( parsed_order['ship_to'], partner, []).id existing_quotations = self.env['sale.order'].search([ ('commercial_partner_id', '=', commercial_partner.id), @@ -317,12 +319,13 @@ def create_order_return_action(self, parsed_order): @api.model def _prepare_update_order_vals(self, parsed_order, order, partner): - partner = self._match_partner( + bdio = self.env['business.document.import'] + partner = bdio._match_partner( parsed_order['partner'], parsed_order['chatter_msg'], partner_type='customer') vals = {'partner_id': partner.id} if parsed_order.get('ship_to'): - shipping_partner = self._match_shipping_partner( + shipping_partner = bdio._match_shipping_partner( parsed_order['ship_to'], partner, parsed_order['chatter_msg']) vals['partner_shipping_id'] = shipping_partner.id if parsed_order.get('order_ref'): @@ -346,6 +349,7 @@ def update_order_lines(self, parsed_order, order): chatter = parsed_order['chatter_msg'] solo = self.env['sale.order.line'] dpo = self.env['decimal.precision'] + bdio = self.env['business.document.import'] qty_prec = dpo.precision_get('Product UoS') price_prec = dpo.precision_get('Product Price') existing_lines = [] @@ -364,7 +368,7 @@ def update_order_lines(self, parsed_order, order): 'line': oline, 'price_unit': price_unit, }) - compare_res = self.compare_lines( + compare_res = bdio.compare_lines( existing_lines, parsed_order['lines'], chatter, qty_precision=qty_prec, seller=False) # NOW, we start to write/delete/create the order lines @@ -426,13 +430,14 @@ def update_order_lines(self, parsed_order, order): @api.multi def update_order_button(self): self.ensure_one() + bdio = self.env['business.document.import'] order = self.sale_id if not order: raise UserError(_('You must select a quotation to update.')) parsed_order = self.parse_order( self.order_file.decode('base64'), self.order_filename, self.partner_id) - currency = self._match_currency( + currency = bdio._match_currency( parsed_order.get('currency'), parsed_order['chatter_msg']) if currency != order.currency_id: raise UserError(_( @@ -444,7 +449,7 @@ def update_order_button(self): if vals: order.write(vals) self.update_order_lines(parsed_order, order) - self.post_create_or_update(parsed_order, order) + bdio.post_create_or_update(parsed_order, order) logger.info( 'Quotation ID %d updated via import of file %s', order.id, self.order_filename) From 179484864662019139f87cd36c680c5ff86a2fc4 Mon Sep 17 00:00:00 2001 From: "Adrien Peiffer (ACSONE)" Date: Tue, 4 Oct 2016 16:33:44 +0200 Subject: [PATCH 04/91] [IMP] handle the case where the xml file is generated with mime type 'text/xml' --- sale_order_import/wizard/sale_order_import.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sale_order_import/wizard/sale_order_import.py b/sale_order_import/wizard/sale_order_import.py index fbdd7a8980..9dae2fd612 100644 --- a/sale_order_import/wizard/sale_order_import.py +++ b/sale_order_import/wizard/sale_order_import.py @@ -56,7 +56,7 @@ def order_file_change(self): if filetype and filetype[0] in ('text/csv', 'text/plain'): self.csv_import = True self.doc_type = False - elif filetype and filetype[0] == 'application/xml': + elif filetype and filetype[0] in ['application/xml','text/xml']: self.csv_import = False try: xml_root = etree.fromstring( @@ -228,7 +228,7 @@ def parse_order(self, order_file, order_filename, partner=False): if not partner: raise UserError(_('Missing customer')) parsed_order = self.parse_csv_order(order_file, partner) - elif filetype == 'application/xml': + elif filetype in ['application/xml','text/xml']: try: xml_root = etree.fromstring(order_file) except: From 1fb4ad14d189ebf619e2926f6cc6e88002c8877b Mon Sep 17 00:00:00 2001 From: Alexis de Lattre Date: Tue, 4 Oct 2016 19:48:09 +0200 Subject: [PATCH 05/91] Add support for extraction + matching on website PEP8 fix --- sale_order_import/wizard/sale_order_import.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sale_order_import/wizard/sale_order_import.py b/sale_order_import/wizard/sale_order_import.py index 9dae2fd612..190bcfc2d4 100644 --- a/sale_order_import/wizard/sale_order_import.py +++ b/sale_order_import/wizard/sale_order_import.py @@ -56,7 +56,7 @@ def order_file_change(self): if filetype and filetype[0] in ('text/csv', 'text/plain'): self.csv_import = True self.doc_type = False - elif filetype and filetype[0] in ['application/xml','text/xml']: + elif filetype and filetype[0] in ['application/xml', 'text/xml']: self.csv_import = False try: xml_root = etree.fromstring( @@ -228,7 +228,7 @@ def parse_order(self, order_file, order_filename, partner=False): if not partner: raise UserError(_('Missing customer')) parsed_order = self.parse_csv_order(order_file, partner) - elif filetype in ['application/xml','text/xml']: + elif filetype in ['application/xml', 'text/xml']: try: xml_root = etree.fromstring(order_file) except: From 960b1ac2c8598cef27cd17878faae3a2863c2a33 Mon Sep 17 00:00:00 2001 From: Alexis de Lattre Date: Tue, 18 Oct 2016 23:02:55 +0200 Subject: [PATCH 06/91] 8.0 Add support for partner bank matching on invoice update (#6) Add support for partner bank matching on invoice update (before, it was only supported on invoice creation) --- sale_order_import/README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sale_order_import/README.rst b/sale_order_import/README.rst index a48930bf39..715d88de4e 100644 --- a/sale_order_import/README.rst +++ b/sale_order_import/README.rst @@ -31,13 +31,13 @@ Once the RFQ/order is imported, you should read the messages in the chatter of t .. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas :alt: Try me on Runbot - :target: https://runbot.odoo-community.org/runbot/167/8.0 + :target: https://runbot.odoo-community.org/runbot/226/8.0 Bug Tracker =========== Bugs are tracked on `GitHub Issues -`_. In case of trouble, please +`_. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us smashing it by providing a detailed and welcomed feedback. From 9bb75fdfb28e4afd23053b3d4a0b4448d83ebb89 Mon Sep 17 00:00:00 2001 From: OCA Transbot Date: Mon, 28 Nov 2016 22:27:08 -0500 Subject: [PATCH 07/91] OCA Transbot updated translations from Transifex --- sale_order_import/i18n/fr.po | 362 +++++++++++++++++++++++++++++++++++ 1 file changed, 362 insertions(+) create mode 100644 sale_order_import/i18n/fr.po diff --git a/sale_order_import/i18n/fr.po b/sale_order_import/i18n/fr.po new file mode 100644 index 0000000000..1c11edda3d --- /dev/null +++ b/sale_order_import/i18n/fr.po @@ -0,0 +1,362 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * sale_order_import +# +# Translators: +# OCA Transbot , 2016 +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 8.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2016-11-12 13:36+0000\n" +"PO-Revision-Date: 2016-11-12 13:36+0000\n" +"Last-Translator: OCA Transbot , 2016\n" +"Language-Team: French (https://www.transifex.com/oca/teams/23907/fr/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Language: fr\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" + +#. module: sale_order_import +#: code:addons/sale_order_import/models/sale.py:22 +#, python-format +msgid " Amount w/o tax: %s %s)" +msgstr "" + +#. module: sale_order_import +#: code:addons/sale_order_import/wizard/sale_order_import.py:426 +#, python-format +msgid "%d new order line(s) created: %s" +msgstr "" + +#. module: sale_order_import +#: code:addons/sale_order_import/wizard/sale_order_import.py:409 +#, python-format +msgid "%d order line(s) deleted: %s" +msgstr "" + +#. module: sale_order_import +#: code:addons/sale_order_import/wizard/sale_order_import.py:177 +#, python-format +msgid "" +"An order of customer '%s' with reference '%s' already exists: %s (state: %s)" +msgstr "" + +#. module: sale_order_import +#: field:sale.order.import,price_source:0 +msgid "Apply Prices From" +msgstr "" + +#. module: sale_order_import +#: view:sale.order.import:sale_order_import.sale_order_import_form +msgid "Cancel" +msgstr "Annuler" + +#. module: sale_order_import +#: view:sale.order.import:sale_order_import.sale_order_import_form +msgid "Create New" +msgstr "Créer une nouvelle" + +#. module: sale_order_import +#: code:addons/sale_order_import/wizard/sale_order_import.py:307 +#, python-format +msgid "Created automatically via file import (%s)." +msgstr "" + +#. module: sale_order_import +#: field:sale.order.import,create_uid:0 +msgid "Created by" +msgstr "Créé par" + +#. module: sale_order_import +#: field:sale.order.import,create_date:0 +msgid "Created on" +msgstr "Créé le" + +#. module: sale_order_import +#: field:sale.order.import,csv_import:0 +msgid "Csv import" +msgstr "" + +#. module: sale_order_import +#: field:sale.order.import,commercial_partner_id:0 +#: field:sale.order.import,partner_id:0 +msgid "Customer" +msgstr "" + +#. module: sale_order_import +#: selection:sale.order.import,price_source:0 +msgid "Customer Order" +msgstr "" + +#. module: sale_order_import +#: field:sale.order.import,display_name:0 +msgid "Display Name" +msgstr "" + +#. module: sale_order_import +#: field:sale.order.import,doc_type:0 +msgid "Document Type" +msgstr "" + +#. module: sale_order_import +#: field:sale.order.import,order_filename:0 +msgid "Filename" +msgstr "Nom du fichier" + +#. module: sale_order_import +#: field:sale.order.import,id:0 +msgid "ID" +msgstr "ID" + +#. module: sale_order_import +#: view:sale.order.import:sale_order_import.sale_order_import_form +msgid "" +"If it is a CSV file, you will have to manually select the customer. The CSV " +"file should have 2 columns: the product reference or EAN13 (1st col) and " +"then the product quantity (2nd col). It shouldn't have any header line and " +"use semi-colon as field separator. The quantity shouldn't use any thousand " +"separator ; if it is a decimal value, it should use dot as the decimal " +"separator." +msgstr "" + +#. module: sale_order_import +#: view:sale.order.import:sale_order_import.sale_order_import_form +msgid "" +"If it is a PDF file, Odoo will try to find an XML file in the attachments of" +" the PDF file and then use this XML file to create the quotation." +msgstr "" + +#. module: sale_order_import +#: view:sale.order.import:sale_order_import.sale_order_import_form +msgid "" +"If it is an XML file, Odoo will parse it if the module that adds support for" +" this XML format is installed. For the" +msgstr "" + +#. module: sale_order_import +#: view:sale.order.import:sale_order_import.sale_order_import_form +#: selection:sale.order.import,state:0 +msgid "Import" +msgstr "Importer" + +#. module: sale_order_import +#: model:ir.actions.act_window,name:sale_order_import.sale_order_import_action +#: model:ir.ui.menu,name:sale_order_import.sale_order_import_menu +msgid "Import RFQ or Order" +msgstr "" + +#. module: sale_order_import +#: view:sale.order.import:sale_order_import.sale_order_import_form +msgid "Import Sale Orders" +msgstr "" + +#. module: sale_order_import +#: field:sale.order.import,__last_update:0 +msgid "Last Modified on" +msgstr "" + +#. module: sale_order_import +#: field:sale.order.import,write_uid:0 +msgid "Last Updated by" +msgstr "Dernière mise-à-jour par" + +#. module: sale_order_import +#: field:sale.order.import,write_date:0 +msgid "Last Updated on" +msgstr "Dernière mise-à-jour le" + +#. module: sale_order_import +#: code:addons/sale_order_import/wizard/sale_order_import.py:229 +#, python-format +msgid "Missing customer" +msgstr "" + +#. module: sale_order_import +#: selection:sale.order.import,price_source:0 +msgid "Pricelist" +msgstr "" + +#. module: sale_order_import +#: field:sale.order.import,sale_id:0 +msgid "Quotation to Update" +msgstr "" + +#. module: sale_order_import +#: selection:sale.order.import,doc_type:0 +msgid "Request For Quotation" +msgstr "" + +#. module: sale_order_import +#: field:sale.order.import,order_file:0 +msgid "Request for Quotation or Order" +msgstr "" + +#. module: sale_order_import +#: selection:sale.order.import,doc_type:0 +msgid "Sale Order" +msgstr "" + +#. module: sale_order_import +#: model:ir.model,name:sale_order_import.model_sale_order_import +msgid "Sale Order Import from Files" +msgstr "" + +#. module: sale_order_import +#: model:ir.model,name:sale_order_import.model_sale_order +msgid "Sales Order" +msgstr "" + +#. module: sale_order_import +#: field:sale.order.import,partner_shipping_id:0 +msgid "Shipping Address" +msgstr "" + +#. module: sale_order_import +#: view:sale.order.import:sale_order_import.sale_order_import_form +msgid "" +"Some quotations have been found for this customer ; one of them may " +"correspond to the order or RFQ that you are importing. You can either select" +" an existing quotation to update or create a new one." +msgstr "" + +#. module: sale_order_import +#: field:sale.order.import,state:0 +msgid "State" +msgstr "Etat" + +#. module: sale_order_import +#: code:addons/sale_order_import/wizard/sale_order_import.py:443 +#, python-format +msgid "" +"The currency of the imported order (%s) is different from the currency of " +"the existing order (%s)" +msgstr "" + +#. module: sale_order_import +#: code:addons/sale_order_import/wizard/sale_order_import.py:164 +#, python-format +msgid "" +"The customer '%s' has a pricelist '%s' but the currency of this order is " +"'%s'." +msgstr "" + +#. module: sale_order_import +#: code:addons/sale_order_import/wizard/sale_order_import.py:379 +#, python-format +msgid "" +"The quantity has been updated on the order line with product '%s' from %s to" +" %s %s" +msgstr "" + +#. module: sale_order_import +#: code:addons/sale_order_import/wizard/sale_order_import.py:395 +#, python-format +msgid "" +"The unit price has been updated on the order line with product '%s' from %s " +"to %s %s" +msgstr "" + +#. module: sale_order_import +#: code:addons/sale_order_import/wizard/sale_order_import.py:109 +#, python-format +msgid "There are no embedded XML file in this PDF file." +msgstr "" + +#. module: sale_order_import +#: code:addons/sale_order_import/wizard/sale_order_import.py:65 +#: code:addons/sale_order_import/wizard/sale_order_import.py:235 +#, python-format +msgid "This XML file is not XML-compliant" +msgstr "" + +#. module: sale_order_import +#: code:addons/sale_order_import/wizard/sale_order_import.py:76 +#: code:addons/sale_order_import/wizard/sale_order_import.py:245 +#, python-format +msgid "" +"This file '%s' is not recognised as a CSV, XML nor PDF file. Please check " +"the file and it's extension." +msgstr "" + +#. module: sale_order_import +#: code:addons/sale_order_import/wizard/sale_order_import.py:265 +#, python-format +msgid "This order doesn't have any line !" +msgstr "" + +#. module: sale_order_import +#: code:addons/sale_order_import/wizard/sale_order_import.py:456 +#, python-format +msgid "" +"This quotation has been updated automatically via the import of file %s" +msgstr "" + +#. module: sale_order_import +#: code:addons/sale_order_import/wizard/sale_order_import.py:98 +#, python-format +msgid "" +"This type of CSV order is not supported. Did you install the module to " +"support CSV orders?" +msgstr "" + +#. module: sale_order_import +#: code:addons/sale_order_import/wizard/sale_order_import.py:91 +#: code:addons/sale_order_import/wizard/sale_order_import.py:119 +#, python-format +msgid "" +"This type of XML RFQ/order is not supported. Did you install the module to " +"support this XML format?" +msgstr "" + +#. module: sale_order_import +#: view:sale.order.import:sale_order_import.sale_order_import_form +msgid "Universal Business Language" +msgstr "" + +#. module: sale_order_import +#: code:addons/sale_order_import/wizard/sale_order_import.py:75 +#, python-format +msgid "Unsupported file format" +msgstr "" + +#. module: sale_order_import +#: selection:sale.order.import,state:0 +msgid "Update" +msgstr "Mettre à jour" + +#. module: sale_order_import +#: view:sale.order.import:sale_order_import.sale_order_import_form +msgid "Update Existing" +msgstr "Mettre à jour une existante" + +#. module: sale_order_import +#: help:sale.order.import,order_file:0 +msgid "" +"Upload a Request for Quotation or an Order file. Supported formats: CSV, XML" +" and PDF (PDF with an embeded XML file)." +msgstr "" + +#. module: sale_order_import +#: view:sale.order.import:sale_order_import.sale_order_import_form +msgid "" +"Upload below the customer order or request for quotation as CSV, XML or PDF " +"file. When you click on the Import button:" +msgstr "" + +#. module: sale_order_import +#: code:addons/sale_order_import/wizard/sale_order_import.py:436 +#, python-format +msgid "You must select a quotation to update." +msgstr "" + +#. module: sale_order_import +#: view:sale.order.import:sale_order_import.sale_order_import_form +msgid "format (UBL), you should install the module" +msgstr "" + +#. module: sale_order_import +#: view:sale.order.import:sale_order_import.sale_order_import_form +msgid "sale_order_import_ubl" +msgstr "" From 0b1630f3726fce3bff0ad4e4fa5fe83ced1adf43 Mon Sep 17 00:00:00 2001 From: Alexis de Lattre Date: Wed, 15 Feb 2017 15:11:22 +0100 Subject: [PATCH 08/91] Prepare v10 branch Rename __openerp__.py to __manifest__.py and set installable to False --- sale_order_import/{__openerp__.py => __manifest__.py} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename sale_order_import/{__openerp__.py => __manifest__.py} (95%) diff --git a/sale_order_import/__openerp__.py b/sale_order_import/__manifest__.py similarity index 95% rename from sale_order_import/__openerp__.py rename to sale_order_import/__manifest__.py index 4da4c028c2..6b2eab8412 100644 --- a/sale_order_import/__openerp__.py +++ b/sale_order_import/__manifest__.py @@ -14,5 +14,5 @@ 'data': [ 'wizard/sale_order_import_view.xml', ], - 'installable': True, + 'installable': False, } From 6442a7e45063d7e3ee1cf16890e75b7921b19572 Mon Sep 17 00:00:00 2001 From: Alexis de Lattre Date: Mon, 27 Feb 2017 23:23:59 +0100 Subject: [PATCH 09/91] Continue port of modules for v10.0, in particular sale_order_import_* module Fix spelling mistake and other remarks on README by Tarteo --- sale_order_import/README.rst | 6 +++--- sale_order_import/__manifest__.py | 12 ++++++++---- sale_order_import/models/sale.py | 5 ++--- sale_order_import/wizard/sale_order_import.py | 17 ++++++++--------- .../wizard/sale_order_import_view.xml | 12 +++++------- 5 files changed, 26 insertions(+), 26 deletions(-) diff --git a/sale_order_import/README.rst b/sale_order_import/README.rst index 715d88de4e..b2973a213d 100644 --- a/sale_order_import/README.rst +++ b/sale_order_import/README.rst @@ -6,7 +6,7 @@ Sale Order Import ================= -This module adds support for the import of electronic RFQ or orders. This module provides the base methods to import electronic orders ; it requires additionnal modules to support specific order formats: +This module adds support for the import of electronic RFQ or orders. This module provides the base methods to import electronic orders ; it requires additional modules to support specific order formats: * module *sale_order_import_csv*: adds support for CSV orders, @@ -23,7 +23,7 @@ No configuration is needed. Usage ===== -This module adds a wizard in the sale menu named *Import RFQ or Order*. This wizard will propose you to select the order or RFQ file. Depending on the format of the file (CSV, XML or PDF) and the type of file (RFQ or order), it may propose you additionnal options. +This module adds a wizard in the sale menu named *Import RFQ or Order*. This wizard will propose you to select the order or RFQ file. Depending on the format of the file (CSV, XML or PDF) and the type of file (RFQ or order), it may propose you additional options. When you import an order, if there is a quotation in Odoo for the same customer, the wizard will propose you to either update the existing quotation or create a new order (in fact, it will create a new quotation, so that you are free to make some modifications before you click on the *Confirm Sale* button to convert the quotation to a sale order). @@ -31,7 +31,7 @@ Once the RFQ/order is imported, you should read the messages in the chatter of t .. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas :alt: Try me on Runbot - :target: https://runbot.odoo-community.org/runbot/226/8.0 + :target: https://runbot.odoo-community.org/runbot/226/10.0 Bug Tracker =========== diff --git a/sale_order_import/__manifest__.py b/sale_order_import/__manifest__.py index 6b2eab8412..3e79e11cbf 100644 --- a/sale_order_import/__manifest__.py +++ b/sale_order_import/__manifest__.py @@ -1,18 +1,22 @@ # -*- coding: utf-8 -*- -# © 2016 Akretion (Alexis de Lattre ) +# © 2016-2017 Akretion (Alexis de Lattre ) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). { 'name': 'Sale Order Import', - 'version': '8.0.1.0.0', + 'version': '10.0.1.0.0', 'category': 'Sales Management', 'license': 'AGPL-3', 'summary': 'Import RFQ or sale orders from files', 'author': 'Akretion,Odoo Community Association (OCA)', 'website': 'http://www.akretion.com', - 'depends': ['sale_commercial_partner', 'base_business_document_import'], + 'depends': [ + 'sale_commercial_partner', + 'base_business_document_import', + 'onchange_helper', + ], 'data': [ 'wizard/sale_order_import_view.xml', ], - 'installable': False, + 'installable': True, } diff --git a/sale_order_import/models/sale.py b/sale_order_import/models/sale.py index 1a8123809c..0fab2cd14e 100644 --- a/sale_order_import/models/sale.py +++ b/sale_order_import/models/sale.py @@ -1,14 +1,13 @@ # -*- coding: utf-8 -*- -# © 2016 Akretion (Alexis de Lattre ) +# © 2016-2017 Akretion (Alexis de Lattre ) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from openerp import models, api, _ +from odoo import models, api, _ class SaleOrder(models.Model): _inherit = 'sale.order' - @api.multi def name_get(self): """Add amount_untaxed in name_get of sale orders""" res = super(SaleOrder, self).name_get() diff --git a/sale_order_import/wizard/sale_order_import.py b/sale_order_import/wizard/sale_order_import.py index 190bcfc2d4..eae871e3c6 100644 --- a/sale_order_import/wizard/sale_order_import.py +++ b/sale_order_import/wizard/sale_order_import.py @@ -1,11 +1,11 @@ # -*- coding: utf-8 -*- -# © 2016 Akretion (Alexis de Lattre ) +# © 2016-2017 Akretion (Alexis de Lattre ) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from openerp import models, fields, api, _ +from odoo import models, fields, api, _ # import openerp.addons.decimal_precision as dp -from openerp.tools import float_compare, float_is_zero -from openerp.exceptions import Warning as UserError +from odoo.tools import float_compare, float_is_zero +from odoo.exceptions import UserError import logging import mimetypes from lxml import etree @@ -181,14 +181,13 @@ def _prepare_order(self, parsed_order, price_source): parsed_order['order_ref'], existing_orders[0].name, existing_orders[0].state)) - partner_change_res = soo.onchange_partner_id(partner.id) - assert 'value' in partner_change_res, 'Error in partner change' + so_vals = { 'partner_id': partner.id, 'client_order_ref': parsed_order.get('order_ref'), - 'order_line': [] } - so_vals.update(partner_change_res['value']) + so_vals = soo.play_onchanges(so_vals, ['partner_id']) + so_vals['order_line'] = [] if parsed_order.get('ship_to'): shipping_partner = bdio._match_shipping_partner( parsed_order['ship_to'], partner, parsed_order['chatter_msg']) @@ -204,7 +203,7 @@ def _prepare_order(self, parsed_order, price_source): line_vals = self._prepare_create_order_line( product, uom, line, price_source) # product_id_change is played in the inherit of create() - # of sale.order.line cf odoo/addons/sale/sale.py + # of sale.order.line cf odoo/addons/sale/models/sale.py so_vals['order_line'].append((0, 0, line_vals)) return so_vals diff --git a/sale_order_import/wizard/sale_order_import_view.xml b/sale_order_import/wizard/sale_order_import_view.xml index 13c42de50c..b350e8e0d9 100644 --- a/sale_order_import/wizard/sale_order_import_view.xml +++ b/sale_order_import/wizard/sale_order_import_view.xml @@ -1,11 +1,10 @@ - - + sale.order.import.form @@ -71,8 +70,7 @@ + parent="sales_team.menu_sales" + action="sale_order_import_action" sequence="12"/> - - + From 7b27d60fcc63ad3eff0a4d0da9a63caa340b78f9 Mon Sep 17 00:00:00 2001 From: Alexis de Lattre Date: Tue, 28 Feb 2017 09:24:46 +0100 Subject: [PATCH 10/91] Port account_invoice_import_factur-x and account_invoice_import_ubl to v10.0 base_business_document_import: Add support for the creation of res.bank --- sale_order_import/models/sale.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sale_order_import/models/sale.py b/sale_order_import/models/sale.py index 0fab2cd14e..9ff3b1efb8 100644 --- a/sale_order_import/models/sale.py +++ b/sale_order_import/models/sale.py @@ -2,7 +2,7 @@ # © 2016-2017 Akretion (Alexis de Lattre ) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import models, api, _ +from odoo import models, _ class SaleOrder(models.Model): From edfc80ad61b752033da4ee0bd9c2a3ac9103de06 Mon Sep 17 00:00:00 2001 From: Alexis de Lattre Date: Tue, 28 Feb 2017 21:51:43 +0100 Subject: [PATCH 11/91] Port purchase_order_import* to v10.0 Add ubl invoice generation option in accounting config page --- sale_order_import/wizard/sale_order_import.py | 1 - 1 file changed, 1 deletion(-) diff --git a/sale_order_import/wizard/sale_order_import.py b/sale_order_import/wizard/sale_order_import.py index eae871e3c6..dcb9137ed6 100644 --- a/sale_order_import/wizard/sale_order_import.py +++ b/sale_order_import/wizard/sale_order_import.py @@ -3,7 +3,6 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from odoo import models, fields, api, _ -# import openerp.addons.decimal_precision as dp from odoo.tools import float_compare, float_is_zero from odoo.exceptions import UserError import logging From 02d573b32579223168f8c09c99e5cd01276f8fee Mon Sep 17 00:00:00 2001 From: Alexis de Lattre Date: Fri, 31 Mar 2017 20:07:05 +0200 Subject: [PATCH 12/91] FIX crash when pivot format had a 'note' key (and no attachment) Add method to create SO in sale.order.import accessible via JSON-RPC --- sale_order_import/wizard/sale_order_import.py | 25 ++++++++++++++----- 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/sale_order_import/wizard/sale_order_import.py b/sale_order_import/wizard/sale_order_import.py index dcb9137ed6..a40a1d4b58 100644 --- a/sale_order_import/wizard/sale_order_import.py +++ b/sale_order_import/wizard/sale_order_import.py @@ -207,15 +207,25 @@ def _prepare_order(self, parsed_order, price_source): return so_vals @api.model - def create_order(self, parsed_order, price_source): + def create_order(self, parsed_order, price_source, order_filename=None): soo = self.env['sale.order'] bdio = self.env['business.document.import'] so_vals = self._prepare_order(parsed_order, price_source) order = soo.create(so_vals) - bdio.post_create_or_update(parsed_order, order) + bdio.post_create_or_update( + parsed_order, order, doc_filename=order_filename) logger.info('Sale Order ID %d created', order.id) return order + @api.model + def create_order_ws(self, parsed_order, price_source, order_filename=None): + """Same method as create_order() but callable via JSON-RPC + webservice. Returns an ID to avoid this error: + TypeError: sale.order(15,) is not JSON serializable""" + order = self.create_order( + parsed_order, price_source, order_filename=order_filename) + return order.id + @api.model def parse_order(self, order_file, order_filename, partner=False): assert order_file, 'Missing order file' @@ -288,7 +298,8 @@ def import_order_button(self): action['res_id'] = self.id return action else: - return self.create_order_return_action(parsed_order) + return self.create_order_return_action( + parsed_order, self.order_filename) @api.multi def create_order_button(self): @@ -296,12 +307,14 @@ def create_order_button(self): parsed_order = self.parse_order( self.order_file.decode('base64'), self.order_filename, self.partner_id) - return self.create_order_return_action(parsed_order) + return self.create_order_return_action( + parsed_order, self.order_filename) @api.multi - def create_order_return_action(self, parsed_order): + def create_order_return_action(self, parsed_order, order_filename): self.ensure_one() - order = self.create_order(parsed_order, self.price_source) + order = self.create_order( + parsed_order, self.price_source, order_filename) order.message_post(_( "Created automatically via file import (%s).") % self.order_filename) From ebc93cca6e7723d8080b403c9b1dc7924a16cd27 Mon Sep 17 00:00:00 2001 From: OCA Transbot Date: Sat, 31 Mar 2018 05:31:32 +0200 Subject: [PATCH 13/91] OCA Transbot updated translations from Transifex --- sale_order_import/i18n/es.po | 358 +++++++++++++++++++++++++++++++++++ 1 file changed, 358 insertions(+) create mode 100644 sale_order_import/i18n/es.po diff --git a/sale_order_import/i18n/es.po b/sale_order_import/i18n/es.po new file mode 100644 index 0000000000..2e12eb913f --- /dev/null +++ b/sale_order_import/i18n/es.po @@ -0,0 +1,358 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * sale_order_import +# +# Translators: +# enjolras , 2018 +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 10.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2018-03-12 01:43+0000\n" +"PO-Revision-Date: 2018-03-12 01:43+0000\n" +"Last-Translator: enjolras , 2018\n" +"Language-Team: Spanish (https://www.transifex.com/oca/teams/23907/es/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Language: es\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#. module: sale_order_import +#: code:addons/sale_order_import/models/sale.py:21 +#, python-format +msgid " Amount w/o tax: %s %s)" +msgstr "" + +#. module: sale_order_import +#: code:addons/sale_order_import/wizard/sale_order_import.py:437 +#, python-format +msgid "%d new order line(s) created: %s" +msgstr "" + +#. module: sale_order_import +#: code:addons/sale_order_import/wizard/sale_order_import.py:420 +#, python-format +msgid "%d order line(s) deleted: %s" +msgstr "" + +#. module: sale_order_import +#: code:addons/sale_order_import/wizard/sale_order_import.py:176 +#, python-format +msgid "" +"An order of customer '%s' with reference '%s' already exists: %s (state: %s)" +msgstr "" + +#. module: sale_order_import +#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import_price_source +msgid "Apply Prices From" +msgstr "" + +#. module: sale_order_import +#: model:ir.ui.view,arch_db:sale_order_import.sale_order_import_form +msgid "Cancel" +msgstr "Cancelar" + +#. module: sale_order_import +#: model:ir.ui.view,arch_db:sale_order_import.sale_order_import_form +msgid "Create New" +msgstr "Crear nuevo" + +#. module: sale_order_import +#: code:addons/sale_order_import/wizard/sale_order_import.py:318 +#, python-format +msgid "Created automatically via file import (%s)." +msgstr "" + +#. module: sale_order_import +#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import_create_uid +msgid "Created by" +msgstr "Creado por" + +#. module: sale_order_import +#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import_create_date +msgid "Created on" +msgstr "Creado el" + +#. module: sale_order_import +#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import_csv_import +msgid "Csv import" +msgstr "Importar CSV" + +#. module: sale_order_import +#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import_commercial_partner_id +#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import_partner_id +msgid "Customer" +msgstr "Cliente" + +#. module: sale_order_import +#: selection:sale.order.import,price_source:0 +msgid "Customer Order" +msgstr "Pedido del cliente" + +#. module: sale_order_import +#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import_display_name +msgid "Display Name" +msgstr "Nombre mostrado" + +#. module: sale_order_import +#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import_doc_type +msgid "Document Type" +msgstr "Tipo de documento" + +#. module: sale_order_import +#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import_order_filename +msgid "Filename" +msgstr "Archivo" + +#. module: sale_order_import +#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import_id +msgid "ID" +msgstr "ID" + +#. module: sale_order_import +#: model:ir.ui.view,arch_db:sale_order_import.sale_order_import_form +msgid "" +"If it is a CSV file, you will have to manually select the customer. The CSV " +"file should have 2 columns: the product reference or EAN13 (1st col) and " +"then the product quantity (2nd col). It shouldn't have any header line and " +"use semi-colon as field separator. The quantity shouldn't use any thousand " +"separator ; if it is a decimal value, it should use dot as the decimal " +"separator." +msgstr "" + +#. module: sale_order_import +#: model:ir.ui.view,arch_db:sale_order_import.sale_order_import_form +msgid "" +"If it is a PDF file, Odoo will try to find an XML file in the attachments of" +" the PDF file and then use this XML file to create the quotation." +msgstr "" + +#. module: sale_order_import +#: model:ir.ui.view,arch_db:sale_order_import.sale_order_import_form +msgid "" +"If it is an XML file, Odoo will parse it if the module that adds support for" +" this XML format is installed. For the" +msgstr "" + +#. module: sale_order_import +#: model:ir.ui.view,arch_db:sale_order_import.sale_order_import_form +#: selection:sale.order.import,state:0 +msgid "Import" +msgstr "Importar" + +#. module: sale_order_import +#: model:ir.actions.act_window,name:sale_order_import.sale_order_import_action +#: model:ir.ui.menu,name:sale_order_import.sale_order_import_menu +msgid "Import RFQ or Order" +msgstr "Importar solicitud de presupuesto o pedido" + +#. module: sale_order_import +#: model:ir.ui.view,arch_db:sale_order_import.sale_order_import_form +msgid "Import Sale Orders" +msgstr "Importar pedidos de venta" + +#. module: sale_order_import +#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import___last_update +msgid "Last Modified on" +msgstr "Última modificación el" + +#. module: sale_order_import +#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import_write_uid +msgid "Last Updated by" +msgstr "Última actualización por" + +#. module: sale_order_import +#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import_write_date +msgid "Last Updated on" +msgstr "Última actualización el" + +#. module: sale_order_import +#: code:addons/sale_order_import/wizard/sale_order_import.py:237 +#, python-format +msgid "Missing customer" +msgstr "Falta cliente" + +#. module: sale_order_import +#: selection:sale.order.import,price_source:0 +msgid "Pricelist" +msgstr "Tarifa" + +#. module: sale_order_import +#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import_sale_id +msgid "Quotation to Update" +msgstr "Presupuesto para actualizar" + +#. module: sale_order_import +#: selection:sale.order.import,doc_type:0 +msgid "Request For Quotation" +msgstr "Solicitud de presupuesto" + +#. module: sale_order_import +#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import_order_file +msgid "Request for Quotation or Order" +msgstr "Solicitud de presupuesto o pedido" + +#. module: sale_order_import +#: selection:sale.order.import,doc_type:0 +msgid "Sale Order" +msgstr "Pedido de venta" + +#. module: sale_order_import +#: model:ir.model,name:sale_order_import.model_sale_order_import +msgid "Sale Order Import from Files" +msgstr "" + +#. module: sale_order_import +#: model:ir.model,name:sale_order_import.model_sale_order +msgid "Sales Order" +msgstr "Pedido de venta" + +#. module: sale_order_import +#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import_partner_shipping_id +msgid "Shipping Address" +msgstr "Dirección de envío" + +#. module: sale_order_import +#: model:ir.ui.view,arch_db:sale_order_import.sale_order_import_form +msgid "" +"Some quotations have been found for this customer ; one of them may " +"correspond to the order or RFQ that you are importing. You can either select" +" an existing quotation to update or create a new one." +msgstr "" + +#. module: sale_order_import +#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import_state +msgid "State" +msgstr "Estado" + +#. module: sale_order_import +#: code:addons/sale_order_import/wizard/sale_order_import.py:454 +#, python-format +msgid "" +"The currency of the imported order (%s) is different from the currency of " +"the existing order (%s)" +msgstr "" + +#. module: sale_order_import +#: code:addons/sale_order_import/wizard/sale_order_import.py:163 +#, python-format +msgid "" +"The customer '%s' has a pricelist '%s' but the currency of this order is " +"'%s'." +msgstr "" + +#. module: sale_order_import +#: code:addons/sale_order_import/wizard/sale_order_import.py:390 +#, python-format +msgid "" +"The quantity has been updated on the order line with product '%s' from %s to" +" %s %s" +msgstr "" + +#. module: sale_order_import +#: code:addons/sale_order_import/wizard/sale_order_import.py:406 +#, python-format +msgid "" +"The unit price has been updated on the order line with product '%s' from %s " +"to %s %s" +msgstr "" + +#. module: sale_order_import +#: code:addons/sale_order_import/wizard/sale_order_import.py:108 +#, python-format +msgid "There are no embedded XML file in this PDF file." +msgstr "" + +#. module: sale_order_import +#: code:addons/sale_order_import/wizard/sale_order_import.py:64 +#: code:addons/sale_order_import/wizard/sale_order_import.py:243 +#, python-format +msgid "This XML file is not XML-compliant" +msgstr "" + +#. module: sale_order_import +#: code:addons/sale_order_import/wizard/sale_order_import.py:75 +#: code:addons/sale_order_import/wizard/sale_order_import.py:253 +#, python-format +msgid "" +"This file '%s' is not recognised as a CSV, XML nor PDF file. Please check " +"the file and it's extension." +msgstr "" + +#. module: sale_order_import +#: code:addons/sale_order_import/wizard/sale_order_import.py:273 +#, python-format +msgid "This order doesn't have any line !" +msgstr "" + +#. module: sale_order_import +#: code:addons/sale_order_import/wizard/sale_order_import.py:467 +#, python-format +msgid "" +"This quotation has been updated automatically via the import of file %s" +msgstr "" + +#. module: sale_order_import +#: code:addons/sale_order_import/wizard/sale_order_import.py:97 +#, python-format +msgid "" +"This type of CSV order is not supported. Did you install the module to " +"support CSV orders?" +msgstr "" + +#. module: sale_order_import +#: code:addons/sale_order_import/wizard/sale_order_import.py:90 +#: code:addons/sale_order_import/wizard/sale_order_import.py:118 +#, python-format +msgid "" +"This type of XML RFQ/order is not supported. Did you install the module to " +"support this XML format?" +msgstr "" + +#. module: sale_order_import +#: model:ir.ui.view,arch_db:sale_order_import.sale_order_import_form +msgid "Universal Business Language" +msgstr "Universal Business Language" + +#. module: sale_order_import +#: code:addons/sale_order_import/wizard/sale_order_import.py:74 +#, python-format +msgid "Unsupported file format" +msgstr "Formato de archivo no soportado" + +#. module: sale_order_import +#: selection:sale.order.import,state:0 +msgid "Update" +msgstr "Actualizar" + +#. module: sale_order_import +#: model:ir.ui.view,arch_db:sale_order_import.sale_order_import_form +msgid "Update Existing" +msgstr "Actualizar existente" + +#. module: sale_order_import +#: model:ir.model.fields,help:sale_order_import.field_sale_order_import_order_file +msgid "" +"Upload a Request for Quotation or an Order file. Supported formats: CSV, XML" +" and PDF (PDF with an embeded XML file)." +msgstr "" + +#. module: sale_order_import +#: model:ir.ui.view,arch_db:sale_order_import.sale_order_import_form +msgid "" +"Upload below the customer order or request for quotation as CSV, XML or PDF " +"file. When you click on the Import button:" +msgstr "" + +#. module: sale_order_import +#: code:addons/sale_order_import/wizard/sale_order_import.py:447 +#, python-format +msgid "You must select a quotation to update." +msgstr "" + +#. module: sale_order_import +#: model:ir.ui.view,arch_db:sale_order_import.sale_order_import_form +msgid "" +"format (UBL), you should install the module sale_order_import_ubl." +msgstr "" From 887f714a41f7369020ace8972b9e9660ebeebb32 Mon Sep 17 00:00:00 2001 From: tarteo Date: Thu, 17 May 2018 15:26:18 +0200 Subject: [PATCH 14/91] [11.0][MIG] sale_order_import: Migration + add tests Add readme fragments --- sale_order_import/README.rst | 67 ++- sale_order_import/__init__.py | 2 - sale_order_import/__manifest__.py | 5 +- sale_order_import/models/__init__.py | 2 - sale_order_import/models/sale.py | 1 - sale_order_import/readme/CONTRIBUTORS.rst | 2 + sale_order_import/readme/DESCRIPTION.rst | 8 + sale_order_import/readme/USAGE.rst | 5 + .../static/description/index.html | 435 ++++++++++++++++++ sale_order_import/tests/__init__.py | 1 + sale_order_import/tests/test_sale_order.py | 16 + sale_order_import/wizard/__init__.py | 2 - sale_order_import/wizard/sale_order_import.py | 20 +- .../wizard/sale_order_import_view.xml | 2 +- 14 files changed, 526 insertions(+), 42 deletions(-) create mode 100644 sale_order_import/readme/CONTRIBUTORS.rst create mode 100644 sale_order_import/readme/DESCRIPTION.rst create mode 100644 sale_order_import/readme/USAGE.rst create mode 100644 sale_order_import/static/description/index.html create mode 100644 sale_order_import/tests/__init__.py create mode 100644 sale_order_import/tests/test_sale_order.py diff --git a/sale_order_import/README.rst b/sale_order_import/README.rst index b2973a213d..32e959db7d 100644 --- a/sale_order_import/README.rst +++ b/sale_order_import/README.rst @@ -1,11 +1,30 @@ -.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg - :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html - :alt: License: AGPL-3 - ================= Sale Order Import ================= +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fedi-lightgray.png?logo=github + :target: https://github.com/OCA/edi/tree/11.0/sale_order_import + :alt: OCA/edi +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/edi-11-0/edi-11-0-sale_order_import + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/226/11.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| + This module adds support for the import of electronic RFQ or orders. This module provides the base methods to import electronic orders ; it requires additional modules to support specific order formats: * module *sale_order_import_csv*: adds support for CSV orders, @@ -15,10 +34,10 @@ This module adds support for the import of electronic RFQ or orders. This module - XML file, - PDF file with an embedded XML file. -Configuration -============= +**Table of contents** -No configuration is needed. +.. contents:: + :local: Usage ===== @@ -29,37 +48,43 @@ When you import an order, if there is a quotation in Odoo for the same customer, Once the RFQ/order is imported, you should read the messages in the chatter of the quotation because it may contain important information about the import. -.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas - :alt: Try me on Runbot - :target: https://runbot.odoo-community.org/runbot/226/10.0 - 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 smashing it by providing a detailed and welcomed feedback. +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 smashing it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. Credits ======= +Authors +~~~~~~~ + +* Akretion + Contributors ------------- +~~~~~~~~~~~~ * Alexis de Lattre +* Dennis Sluijk -Maintainer ----------- +Maintainers +~~~~~~~~~~~ + +This module is maintained by the OCA. .. image:: https://odoo-community.org/logo.png :alt: Odoo Community Association :target: https://odoo-community.org -This module is maintained by the OCA. - 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. -To contribute to this module, please visit https://odoo-community.org. +This module is part of the `OCA/edi `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/sale_order_import/__init__.py b/sale_order_import/__init__.py index 3c4e748f01..134df27435 100644 --- a/sale_order_import/__init__.py +++ b/sale_order_import/__init__.py @@ -1,4 +1,2 @@ -# -*- coding: utf-8 -*- - from . import wizard from . import models diff --git a/sale_order_import/__manifest__.py b/sale_order_import/__manifest__.py index 3e79e11cbf..486d203771 100644 --- a/sale_order_import/__manifest__.py +++ b/sale_order_import/__manifest__.py @@ -1,15 +1,14 @@ -# -*- coding: utf-8 -*- # © 2016-2017 Akretion (Alexis de Lattre ) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). { 'name': 'Sale Order Import', - 'version': '10.0.1.0.0', + 'version': '11.0.1.0.0', 'category': 'Sales Management', 'license': 'AGPL-3', 'summary': 'Import RFQ or sale orders from files', 'author': 'Akretion,Odoo Community Association (OCA)', - 'website': 'http://www.akretion.com', + 'website': 'https://github.com/oca/edi', 'depends': [ 'sale_commercial_partner', 'base_business_document_import', diff --git a/sale_order_import/models/__init__.py b/sale_order_import/models/__init__.py index 78a9604960..8a0dc04e1f 100644 --- a/sale_order_import/models/__init__.py +++ b/sale_order_import/models/__init__.py @@ -1,3 +1 @@ -# -*- coding: utf-8 -*- - from . import sale diff --git a/sale_order_import/models/sale.py b/sale_order_import/models/sale.py index 9ff3b1efb8..4ea663c0ff 100644 --- a/sale_order_import/models/sale.py +++ b/sale_order_import/models/sale.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # © 2016-2017 Akretion (Alexis de Lattre ) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). diff --git a/sale_order_import/readme/CONTRIBUTORS.rst b/sale_order_import/readme/CONTRIBUTORS.rst new file mode 100644 index 0000000000..97c5fe672b --- /dev/null +++ b/sale_order_import/readme/CONTRIBUTORS.rst @@ -0,0 +1,2 @@ +* Alexis de Lattre +* Dennis Sluijk diff --git a/sale_order_import/readme/DESCRIPTION.rst b/sale_order_import/readme/DESCRIPTION.rst new file mode 100644 index 0000000000..722fb59913 --- /dev/null +++ b/sale_order_import/readme/DESCRIPTION.rst @@ -0,0 +1,8 @@ +This module adds support for the import of electronic RFQ or orders. This module provides the base methods to import electronic orders ; it requires additional modules to support specific order formats: + +* module *sale_order_import_csv*: adds support for CSV orders, + +* module *sale_order_import_ubl*: adds support for `Universal Business Language (UBL) `_ RFQs and orders as: + + - XML file, + - PDF file with an embedded XML file. diff --git a/sale_order_import/readme/USAGE.rst b/sale_order_import/readme/USAGE.rst new file mode 100644 index 0000000000..76f4589175 --- /dev/null +++ b/sale_order_import/readme/USAGE.rst @@ -0,0 +1,5 @@ +This module adds a wizard in the sale menu named *Import RFQ or Order*. This wizard will propose you to select the order or RFQ file. Depending on the format of the file (CSV, XML or PDF) and the type of file (RFQ or order), it may propose you additional options. + +When you import an order, if there is a quotation in Odoo for the same customer, the wizard will propose you to either update the existing quotation or create a new order (in fact, it will create a new quotation, so that you are free to make some modifications before you click on the *Confirm Sale* button to convert the quotation to a sale order). + +Once the RFQ/order is imported, you should read the messages in the chatter of the quotation because it may contain important information about the import. diff --git a/sale_order_import/static/description/index.html b/sale_order_import/static/description/index.html new file mode 100644 index 0000000000..53baadf52c --- /dev/null +++ b/sale_order_import/static/description/index.html @@ -0,0 +1,435 @@ + + + + + + +Sale Order Import + + + +
+

Sale Order Import

+ + +

Beta License: AGPL-3 OCA/edi Translate me on Weblate Try me on Runbot

+

This module adds support for the import of electronic RFQ or orders. This module provides the base methods to import electronic orders ; it requires additional modules to support specific order formats:

+
    +
  • module sale_order_import_csv: adds support for CSV orders,
  • +
  • module sale_order_import_ubl: adds support for Universal Business Language (UBL) RFQs and orders as:
      +
    • XML file,
    • +
    • PDF file with an embedded XML file.
    • +
    +
  • +
+

Table of contents

+ +
+

Usage

+

This module adds a wizard in the sale menu named Import RFQ or Order. This wizard will propose you to select the order or RFQ file. Depending on the format of the file (CSV, XML or PDF) and the type of file (RFQ or order), it may propose you additional options.

+

When you import an order, if there is a quotation in Odoo for the same customer, the wizard will propose you to either update the existing quotation or create a new order (in fact, it will create a new quotation, so that you are free to make some modifications before you click on the Confirm Sale button to convert the quotation to a sale order).

+

Once the RFQ/order is imported, you should read the messages in the chatter of the quotation because it may contain important information about the import.

+
+
+

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 smashing it by providing a detailed and welcomed +feedback.

+

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

+
+
+

Credits

+
+

Authors

+
    +
  • Akretion
  • +
+
+
+

Contributors

+ +
+
+

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/edi project on GitHub.

+

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

+
+
+
+ + diff --git a/sale_order_import/tests/__init__.py b/sale_order_import/tests/__init__.py new file mode 100644 index 0000000000..6f699d0d8b --- /dev/null +++ b/sale_order_import/tests/__init__.py @@ -0,0 +1 @@ +from . import test_sale_order diff --git a/sale_order_import/tests/test_sale_order.py b/sale_order_import/tests/test_sale_order.py new file mode 100644 index 0000000000..023f1b8bea --- /dev/null +++ b/sale_order_import/tests/test_sale_order.py @@ -0,0 +1,16 @@ +from odoo.tests.common import TransactionCase +from odoo import _ + + +class TestSaleOrder(TransactionCase): + def test_name_get(self): + sale_order = self.env.ref('sale.sale_order_1') + name = sale_order.name + _(' Amount w/o tax: %s %s)') % ( + sale_order.amount_untaxed, sale_order.currency_id.name) + so = self.env['sale.order'].with_context( + sale_order_show_amount=True + ) + name_get_res = so.search([ + ('id', '=', sale_order.id) + ]).name_get() + self.assertEqual(name, name_get_res[0][1]) diff --git a/sale_order_import/wizard/__init__.py b/sale_order_import/wizard/__init__.py index 38fba3b37f..e0ddf6156f 100644 --- a/sale_order_import/wizard/__init__.py +++ b/sale_order_import/wizard/__init__.py @@ -1,3 +1 @@ -# -*- coding: utf-8 -*- - from . import sale_order_import diff --git a/sale_order_import/wizard/sale_order_import.py b/sale_order_import/wizard/sale_order_import.py index a40a1d4b58..dc36180de2 100644 --- a/sale_order_import/wizard/sale_order_import.py +++ b/sale_order_import/wizard/sale_order_import.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # © 2016-2017 Akretion (Alexis de Lattre ) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). @@ -8,6 +7,7 @@ import logging import mimetypes from lxml import etree +from base64 import b64decode, b64encode logger = logging.getLogger(__name__) @@ -59,7 +59,7 @@ def order_file_change(self): self.csv_import = False try: xml_root = etree.fromstring( - self.order_file.decode('base64')) + b64decode(self.order_file)) except: raise UserError(_("This XML file is not XML-compliant")) doc_type = self.parse_xml_order(xml_root, detect_doc_type=True) @@ -67,7 +67,7 @@ def order_file_change(self): elif filetype and filetype[0] == 'application/pdf': self.csv_import = False doc_type = self.parse_pdf_order( - self.order_file.decode('base64'), detect_doc_type=True) + b64decode(self.order_file), detect_doc_type=True) self.doc_type = doc_type else: return {'warning': { @@ -107,7 +107,7 @@ def parse_pdf_order(self, order_file, detect_doc_type=False): if not xml_files_dict: raise UserError(_( 'There are no embedded XML file in this PDF file.')) - for xml_filename, xml_root in xml_files_dict.iteritems(): + for xml_filename, xml_root in xml_files_dict.items(): logger.info('Trying to parse XML file %s', xml_filename) try: parsed_order = self.parse_xml_order( @@ -256,8 +256,8 @@ def parse_order(self, order_file, order_filename, partner=False): logger.debug('Result of order parsing: %s', parsed_order) if 'attachments' not in parsed_order: parsed_order['attachments'] = {} - parsed_order['attachments'][order_filename] =\ - order_file.encode('base64') + parsed_order['attachments'][order_filename] = \ + b64encode(order_file) if 'chatter_msg' not in parsed_order: parsed_order['chatter_msg'] = [] return parsed_order @@ -266,7 +266,7 @@ def parse_order(self, order_file, order_filename, partner=False): def import_order_button(self): self.ensure_one() bdio = self.env['business.document.import'] - order_file_decoded = self.order_file.decode('base64') + order_file_decoded = b64decode(self.order_file) parsed_order = self.parse_order( order_file_decoded, self.order_filename, self.partner_id) if not parsed_order.get('lines'): @@ -305,7 +305,7 @@ def import_order_button(self): def create_order_button(self): self.ensure_one() parsed_order = self.parse_order( - self.order_file.decode('base64'), self.order_filename, + b64decode(self.order_file), self.order_filename, self.partner_id) return self.create_order_return_action( parsed_order, self.order_filename) @@ -383,7 +383,7 @@ def update_order_lines(self, parsed_order, order): existing_lines, parsed_order['lines'], chatter, qty_precision=qty_prec, seller=False) # NOW, we start to write/delete/create the order lines - for oline, cdict in compare_res['to_update'].iteritems(): + for oline, cdict in compare_res['to_update'].items(): write_vals = {} # TODO: add support for price_source == order if cdict.get('qty'): @@ -446,7 +446,7 @@ def update_order_button(self): if not order: raise UserError(_('You must select a quotation to update.')) parsed_order = self.parse_order( - self.order_file.decode('base64'), self.order_filename, + b64decode(self.order_file), self.order_filename, self.partner_id) currency = bdio._match_currency( parsed_order.get('currency'), parsed_order['chatter_msg']) diff --git a/sale_order_import/wizard/sale_order_import_view.xml b/sale_order_import/wizard/sale_order_import_view.xml index b350e8e0d9..6053d50b8f 100644 --- a/sale_order_import/wizard/sale_order_import_view.xml +++ b/sale_order_import/wizard/sale_order_import_view.xml @@ -70,7 +70,7 @@ From 0cca485ec65f88f1b88c7cc872ffe2ddef111023 Mon Sep 17 00:00:00 2001 From: Alexis de Lattre Date: Tue, 14 Aug 2018 11:04:43 +0200 Subject: [PATCH 15/91] Fix import of sale order with price_source='pricelist' when pricelist has visible discounts Code improvements in sale_order_import Add unit tests in sale_order_import Use display_name instead of name_get()[0][1] Fix travis --- sale_order_import/tests/__init__.py | 2 + sale_order_import/tests/test_order_import.py | 53 +++++++++++++++++++ sale_order_import/wizard/sale_order_import.py | 43 ++++++++------- 3 files changed, 79 insertions(+), 19 deletions(-) create mode 100644 sale_order_import/tests/test_order_import.py diff --git a/sale_order_import/tests/__init__.py b/sale_order_import/tests/__init__.py index 6f699d0d8b..111b7352d6 100644 --- a/sale_order_import/tests/__init__.py +++ b/sale_order_import/tests/__init__.py @@ -1 +1,3 @@ + from . import test_sale_order +from . import test_order_import diff --git a/sale_order_import/tests/test_order_import.py b/sale_order_import/tests/test_order_import.py new file mode 100644 index 0000000000..8f4cb35ab0 --- /dev/null +++ b/sale_order_import/tests/test_order_import.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- +# Copyright 2018 Akretion (Alexis de Lattre ) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo.tests.common import TransactionCase + + +class TestOrderImport(TransactionCase): + + def test_order_import(self): + soio = self.env['sale.order.import'] + parsed_order = { + 'partner': {'email': 'agrolait@yourcompany.example.com'}, + 'date': '2018-08-14', + 'order_ref': 'TEST1242', + 'lines': [{ + 'product': {'code': 'PROD_DEL'}, + 'qty': 2, + 'uom': {'unece_code': 'C62'}, + 'price_unit': 12.42, + }], + 'chatter_msg': [], + 'doc_type': 'rfq', + } + order = soio.create_order(parsed_order, 'pricelist') + self.assertEquals(order.client_order_ref, parsed_order['order_ref']) + self.assertEquals( + order.order_line[0].product_id.default_code, + parsed_order['lines'][0]['product']['code']) + self.assertEquals(int(order.order_line[0].product_uom_qty), 2) + # Now update the order + parsed_order_up = { + 'partner': {'email': 'agrolait@yourcompany.example.com'}, + 'date': '2018-08-14', + 'order_ref': 'TEST1242', + 'lines': [{ + 'product': {'code': 'PROD_DEL'}, + 'qty': 3, + 'uom': {'unece_code': 'C62'}, + 'price_unit': 12.42, + }, + { + 'product': {'code': 'PROD_DEL02'}, + 'qty': 1, + 'uom': {'unece_code': 'C62'}, + 'price_unit': 1.42, + }], + 'chatter_msg': [], + 'doc_type': 'rfq', + } + soio.update_order_lines(parsed_order_up, order, 'pricelist') + self.assertEquals(len(order.order_line), 2) + self.assertEquals(int(order.order_line[0].product_uom_qty), 3) diff --git a/sale_order_import/wizard/sale_order_import.py b/sale_order_import/wizard/sale_order_import.py index dc36180de2..24225aabc8 100644 --- a/sale_order_import/wizard/sale_order_import.py +++ b/sale_order_import/wizard/sale_order_import.py @@ -163,8 +163,8 @@ def _prepare_order(self, parsed_order, price_source): raise UserError(_( "The customer '%s' has a pricelist '%s' but the " "currency of this order is '%s'.") % ( - partner.name_get()[0][1], - partner.property_product_pricelist.name_get()[0][1], + partner.display_name, + partner.property_product_pricelist.display_name, currency.name)) if parsed_order.get('order_ref'): existing_orders = soo.search([ @@ -176,7 +176,7 @@ def _prepare_order(self, parsed_order, price_source): raise UserError(_( "An order of customer '%s' with reference '%s' " "already exists: %s (state: %s)") % ( - partner.name_get()[0][1], + partner.display_name, parsed_order['order_ref'], existing_orders[0].name, existing_orders[0].state)) @@ -200,9 +200,7 @@ def _prepare_order(self, parsed_order, price_source): uom = bdio._match_uom( line.get('uom'), parsed_order['chatter_msg'], product) line_vals = self._prepare_create_order_line( - product, uom, line, price_source) - # product_id_change is played in the inherit of create() - # of sale.order.line cf odoo/addons/sale/models/sale.py + product, uom, so_vals, line, price_source) so_vals['order_line'].append((0, 0, line_vals)) return so_vals @@ -262,7 +260,6 @@ def parse_order(self, order_file, order_filename, partner=False): parsed_order['chatter_msg'] = [] return parsed_order - @api.multi def import_order_button(self): self.ensure_one() bdio = self.env['business.document.import'] @@ -301,7 +298,6 @@ def import_order_button(self): return self.create_order_return_action( parsed_order, self.order_filename) - @api.multi def create_order_button(self): self.ensure_one() parsed_order = self.parse_order( @@ -310,7 +306,6 @@ def create_order_button(self): return self.create_order_return_action( parsed_order, self.order_filename) - @api.multi def create_order_return_action(self, parsed_order, order_filename): self.ensure_one() order = self.create_order( @@ -345,7 +340,10 @@ def _prepare_update_order_vals(self, parsed_order, order, partner): @api.model def _prepare_create_order_line( - self, product, uom, import_line, price_source): + self, product, uom, order, import_line, price_source): + """the 'order' arg can be a recordset (in case of an update of a sale order) + or a dict (in case of the creation of a new sale order)""" + solo = self.env['sale.order.line'] vals = { 'product_id': product.id, 'product_uom_qty': import_line['qty'], @@ -353,10 +351,18 @@ def _prepare_create_order_line( } if price_source == 'order': vals['price_unit'] = import_line['price_unit'] # TODO : fix + elif price_source == 'pricelist': + # product_id_change is played in the inherit of create() + # of sale.order.line cf odoo/addons/sale/models/sale.py + # but it is not enough: we also need to play _onchange_discount() + # to have the right discount for pricelist + vals['order_id'] = order + vals = solo.play_onchanges(vals, ['product_id']) + vals.pop('order_id') return vals - @api.multi - def update_order_lines(self, parsed_order, order): + @api.model + def update_order_lines(self, parsed_order, order, price_source): chatter = parsed_order['chatter_msg'] solo = self.env['sale.order.line'] dpo = self.env['decimal.precision'] @@ -390,11 +396,11 @@ def update_order_lines(self, parsed_order, order): chatter.append(_( "The quantity has been updated on the order line " "with product '%s' from %s to %s %s") % ( - oline.product_id.name_get()[0][1], + oline.product_id.display_name, cdict['qty'][0], cdict['qty'][1], oline.product_uom.name)) write_vals['product_uom_qty'] = cdict['qty'][1] - if self.price_source != 'order': + if price_source != 'order': new_price_unit = order.pricelist_id.with_context( date=order.date_order, uom=oline.product_uom.id).price_get( @@ -406,7 +412,7 @@ def update_order_lines(self, parsed_order, order): chatter.append(_( "The unit price has been updated on the order " "line with product '%s' from %s to %s %s") % ( - oline.product_id.name_get()[0][1], + oline.product_id.display_name, oline.price_unit, new_price_unit, order.currency_id.name)) write_vals['price_unit'] = new_price_unit @@ -426,8 +432,8 @@ def update_order_lines(self, parsed_order, order): to_create_label = [] for add in compare_res['to_add']: line_vals = self._prepare_create_order_line( - add['product'], add['uom'], add['import_line'], - self.price_source) + add['product'], add['uom'], order, add['import_line'], + price_source) line_vals['order_id'] = order.id new_line = solo.create(line_vals) to_create_label.append('%s %s x %s' % ( @@ -438,7 +444,6 @@ def update_order_lines(self, parsed_order, order): len(compare_res['to_add']), ', '.join(to_create_label))) return True - @api.multi def update_order_button(self): self.ensure_one() bdio = self.env['business.document.import'] @@ -459,7 +464,7 @@ def update_order_button(self): parsed_order, order, self.commercial_partner_id) if vals: order.write(vals) - self.update_order_lines(parsed_order, order) + self.update_order_lines(parsed_order, order, self.price_source) bdio.post_create_or_update(parsed_order, order) logger.info( 'Quotation ID %d updated via import of file %s', order.id, From 8549cb8771d2828aaa58edccd0dddb13dd6d0ede Mon Sep 17 00:00:00 2001 From: Alexis de Lattre Date: Sun, 26 Aug 2018 00:05:16 +0200 Subject: [PATCH 16/91] Check the VAT number of the destination partner, to make sure the business document is imported in the right company --- sale_order_import/wizard/sale_order_import.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/sale_order_import/wizard/sale_order_import.py b/sale_order_import/wizard/sale_order_import.py index 24225aabc8..9d0c074f84 100644 --- a/sale_order_import/wizard/sale_order_import.py +++ b/sale_order_import/wizard/sale_order_import.py @@ -2,7 +2,7 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from odoo import models, fields, api, _ -from odoo.tools import float_compare, float_is_zero +from odoo.tools import float_compare, float_is_zero, config from odoo.exceptions import UserError import logging import mimetypes @@ -132,7 +132,10 @@ def parse_pdf_order(self, order_file, detect_doc_type=False): # 'country_code': 'FR', # 'state_code': False, # 'zip': False, - # } + # }, + # 'company': {'vat': 'FR12123456789'}, # Only used to check we are not + # # importing the order in the + # # wrong company by mistake # 'date': '2016-08-16', # order date # 'order_ref': 'PO1242', # Customer PO number # 'currency': {'iso': 'EUR', 'symbol': u'€'}, @@ -258,6 +261,12 @@ def parse_order(self, order_file, order_filename, partner=False): b64encode(order_file) if 'chatter_msg' not in parsed_order: parsed_order['chatter_msg'] = [] + if ( + parsed_order.get('company') and + not config['test_enable'] and + not self._context.get('edi_skip_company_check')): + self.env['business.document.import']._check_company( + parsed_order['company'], parsed_order['chatter_msg']) return parsed_order def import_order_button(self): From 673f3944f01d6bbc2a6e262c7c4fc9ea4a9598d8 Mon Sep 17 00:00:00 2001 From: oca-travis Date: Wed, 31 Oct 2018 22:18:13 +0000 Subject: [PATCH 17/91] [UPD] Update sale_order_import.pot --- sale_order_import/i18n/es.po | 75 +++-- sale_order_import/i18n/fr.po | 155 +++++---- sale_order_import/i18n/sale_order_import.pot | 321 +++++++++++++++++++ 3 files changed, 437 insertions(+), 114 deletions(-) create mode 100644 sale_order_import/i18n/sale_order_import.pot diff --git a/sale_order_import/i18n/es.po b/sale_order_import/i18n/es.po index 2e12eb913f..bc26f1eab6 100644 --- a/sale_order_import/i18n/es.po +++ b/sale_order_import/i18n/es.po @@ -1,7 +1,7 @@ # Translation of Odoo Server. # This file contains the translation of the following modules: # * sale_order_import -# +# # Translators: # enjolras , 2018 msgid "" @@ -12,32 +12,33 @@ msgstr "" "PO-Revision-Date: 2018-03-12 01:43+0000\n" "Last-Translator: enjolras , 2018\n" "Language-Team: Spanish (https://www.transifex.com/oca/teams/23907/es/)\n" +"Language: es\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: \n" -"Language: es\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #. module: sale_order_import -#: code:addons/sale_order_import/models/sale.py:21 +#: code:addons/sale_order_import/models/sale.py:20 +#: code:addons/sale_order_import/tests/test_sale_order.py:8 #, python-format msgid " Amount w/o tax: %s %s)" msgstr "" #. module: sale_order_import -#: code:addons/sale_order_import/wizard/sale_order_import.py:437 +#: code:addons/sale_order_import/wizard/sale_order_import.py:452 #, python-format msgid "%d new order line(s) created: %s" msgstr "" #. module: sale_order_import -#: code:addons/sale_order_import/wizard/sale_order_import.py:420 +#: code:addons/sale_order_import/wizard/sale_order_import.py:435 #, python-format msgid "%d order line(s) deleted: %s" msgstr "" #. module: sale_order_import -#: code:addons/sale_order_import/wizard/sale_order_import.py:176 +#: code:addons/sale_order_import/wizard/sale_order_import.py:179 #, python-format msgid "" "An order of customer '%s' with reference '%s' already exists: %s (state: %s)" @@ -59,7 +60,7 @@ msgid "Create New" msgstr "Crear nuevo" #. module: sale_order_import -#: code:addons/sale_order_import/wizard/sale_order_import.py:318 +#: code:addons/sale_order_import/wizard/sale_order_import.py:322 #, python-format msgid "Created automatically via file import (%s)." msgstr "" @@ -76,7 +77,8 @@ msgstr "Creado el" #. module: sale_order_import #: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import_csv_import -msgid "Csv import" +#, fuzzy +msgid "Csv Import" msgstr "Importar CSV" #. module: sale_order_import @@ -124,15 +126,15 @@ msgstr "" #. module: sale_order_import #: model:ir.ui.view,arch_db:sale_order_import.sale_order_import_form msgid "" -"If it is a PDF file, Odoo will try to find an XML file in the attachments of" -" the PDF file and then use this XML file to create the quotation." +"If it is a PDF file, Odoo will try to find an XML file in the attachments of " +"the PDF file and then use this XML file to create the quotation." msgstr "" #. module: sale_order_import #: model:ir.ui.view,arch_db:sale_order_import.sale_order_import_form msgid "" -"If it is an XML file, Odoo will parse it if the module that adds support for" -" this XML format is installed. For the" +"If it is an XML file, Odoo will parse it if the module that adds support for " +"this XML format is installed. For the" msgstr "" #. module: sale_order_import @@ -168,7 +170,7 @@ msgid "Last Updated on" msgstr "Última actualización el" #. module: sale_order_import -#: code:addons/sale_order_import/wizard/sale_order_import.py:237 +#: code:addons/sale_order_import/wizard/sale_order_import.py:238 #, python-format msgid "Missing customer" msgstr "Falta cliente" @@ -178,6 +180,12 @@ msgstr "Falta cliente" msgid "Pricelist" msgstr "Tarifa" +#. module: sale_order_import +#: model:ir.model,name:sale_order_import.model_sale_order +#, fuzzy +msgid "Quotation" +msgstr "Presupuesto para actualizar" + #. module: sale_order_import #: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import_sale_id msgid "Quotation to Update" @@ -203,11 +211,6 @@ msgstr "Pedido de venta" msgid "Sale Order Import from Files" msgstr "" -#. module: sale_order_import -#: model:ir.model,name:sale_order_import.model_sale_order -msgid "Sales Order" -msgstr "Pedido de venta" - #. module: sale_order_import #: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import_partner_shipping_id msgid "Shipping Address" @@ -217,8 +220,8 @@ msgstr "Dirección de envío" #: model:ir.ui.view,arch_db:sale_order_import.sale_order_import_form msgid "" "Some quotations have been found for this customer ; one of them may " -"correspond to the order or RFQ that you are importing. You can either select" -" an existing quotation to update or create a new one." +"correspond to the order or RFQ that you are importing. You can either select " +"an existing quotation to update or create a new one." msgstr "" #. module: sale_order_import @@ -227,7 +230,7 @@ msgid "State" msgstr "Estado" #. module: sale_order_import -#: code:addons/sale_order_import/wizard/sale_order_import.py:454 +#: code:addons/sale_order_import/wizard/sale_order_import.py:468 #, python-format msgid "" "The currency of the imported order (%s) is different from the currency of " @@ -235,7 +238,7 @@ msgid "" msgstr "" #. module: sale_order_import -#: code:addons/sale_order_import/wizard/sale_order_import.py:163 +#: code:addons/sale_order_import/wizard/sale_order_import.py:166 #, python-format msgid "" "The customer '%s' has a pricelist '%s' but the currency of this order is " @@ -243,15 +246,15 @@ msgid "" msgstr "" #. module: sale_order_import -#: code:addons/sale_order_import/wizard/sale_order_import.py:390 +#: code:addons/sale_order_import/wizard/sale_order_import.py:405 #, python-format msgid "" -"The quantity has been updated on the order line with product '%s' from %s to" -" %s %s" +"The quantity has been updated on the order line with product '%s' from %s to " +"%s %s" msgstr "" #. module: sale_order_import -#: code:addons/sale_order_import/wizard/sale_order_import.py:406 +#: code:addons/sale_order_import/wizard/sale_order_import.py:421 #, python-format msgid "" "The unit price has been updated on the order line with product '%s' from %s " @@ -266,14 +269,14 @@ msgstr "" #. module: sale_order_import #: code:addons/sale_order_import/wizard/sale_order_import.py:64 -#: code:addons/sale_order_import/wizard/sale_order_import.py:243 +#: code:addons/sale_order_import/wizard/sale_order_import.py:244 #, python-format msgid "This XML file is not XML-compliant" msgstr "" #. module: sale_order_import #: code:addons/sale_order_import/wizard/sale_order_import.py:75 -#: code:addons/sale_order_import/wizard/sale_order_import.py:253 +#: code:addons/sale_order_import/wizard/sale_order_import.py:254 #, python-format msgid "" "This file '%s' is not recognised as a CSV, XML nor PDF file. Please check " @@ -281,16 +284,15 @@ msgid "" msgstr "" #. module: sale_order_import -#: code:addons/sale_order_import/wizard/sale_order_import.py:273 +#: code:addons/sale_order_import/wizard/sale_order_import.py:279 #, python-format msgid "This order doesn't have any line !" msgstr "" #. module: sale_order_import -#: code:addons/sale_order_import/wizard/sale_order_import.py:467 +#: code:addons/sale_order_import/wizard/sale_order_import.py:481 #, python-format -msgid "" -"This quotation has been updated automatically via the import of file %s" +msgid "This quotation has been updated automatically via the import of file %s" msgstr "" #. module: sale_order_import @@ -334,8 +336,8 @@ msgstr "Actualizar existente" #. module: sale_order_import #: model:ir.model.fields,help:sale_order_import.field_sale_order_import_order_file msgid "" -"Upload a Request for Quotation or an Order file. Supported formats: CSV, XML" -" and PDF (PDF with an embeded XML file)." +"Upload a Request for Quotation or an Order file. Supported formats: CSV, XML " +"and PDF (PDF with an embeded XML file)." msgstr "" #. module: sale_order_import @@ -346,7 +348,7 @@ msgid "" msgstr "" #. module: sale_order_import -#: code:addons/sale_order_import/wizard/sale_order_import.py:447 +#: code:addons/sale_order_import/wizard/sale_order_import.py:461 #, python-format msgid "You must select a quotation to update." msgstr "" @@ -356,3 +358,6 @@ msgstr "" msgid "" "format (UBL), you should install the module sale_order_import_ubl." msgstr "" + +#~ msgid "Sales Order" +#~ msgstr "Pedido de venta" diff --git a/sale_order_import/i18n/fr.po b/sale_order_import/i18n/fr.po index 1c11edda3d..4a9cc4aa47 100644 --- a/sale_order_import/i18n/fr.po +++ b/sale_order_import/i18n/fr.po @@ -1,7 +1,7 @@ # Translation of Odoo Server. # This file contains the translation of the following modules: # * sale_order_import -# +# # Translators: # OCA Transbot , 2016 msgid "" @@ -12,76 +12,78 @@ msgstr "" "PO-Revision-Date: 2016-11-12 13:36+0000\n" "Last-Translator: OCA Transbot , 2016\n" "Language-Team: French (https://www.transifex.com/oca/teams/23907/fr/)\n" +"Language: fr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: \n" -"Language: fr\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" #. module: sale_order_import -#: code:addons/sale_order_import/models/sale.py:22 +#: code:addons/sale_order_import/models/sale.py:20 +#: code:addons/sale_order_import/tests/test_sale_order.py:8 #, python-format msgid " Amount w/o tax: %s %s)" msgstr "" #. module: sale_order_import -#: code:addons/sale_order_import/wizard/sale_order_import.py:426 +#: code:addons/sale_order_import/wizard/sale_order_import.py:452 #, python-format msgid "%d new order line(s) created: %s" msgstr "" #. module: sale_order_import -#: code:addons/sale_order_import/wizard/sale_order_import.py:409 +#: code:addons/sale_order_import/wizard/sale_order_import.py:435 #, python-format msgid "%d order line(s) deleted: %s" msgstr "" #. module: sale_order_import -#: code:addons/sale_order_import/wizard/sale_order_import.py:177 +#: code:addons/sale_order_import/wizard/sale_order_import.py:179 #, python-format msgid "" "An order of customer '%s' with reference '%s' already exists: %s (state: %s)" msgstr "" #. module: sale_order_import -#: field:sale.order.import,price_source:0 +#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import_price_source msgid "Apply Prices From" msgstr "" #. module: sale_order_import -#: view:sale.order.import:sale_order_import.sale_order_import_form +#: model:ir.ui.view,arch_db:sale_order_import.sale_order_import_form msgid "Cancel" msgstr "Annuler" #. module: sale_order_import -#: view:sale.order.import:sale_order_import.sale_order_import_form +#: model:ir.ui.view,arch_db:sale_order_import.sale_order_import_form msgid "Create New" msgstr "Créer une nouvelle" #. module: sale_order_import -#: code:addons/sale_order_import/wizard/sale_order_import.py:307 +#: code:addons/sale_order_import/wizard/sale_order_import.py:322 #, python-format msgid "Created automatically via file import (%s)." msgstr "" #. module: sale_order_import -#: field:sale.order.import,create_uid:0 +#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import_create_uid msgid "Created by" msgstr "Créé par" #. module: sale_order_import -#: field:sale.order.import,create_date:0 +#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import_create_date msgid "Created on" msgstr "Créé le" #. module: sale_order_import -#: field:sale.order.import,csv_import:0 -msgid "Csv import" -msgstr "" +#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import_csv_import +#, fuzzy +msgid "Csv Import" +msgstr "Importer" #. module: sale_order_import -#: field:sale.order.import,commercial_partner_id:0 -#: field:sale.order.import,partner_id:0 +#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import_commercial_partner_id +#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import_partner_id msgid "Customer" msgstr "" @@ -91,27 +93,27 @@ msgid "Customer Order" msgstr "" #. module: sale_order_import -#: field:sale.order.import,display_name:0 +#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import_display_name msgid "Display Name" msgstr "" #. module: sale_order_import -#: field:sale.order.import,doc_type:0 +#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import_doc_type msgid "Document Type" msgstr "" #. module: sale_order_import -#: field:sale.order.import,order_filename:0 +#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import_order_filename msgid "Filename" msgstr "Nom du fichier" #. module: sale_order_import -#: field:sale.order.import,id:0 +#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import_id msgid "ID" msgstr "ID" #. module: sale_order_import -#: view:sale.order.import:sale_order_import.sale_order_import_form +#: model:ir.ui.view,arch_db:sale_order_import.sale_order_import_form msgid "" "If it is a CSV file, you will have to manually select the customer. The CSV " "file should have 2 columns: the product reference or EAN13 (1st col) and " @@ -122,21 +124,21 @@ msgid "" msgstr "" #. module: sale_order_import -#: view:sale.order.import:sale_order_import.sale_order_import_form +#: model:ir.ui.view,arch_db:sale_order_import.sale_order_import_form msgid "" -"If it is a PDF file, Odoo will try to find an XML file in the attachments of" -" the PDF file and then use this XML file to create the quotation." +"If it is a PDF file, Odoo will try to find an XML file in the attachments of " +"the PDF file and then use this XML file to create the quotation." msgstr "" #. module: sale_order_import -#: view:sale.order.import:sale_order_import.sale_order_import_form +#: model:ir.ui.view,arch_db:sale_order_import.sale_order_import_form msgid "" -"If it is an XML file, Odoo will parse it if the module that adds support for" -" this XML format is installed. For the" +"If it is an XML file, Odoo will parse it if the module that adds support for " +"this XML format is installed. For the" msgstr "" #. module: sale_order_import -#: view:sale.order.import:sale_order_import.sale_order_import_form +#: model:ir.ui.view,arch_db:sale_order_import.sale_order_import_form #: selection:sale.order.import,state:0 msgid "Import" msgstr "Importer" @@ -148,27 +150,27 @@ msgid "Import RFQ or Order" msgstr "" #. module: sale_order_import -#: view:sale.order.import:sale_order_import.sale_order_import_form +#: model:ir.ui.view,arch_db:sale_order_import.sale_order_import_form msgid "Import Sale Orders" msgstr "" #. module: sale_order_import -#: field:sale.order.import,__last_update:0 +#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import___last_update msgid "Last Modified on" msgstr "" #. module: sale_order_import -#: field:sale.order.import,write_uid:0 +#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import_write_uid msgid "Last Updated by" msgstr "Dernière mise-à-jour par" #. module: sale_order_import -#: field:sale.order.import,write_date:0 +#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import_write_date msgid "Last Updated on" msgstr "Dernière mise-à-jour le" #. module: sale_order_import -#: code:addons/sale_order_import/wizard/sale_order_import.py:229 +#: code:addons/sale_order_import/wizard/sale_order_import.py:238 #, python-format msgid "Missing customer" msgstr "" @@ -179,7 +181,12 @@ msgid "Pricelist" msgstr "" #. module: sale_order_import -#: field:sale.order.import,sale_id:0 +#: model:ir.model,name:sale_order_import.model_sale_order +msgid "Quotation" +msgstr "" + +#. module: sale_order_import +#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import_sale_id msgid "Quotation to Update" msgstr "" @@ -189,7 +196,7 @@ msgid "Request For Quotation" msgstr "" #. module: sale_order_import -#: field:sale.order.import,order_file:0 +#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import_order_file msgid "Request for Quotation or Order" msgstr "" @@ -204,30 +211,25 @@ msgid "Sale Order Import from Files" msgstr "" #. module: sale_order_import -#: model:ir.model,name:sale_order_import.model_sale_order -msgid "Sales Order" -msgstr "" - -#. module: sale_order_import -#: field:sale.order.import,partner_shipping_id:0 +#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import_partner_shipping_id msgid "Shipping Address" msgstr "" #. module: sale_order_import -#: view:sale.order.import:sale_order_import.sale_order_import_form +#: model:ir.ui.view,arch_db:sale_order_import.sale_order_import_form msgid "" "Some quotations have been found for this customer ; one of them may " -"correspond to the order or RFQ that you are importing. You can either select" -" an existing quotation to update or create a new one." +"correspond to the order or RFQ that you are importing. You can either select " +"an existing quotation to update or create a new one." msgstr "" #. module: sale_order_import -#: field:sale.order.import,state:0 +#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import_state msgid "State" msgstr "Etat" #. module: sale_order_import -#: code:addons/sale_order_import/wizard/sale_order_import.py:443 +#: code:addons/sale_order_import/wizard/sale_order_import.py:468 #, python-format msgid "" "The currency of the imported order (%s) is different from the currency of " @@ -235,7 +237,7 @@ msgid "" msgstr "" #. module: sale_order_import -#: code:addons/sale_order_import/wizard/sale_order_import.py:164 +#: code:addons/sale_order_import/wizard/sale_order_import.py:166 #, python-format msgid "" "The customer '%s' has a pricelist '%s' but the currency of this order is " @@ -243,15 +245,15 @@ msgid "" msgstr "" #. module: sale_order_import -#: code:addons/sale_order_import/wizard/sale_order_import.py:379 +#: code:addons/sale_order_import/wizard/sale_order_import.py:405 #, python-format msgid "" -"The quantity has been updated on the order line with product '%s' from %s to" -" %s %s" +"The quantity has been updated on the order line with product '%s' from %s to " +"%s %s" msgstr "" #. module: sale_order_import -#: code:addons/sale_order_import/wizard/sale_order_import.py:395 +#: code:addons/sale_order_import/wizard/sale_order_import.py:421 #, python-format msgid "" "The unit price has been updated on the order line with product '%s' from %s " @@ -259,21 +261,21 @@ msgid "" msgstr "" #. module: sale_order_import -#: code:addons/sale_order_import/wizard/sale_order_import.py:109 +#: code:addons/sale_order_import/wizard/sale_order_import.py:108 #, python-format msgid "There are no embedded XML file in this PDF file." msgstr "" #. module: sale_order_import -#: code:addons/sale_order_import/wizard/sale_order_import.py:65 -#: code:addons/sale_order_import/wizard/sale_order_import.py:235 +#: code:addons/sale_order_import/wizard/sale_order_import.py:64 +#: code:addons/sale_order_import/wizard/sale_order_import.py:244 #, python-format msgid "This XML file is not XML-compliant" msgstr "" #. module: sale_order_import -#: code:addons/sale_order_import/wizard/sale_order_import.py:76 -#: code:addons/sale_order_import/wizard/sale_order_import.py:245 +#: code:addons/sale_order_import/wizard/sale_order_import.py:75 +#: code:addons/sale_order_import/wizard/sale_order_import.py:254 #, python-format msgid "" "This file '%s' is not recognised as a CSV, XML nor PDF file. Please check " @@ -281,20 +283,19 @@ msgid "" msgstr "" #. module: sale_order_import -#: code:addons/sale_order_import/wizard/sale_order_import.py:265 +#: code:addons/sale_order_import/wizard/sale_order_import.py:279 #, python-format msgid "This order doesn't have any line !" msgstr "" #. module: sale_order_import -#: code:addons/sale_order_import/wizard/sale_order_import.py:456 +#: code:addons/sale_order_import/wizard/sale_order_import.py:481 #, python-format -msgid "" -"This quotation has been updated automatically via the import of file %s" +msgid "This quotation has been updated automatically via the import of file %s" msgstr "" #. module: sale_order_import -#: code:addons/sale_order_import/wizard/sale_order_import.py:98 +#: code:addons/sale_order_import/wizard/sale_order_import.py:97 #, python-format msgid "" "This type of CSV order is not supported. Did you install the module to " @@ -302,8 +303,8 @@ msgid "" msgstr "" #. module: sale_order_import -#: code:addons/sale_order_import/wizard/sale_order_import.py:91 -#: code:addons/sale_order_import/wizard/sale_order_import.py:119 +#: code:addons/sale_order_import/wizard/sale_order_import.py:90 +#: code:addons/sale_order_import/wizard/sale_order_import.py:118 #, python-format msgid "" "This type of XML RFQ/order is not supported. Did you install the module to " @@ -311,12 +312,12 @@ msgid "" msgstr "" #. module: sale_order_import -#: view:sale.order.import:sale_order_import.sale_order_import_form +#: model:ir.ui.view,arch_db:sale_order_import.sale_order_import_form msgid "Universal Business Language" msgstr "" #. module: sale_order_import -#: code:addons/sale_order_import/wizard/sale_order_import.py:75 +#: code:addons/sale_order_import/wizard/sale_order_import.py:74 #, python-format msgid "Unsupported file format" msgstr "" @@ -327,36 +328,32 @@ msgid "Update" msgstr "Mettre à jour" #. module: sale_order_import -#: view:sale.order.import:sale_order_import.sale_order_import_form +#: model:ir.ui.view,arch_db:sale_order_import.sale_order_import_form msgid "Update Existing" msgstr "Mettre à jour une existante" #. module: sale_order_import -#: help:sale.order.import,order_file:0 +#: model:ir.model.fields,help:sale_order_import.field_sale_order_import_order_file msgid "" -"Upload a Request for Quotation or an Order file. Supported formats: CSV, XML" -" and PDF (PDF with an embeded XML file)." +"Upload a Request for Quotation or an Order file. Supported formats: CSV, XML " +"and PDF (PDF with an embeded XML file)." msgstr "" #. module: sale_order_import -#: view:sale.order.import:sale_order_import.sale_order_import_form +#: model:ir.ui.view,arch_db:sale_order_import.sale_order_import_form msgid "" "Upload below the customer order or request for quotation as CSV, XML or PDF " "file. When you click on the Import button:" msgstr "" #. module: sale_order_import -#: code:addons/sale_order_import/wizard/sale_order_import.py:436 +#: code:addons/sale_order_import/wizard/sale_order_import.py:461 #, python-format msgid "You must select a quotation to update." msgstr "" #. module: sale_order_import -#: view:sale.order.import:sale_order_import.sale_order_import_form -msgid "format (UBL), you should install the module" -msgstr "" - -#. module: sale_order_import -#: view:sale.order.import:sale_order_import.sale_order_import_form -msgid "sale_order_import_ubl" +#: model:ir.ui.view,arch_db:sale_order_import.sale_order_import_form +msgid "" +"format (UBL), you should install the module sale_order_import_ubl." msgstr "" diff --git a/sale_order_import/i18n/sale_order_import.pot b/sale_order_import/i18n/sale_order_import.pot new file mode 100644 index 0000000000..99bb03f99e --- /dev/null +++ b/sale_order_import/i18n/sale_order_import.pot @@ -0,0 +1,321 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * sale_order_import +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 11.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: <>\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: sale_order_import +#: code:addons/sale_order_import/models/sale.py:20 +#: code:addons/sale_order_import/tests/test_sale_order.py:8 +#, python-format +msgid " Amount w/o tax: %s %s)" +msgstr "" + +#. module: sale_order_import +#: code:addons/sale_order_import/wizard/sale_order_import.py:452 +#, python-format +msgid "%d new order line(s) created: %s" +msgstr "" + +#. module: sale_order_import +#: code:addons/sale_order_import/wizard/sale_order_import.py:435 +#, python-format +msgid "%d order line(s) deleted: %s" +msgstr "" + +#. module: sale_order_import +#: code:addons/sale_order_import/wizard/sale_order_import.py:179 +#, python-format +msgid "An order of customer '%s' with reference '%s' already exists: %s (state: %s)" +msgstr "" + +#. module: sale_order_import +#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import_price_source +msgid "Apply Prices From" +msgstr "" + +#. module: sale_order_import +#: model:ir.ui.view,arch_db:sale_order_import.sale_order_import_form +msgid "Cancel" +msgstr "" + +#. module: sale_order_import +#: model:ir.ui.view,arch_db:sale_order_import.sale_order_import_form +msgid "Create New" +msgstr "" + +#. module: sale_order_import +#: code:addons/sale_order_import/wizard/sale_order_import.py:322 +#, python-format +msgid "Created automatically via file import (%s)." +msgstr "" + +#. module: sale_order_import +#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import_create_uid +msgid "Created by" +msgstr "" + +#. module: sale_order_import +#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import_create_date +msgid "Created on" +msgstr "" + +#. module: sale_order_import +#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import_csv_import +msgid "Csv Import" +msgstr "" + +#. module: sale_order_import +#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import_commercial_partner_id +#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import_partner_id +msgid "Customer" +msgstr "" + +#. module: sale_order_import +#: selection:sale.order.import,price_source:0 +msgid "Customer Order" +msgstr "" + +#. module: sale_order_import +#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import_display_name +msgid "Display Name" +msgstr "" + +#. module: sale_order_import +#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import_doc_type +msgid "Document Type" +msgstr "" + +#. module: sale_order_import +#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import_order_filename +msgid "Filename" +msgstr "" + +#. module: sale_order_import +#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import_id +msgid "ID" +msgstr "" + +#. module: sale_order_import +#: model:ir.ui.view,arch_db:sale_order_import.sale_order_import_form +msgid "If it is a CSV file, you will have to manually select the customer. The CSV file should have 2 columns: the product reference or EAN13 (1st col) and then the product quantity (2nd col). It shouldn't have any header line and use semi-colon as field separator. The quantity shouldn't use any thousand separator ; if it is a decimal value, it should use dot as the decimal separator." +msgstr "" + +#. module: sale_order_import +#: model:ir.ui.view,arch_db:sale_order_import.sale_order_import_form +msgid "If it is a PDF file, Odoo will try to find an XML file in the attachments of the PDF file and then use this XML file to create the quotation." +msgstr "" + +#. module: sale_order_import +#: model:ir.ui.view,arch_db:sale_order_import.sale_order_import_form +msgid "If it is an XML file, Odoo will parse it if the module that adds support for this XML format is installed. For the" +msgstr "" + +#. module: sale_order_import +#: model:ir.ui.view,arch_db:sale_order_import.sale_order_import_form +#: selection:sale.order.import,state:0 +msgid "Import" +msgstr "" + +#. module: sale_order_import +#: model:ir.actions.act_window,name:sale_order_import.sale_order_import_action +#: model:ir.ui.menu,name:sale_order_import.sale_order_import_menu +msgid "Import RFQ or Order" +msgstr "" + +#. module: sale_order_import +#: model:ir.ui.view,arch_db:sale_order_import.sale_order_import_form +msgid "Import Sale Orders" +msgstr "" + +#. module: sale_order_import +#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import___last_update +msgid "Last Modified on" +msgstr "" + +#. module: sale_order_import +#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import_write_uid +msgid "Last Updated by" +msgstr "" + +#. module: sale_order_import +#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import_write_date +msgid "Last Updated on" +msgstr "" + +#. module: sale_order_import +#: code:addons/sale_order_import/wizard/sale_order_import.py:238 +#, python-format +msgid "Missing customer" +msgstr "" + +#. module: sale_order_import +#: selection:sale.order.import,price_source:0 +msgid "Pricelist" +msgstr "" + +#. module: sale_order_import +#: model:ir.model,name:sale_order_import.model_sale_order +msgid "Quotation" +msgstr "" + +#. module: sale_order_import +#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import_sale_id +msgid "Quotation to Update" +msgstr "" + +#. module: sale_order_import +#: selection:sale.order.import,doc_type:0 +msgid "Request For Quotation" +msgstr "" + +#. module: sale_order_import +#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import_order_file +msgid "Request for Quotation or Order" +msgstr "" + +#. module: sale_order_import +#: selection:sale.order.import,doc_type:0 +msgid "Sale Order" +msgstr "" + +#. module: sale_order_import +#: model:ir.model,name:sale_order_import.model_sale_order_import +msgid "Sale Order Import from Files" +msgstr "" + +#. module: sale_order_import +#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import_partner_shipping_id +msgid "Shipping Address" +msgstr "" + +#. module: sale_order_import +#: model:ir.ui.view,arch_db:sale_order_import.sale_order_import_form +msgid "Some quotations have been found for this customer ; one of them may correspond to the order or RFQ that you are importing. You can either select an existing quotation to update or create a new one." +msgstr "" + +#. module: sale_order_import +#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import_state +msgid "State" +msgstr "" + +#. module: sale_order_import +#: code:addons/sale_order_import/wizard/sale_order_import.py:468 +#, python-format +msgid "The currency of the imported order (%s) is different from the currency of the existing order (%s)" +msgstr "" + +#. module: sale_order_import +#: code:addons/sale_order_import/wizard/sale_order_import.py:166 +#, python-format +msgid "The customer '%s' has a pricelist '%s' but the currency of this order is '%s'." +msgstr "" + +#. module: sale_order_import +#: code:addons/sale_order_import/wizard/sale_order_import.py:405 +#, python-format +msgid "The quantity has been updated on the order line with product '%s' from %s to %s %s" +msgstr "" + +#. module: sale_order_import +#: code:addons/sale_order_import/wizard/sale_order_import.py:421 +#, python-format +msgid "The unit price has been updated on the order line with product '%s' from %s to %s %s" +msgstr "" + +#. module: sale_order_import +#: code:addons/sale_order_import/wizard/sale_order_import.py:108 +#, python-format +msgid "There are no embedded XML file in this PDF file." +msgstr "" + +#. module: sale_order_import +#: code:addons/sale_order_import/wizard/sale_order_import.py:64 +#: code:addons/sale_order_import/wizard/sale_order_import.py:244 +#, python-format +msgid "This XML file is not XML-compliant" +msgstr "" + +#. module: sale_order_import +#: code:addons/sale_order_import/wizard/sale_order_import.py:75 +#: code:addons/sale_order_import/wizard/sale_order_import.py:254 +#, python-format +msgid "This file '%s' is not recognised as a CSV, XML nor PDF file. Please check the file and it's extension." +msgstr "" + +#. module: sale_order_import +#: code:addons/sale_order_import/wizard/sale_order_import.py:279 +#, python-format +msgid "This order doesn't have any line !" +msgstr "" + +#. module: sale_order_import +#: code:addons/sale_order_import/wizard/sale_order_import.py:481 +#, python-format +msgid "This quotation has been updated automatically via the import of file %s" +msgstr "" + +#. module: sale_order_import +#: code:addons/sale_order_import/wizard/sale_order_import.py:97 +#, python-format +msgid "This type of CSV order is not supported. Did you install the module to support CSV orders?" +msgstr "" + +#. module: sale_order_import +#: code:addons/sale_order_import/wizard/sale_order_import.py:90 +#: code:addons/sale_order_import/wizard/sale_order_import.py:118 +#, python-format +msgid "This type of XML RFQ/order is not supported. Did you install the module to support this XML format?" +msgstr "" + +#. module: sale_order_import +#: model:ir.ui.view,arch_db:sale_order_import.sale_order_import_form +msgid "Universal Business Language" +msgstr "" + +#. module: sale_order_import +#: code:addons/sale_order_import/wizard/sale_order_import.py:74 +#, python-format +msgid "Unsupported file format" +msgstr "" + +#. module: sale_order_import +#: selection:sale.order.import,state:0 +msgid "Update" +msgstr "" + +#. module: sale_order_import +#: model:ir.ui.view,arch_db:sale_order_import.sale_order_import_form +msgid "Update Existing" +msgstr "" + +#. module: sale_order_import +#: model:ir.model.fields,help:sale_order_import.field_sale_order_import_order_file +msgid "Upload a Request for Quotation or an Order file. Supported formats: CSV, XML and PDF (PDF with an embeded XML file)." +msgstr "" + +#. module: sale_order_import +#: model:ir.ui.view,arch_db:sale_order_import.sale_order_import_form +msgid "Upload below the customer order or request for quotation as CSV, XML or PDF file. When you click on the Import button:" +msgstr "" + +#. module: sale_order_import +#: code:addons/sale_order_import/wizard/sale_order_import.py:461 +#, python-format +msgid "You must select a quotation to update." +msgstr "" + +#. module: sale_order_import +#: model:ir.ui.view,arch_db:sale_order_import.sale_order_import_form +msgid "format (UBL), you should install the module sale_order_import_ubl." +msgstr "" + From 824ad9ba971be1f4ddf91e19061cdc5f83990371 Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Wed, 3 Apr 2019 02:46:14 +0000 Subject: [PATCH 18/91] [ADD] icon.png --- sale_order_import/static/description/icon.png | Bin 0 -> 9455 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 sale_order_import/static/description/icon.png diff --git a/sale_order_import/static/description/icon.png b/sale_order_import/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..3a0328b516c4980e8e44cdb63fd945757ddd132d GIT binary patch literal 9455 zcmW++2RxMjAAjx~&dlBk9S+%}OXg)AGE&Cb*&}d0jUxM@u(PQx^-s)697TX`ehR4?GS^qbkof1cslKgkU)h65qZ9Oc=ml_0temigYLJfnz{IDzUf>bGs4N!v3=Z3jMq&A#7%rM5eQ#dc?k~! zVpnB`o+K7|Al`Q_U;eD$B zfJtP*jH`siUq~{KE)`jP2|#TUEFGRryE2`i0**z#*^6~AI|YzIWy$Cu#CSLW3q=GA z6`?GZymC;dCPk~rBS%eCb`5OLr;RUZ;D`}um=H)BfVIq%7VhiMr)_#G0N#zrNH|__ zc+blN2UAB0=617@>_u;MPHN;P;N#YoE=)R#i$k_`UAA>WWCcEVMh~L_ zj--gtp&|K1#58Yz*AHCTMziU1Jzt_jG0I@qAOHsk$2}yTmVkBp_eHuY$A9)>P6o~I z%aQ?!(GqeQ-Y+b0I(m9pwgi(IIZZzsbMv+9w{PFtd_<_(LA~0H(xz{=FhLB@(1&qHA5EJw1>>=%q2f&^X>IQ{!GJ4e9U z&KlB)z(84HmNgm2hg2C0>WM{E(DdPr+EeU_N@57;PC2&DmGFW_9kP&%?X4}+xWi)( z;)z%wI5>D4a*5XwD)P--sPkoY(a~WBw;E~AW`Yue4kFa^LM3X`8x|}ZUeMnqr}>kH zG%WWW>3ml$Yez?i%)2pbKPI7?5o?hydokgQyZsNEr{a|mLdt;X2TX(#B1j35xPnPW z*bMSSOauW>o;*=kO8ojw91VX!qoOQb)zHJ!odWB}d+*K?#sY_jqPdg{Sm2HdYzdEx zOGVPhVRTGPtv0o}RfVP;Nd(|CB)I;*t&QO8h zFfekr30S!-LHmV_Su-W+rEwYXJ^;6&3|L$mMC8*bQptyOo9;>Qb9Q9`ySe3%V$A*9 zeKEe+b0{#KWGp$F+tga)0RtI)nhMa-K@JS}2krK~n8vJ=Ngm?R!9G<~RyuU0d?nz# z-5EK$o(!F?hmX*2Yt6+coY`6jGbb7tF#6nHA zuKk=GGJ;ZwON1iAfG$E#Y7MnZVmrY|j0eVI(DN_MNFJmyZ|;w4tf@=CCDZ#5N_0K= z$;R~bbk?}TpfDjfB&aiQ$VA}s?P}xPERJG{kxk5~R`iRS(SK5d+Xs9swCozZISbnS zk!)I0>t=A<-^z(cmSFz3=jZ23u13X><0b)P)^1T_))Kr`e!-pb#q&J*Q`p+B6la%C zuVl&0duN<;uOsB3%T9Fp8t{ED108<+W(nOZd?gDnfNBC3>M8WE61$So|P zVvqH0SNtDTcsUdzaMDpT=Ty0pDHHNL@Z0w$Y`XO z2M-_r1S+GaH%pz#Uy0*w$Vdl=X=rQXEzO}d6J^R6zjM1u&c9vYLvLp?W7w(?np9x1 zE_0JSAJCPB%i7p*Wvg)pn5T`8k3-uR?*NT|J`eS#_#54p>!p(mLDvmc-3o0mX*mp_ zN*AeS<>#^-{S%W<*mz^!X$w_2dHWpcJ6^j64qFBft-o}o_Vx80o0>}Du;>kLts;$8 zC`7q$QI(dKYG`Wa8#wl@V4jVWBRGQ@1dr-hstpQL)Tl+aqVpGpbSfN>5i&QMXfiZ> zaA?T1VGe?rpQ@;+pkrVdd{klI&jVS@I5_iz!=UMpTsa~mBga?1r}aRBm1WS;TT*s0f0lY=JBl66Upy)-k4J}lh=P^8(SXk~0xW=T9v*B|gzIhN z>qsO7dFd~mgxAy4V?&)=5ieYq?zi?ZEoj)&2o)RLy=@hbCRcfT5jigwtQGE{L*8<@Yd{zg;CsL5mvzfDY}P-wos_6PfprFVaeqNE%h zKZhLtcQld;ZD+>=nqN~>GvROfueSzJD&BE*}XfU|H&(FssBqY=hPCt`d zH?@s2>I(|;fcW&YM6#V#!kUIP8$Nkdh0A(bEVj``-AAyYgwY~jB zT|I7Bf@%;7aL7Wf4dZ%VqF$eiaC38OV6oy3Z#TER2G+fOCd9Iaoy6aLYbPTN{XRPz z;U!V|vBf%H!}52L2gH_+j;`bTcQRXB+y9onc^wLm5wi3-Be}U>k_u>2Eg$=k!(l@I zcCg+flakT2Nej3i0yn+g+}%NYb?ta;R?(g5SnwsQ49U8Wng8d|{B+lyRcEDvR3+`O{zfmrmvFrL6acVP%yG98X zo&+VBg@px@i)%o?dG(`T;n*$S5*rnyiR#=wW}}GsAcfyQpE|>a{=$Hjg=-*_K;UtD z#z-)AXwSRY?OPefw^iI+ z)AXz#PfEjlwTes|_{sB?4(O@fg0AJ^g8gP}ex9Ucf*@_^J(s_5jJV}c)s$`Myn|Kd z$6>}#q^n{4vN@+Os$m7KV+`}c%4)4pv@06af4-x5#wj!KKb%caK{A&Y#Rfs z-po?Dcb1({W=6FKIUirH&(yg=*6aLCekcKwyfK^JN5{wcA3nhO(o}SK#!CINhI`-I z1)6&n7O&ZmyFMuNwvEic#IiOAwNkR=u5it{B9n2sAJV5pNhar=j5`*N!Na;c7g!l$ z3aYBqUkqqTJ=Re-;)s!EOeij=7SQZ3Hq}ZRds%IM*PtM$wV z@;rlc*NRK7i3y5BETSKuumEN`Xu_8GP1Ri=OKQ$@I^ko8>H6)4rjiG5{VBM>B|%`&&s^)jS|-_95&yc=GqjNo{zFkw%%HHhS~e=s zD#sfS+-?*t|J!+ozP6KvtOl!R)@@-z24}`9{QaVLD^9VCSR2b`b!KC#o;Ki<+wXB6 zx3&O0LOWcg4&rv4QG0)4yb}7BFSEg~=IR5#ZRj8kg}dS7_V&^%#Do==#`u zpy6{ox?jWuR(;pg+f@mT>#HGWHAJRRDDDv~@(IDw&R>9643kK#HN`!1vBJHnC+RM&yIh8{gG2q zA%e*U3|N0XSRa~oX-3EAneep)@{h2vvd3Xvy$7og(sayr@95+e6~Xvi1tUqnIxoIH zVWo*OwYElb#uyW{Imam6f2rGbjR!Y3`#gPqkv57dB6K^wRGxc9B(t|aYDGS=m$&S!NmCtrMMaUg(c zc2qC=2Z`EEFMW-me5B)24AqF*bV5Dr-M5ig(l-WPS%CgaPzs6p_gnCIvTJ=Y<6!gT zVt@AfYCzjjsMEGi=rDQHo0yc;HqoRNnNFeWZgcm?f;cp(6CNylj36DoL(?TS7eU#+ z7&mfr#y))+CJOXQKUMZ7QIdS9@#-}7y2K1{8)cCt0~-X0O!O?Qx#E4Og+;A2SjalQ zs7r?qn0H044=sDN$SRG$arw~n=+T_DNdSrarmu)V6@|?1-ZB#hRn`uilTGPJ@fqEy zGt(f0B+^JDP&f=r{#Y_wi#AVDf-y!RIXU^0jXsFpf>=Ji*TeqSY!H~AMbJdCGLhC) zn7Rx+sXw6uYj;WRYrLd^5IZq@6JI1C^YkgnedZEYy<&4(z%Q$5yv#Boo{AH8n$a zhb4Y3PWdr269&?V%uI$xMcUrMzl=;w<_nm*qr=c3Rl@i5wWB;e-`t7D&c-mcQl7x! zZWB`UGcw=Y2=}~wzrfLx=uet<;m3~=8I~ZRuzvMQUQdr+yTV|ATf1Uuomr__nDf=X zZ3WYJtHp_ri(}SQAPjv+Y+0=fH4krOP@S&=zZ-t1jW1o@}z;xk8 z(Nz1co&El^HK^NrhVHa-_;&88vTU>_J33=%{if;BEY*J#1n59=07jrGQ#IP>@u#3A z;!q+E1Rj3ZJ+!4bq9F8PXJ@yMgZL;>&gYA0%_Kbi8?S=XGM~dnQZQ!yBSgcZhY96H zrWnU;k)qy`rX&&xlDyA%(a1Hhi5CWkmg(`Gb%m(HKi-7Z!LKGRP_B8@`7&hdDy5n= z`OIxqxiVfX@OX1p(mQu>0Ai*v_cTMiw4qRt3~NBvr9oBy0)r>w3p~V0SCm=An6@3n)>@z!|o-$HvDK z|3D2ZMJkLE5loMKl6R^ez@Zz%S$&mbeoqH5`Bb){Ei21q&VP)hWS2tjShfFtGE+$z zzCR$P#uktu+#!w)cX!lWN1XU%K-r=s{|j?)Akf@q#3b#{6cZCuJ~gCxuMXRmI$nGtnH+-h z+GEi!*X=AP<|fG`1>MBdTb?28JYc=fGvAi2I<$B(rs$;eoJCyR6_bc~p!XR@O-+sD z=eH`-ye})I5ic1eL~TDmtfJ|8`0VJ*Yr=hNCd)G1p2MMz4C3^Mj?7;!w|Ly%JqmuW zlIEW^Ft%z?*|fpXda>Jr^1noFZEwFgVV%|*XhH@acv8rdGxeEX{M$(vG{Zw+x(ei@ zmfXb22}8-?Fi`vo-YVrTH*C?a8%M=Hv9MqVH7H^J$KsD?>!SFZ;ZsvnHr_gn=7acz z#W?0eCdVhVMWN12VV^$>WlQ?f;P^{(&pYTops|btm6aj>_Uz+hqpGwB)vWp0Cf5y< zft8-je~nn?W11plq}N)4A{l8I7$!ks_x$PXW-2XaRFswX_BnF{R#6YIwMhAgd5F9X zGmwdadS6(a^fjHtXg8=l?Rc0Sm%hk6E9!5cLVloEy4eh(=FwgP`)~I^5~pBEWo+F6 zSf2ncyMurJN91#cJTy_u8Y}@%!bq1RkGC~-bV@SXRd4F{R-*V`bS+6;W5vZ(&+I<9$;-V|eNfLa5n-6% z2(}&uGRF;p92eS*sE*oR$@pexaqr*meB)VhmIg@h{uzkk$9~qh#cHhw#>O%)b@+(| z^IQgqzuj~Sk(J;swEM-3TrJAPCq9k^^^`q{IItKBRXYe}e0Tdr=Huf7da3$l4PdpwWDop%^}n;dD#K4s#DYA8SHZ z&1!riV4W4R7R#C))JH1~axJ)RYnM$$lIR%6fIVA@zV{XVyx}C+a-Dt8Y9M)^KU0+H zR4IUb2CJ{Hg>CuaXtD50jB(_Tcx=Z$^WYu2u5kubqmwp%drJ6 z?Fo40g!Qd<-l=TQxqHEOuPX0;^z7iX?Ke^a%XT<13TA^5`4Xcw6D@Ur&VT&CUe0d} z1GjOVF1^L@>O)l@?bD~$wzgf(nxX1OGD8fEV?TdJcZc2KoUe|oP1#=$$7ee|xbY)A zDZq+cuTpc(fFdj^=!;{k03C69lMQ(|>uhRfRu%+!k&YOi-3|1QKB z z?n?eq1XP>p-IM$Z^C;2L3itnbJZAip*Zo0aw2bs8@(s^~*8T9go!%dHcAz2lM;`yp zD=7&xjFV$S&5uDaiScyD?B-i1ze`+CoRtz`Wn+Zl&#s4&}MO{@N!ufrzjG$B79)Y2d3tBk&)TxUTw@QS0TEL_?njX|@vq?Uz(nBFK5Pq7*xj#u*R&i|?7+6# z+|r_n#SW&LXhtheZdah{ZVoqwyT{D>MC3nkFF#N)xLi{p7J1jXlmVeb;cP5?e(=f# zuT7fvjSbjS781v?7{)-X3*?>tq?)Yd)~|1{BDS(pqC zC}~H#WXlkUW*H5CDOo<)#x7%RY)A;ShGhI5s*#cRDA8YgqG(HeKDx+#(ZQ?386dv! zlXCO)w91~Vw4AmOcATuV653fa9R$fyK8ul%rG z-wfS zihugoZyr38Im?Zuh6@RcF~t1anQu7>#lPpb#}4cOA!EM11`%f*07RqOVkmX{p~KJ9 z^zP;K#|)$`^Rb{rnHGH{~>1(fawV0*Z#)}M`m8-?ZJV<+e}s9wE# z)l&az?w^5{)`S(%MRzxdNqrs1n*-=jS^_jqE*5XDrA0+VE`5^*p3CuM<&dZEeCjoz zR;uu_H9ZPZV|fQq`Cyw4nscrVwi!fE6ciMmX$!_hN7uF;jjKG)d2@aC4ropY)8etW=xJvni)8eHi`H$%#zn^WJ5NLc-rqk|u&&4Z6fD_m&JfSI1Bvb?b<*n&sfl0^t z=HnmRl`XrFvMKB%9}>PaA`m-fK6a0(8=qPkWS5bb4=v?XcWi&hRY?O5HdulRi4?fN zlsJ*N-0Qw+Yic@s0(2uy%F@ib;GjXt01Fmx5XbRo6+n|pP(&nodMoap^z{~q ziEeaUT@Mxe3vJSfI6?uLND(CNr=#^W<1b}jzW58bIfyWTDle$mmS(|x-0|2UlX+9k zQ^EX7Nw}?EzVoBfT(-LT|=9N@^hcn-_p&sqG z&*oVs2JSU+N4ZD`FhCAWaS;>|wH2G*Id|?pa#@>tyxX`+4HyIArWDvVrX)2WAOQff z0qyHu&-S@i^MS-+j--!pr4fPBj~_8({~e1bfcl0wI1kaoN>mJL6KUPQm5N7lB(ui1 zE-o%kq)&djzWJ}ob<-GfDlkB;F31j-VHKvQUGQ3sp`CwyGJk_i!y^sD0fqC@$9|jO zOqN!r!8-p==F@ZVP=U$qSpY(gQ0)59P1&t@y?5rvg<}E+GB}26NYPp4f2YFQrQtot5mn3wu_qprZ=>Ig-$ zbW26Ws~IgY>}^5w`vTB(G`PTZaDiGBo5o(tp)qli|NeV( z@H_=R8V39rt5J5YB2Ky?4eJJ#b`_iBe2ot~6%7mLt5t8Vwi^Jy7|jWXqa3amOIoRb zOr}WVFP--DsS`1WpN%~)t3R!arKF^Q$e12KEqU36AWwnCBICpH4XCsfnyrHr>$I$4 z!DpKX$OKLWarN7nv@!uIA+~RNO)l$$w}p(;b>mx8pwYvu;dD_unryX_NhT8*Tj>BTrTTL&!?O+%Rv;b?B??gSzdp?6Uug9{ zd@V08Z$BdI?fpoCS$)t4mg4rT8Q_I}h`0d-vYZ^|dOB*Q^S|xqTV*vIg?@fVFSmMpaw0qtTRbx} z({Pg?#{2`sc9)M5N$*N|4;^t$+QP?#mov zGVC@I*lBVrOU-%2y!7%)fAKjpEFsgQc4{amtiHb95KQEwvf<(3T<9-Zm$xIew#P22 zc2Ix|App^>v6(3L_MCU0d3W##AB0M~3D00EWoKZqsJYT(#@w$Y_H7G22M~ApVFTRHMI_3be)Lkn#0F*V8Pq zc}`Cjy$bE;FJ6H7p=0y#R>`}-m4(0F>%@P|?7fx{=R^uFdISRnZ2W_xQhD{YuR3t< z{6yxu=4~JkeA;|(J6_nv#>Nvs&FuLA&PW^he@t(UwFFE8)|a!R{`E`K`i^ZnyE4$k z;(749Ix|oi$c3QbEJ3b~D_kQsPz~fIUKym($a_7dJ?o+40*OLl^{=&oq$<#Q(yyrp z{J-FAniyAw9tPbe&IhQ|a`DqFTVQGQ&Gq3!C2==4x{6EJwiPZ8zub-iXoUtkJiG{} zPaR&}_fn8_z~(=;5lD-aPWD3z8PZS@AaUiomF!G8I}Mf>e~0g#BelA-5#`cj;O5>N Xviia!U7SGha1wx#SCgwmn*{w2TRX*I literal 0 HcmV?d00001 From 758a0eebfcb50d2b46b2da820ff5912c9dbb5bc5 Mon Sep 17 00:00:00 2001 From: Andrea Date: Wed, 3 Apr 2019 09:10:45 +0200 Subject: [PATCH 19/91] [12.0][MIG] sale_order_import --- sale_order_import/__init__.py | 2 ++ sale_order_import/__manifest__.py | 4 ++-- sale_order_import/models/__init__.py | 2 ++ sale_order_import/models/sale.py | 2 +- sale_order_import/readme/CONTRIBUTORS.rst | 1 + sale_order_import/tests/__init__.py | 1 + sale_order_import/tests/test_order_import.py | 11 +++++------ sale_order_import/tests/test_sale_order.py | 2 ++ sale_order_import/wizard/__init__.py | 2 ++ sale_order_import/wizard/sale_order_import.py | 8 ++++---- 10 files changed, 22 insertions(+), 13 deletions(-) diff --git a/sale_order_import/__init__.py b/sale_order_import/__init__.py index 134df27435..66596bf78b 100644 --- a/sale_order_import/__init__.py +++ b/sale_order_import/__init__.py @@ -1,2 +1,4 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + from . import wizard from . import models diff --git a/sale_order_import/__manifest__.py b/sale_order_import/__manifest__.py index 486d203771..d190a032bb 100644 --- a/sale_order_import/__manifest__.py +++ b/sale_order_import/__manifest__.py @@ -1,9 +1,9 @@ # © 2016-2017 Akretion (Alexis de Lattre ) -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). { 'name': 'Sale Order Import', - 'version': '11.0.1.0.0', + 'version': '12.0.1.0.0', 'category': 'Sales Management', 'license': 'AGPL-3', 'summary': 'Import RFQ or sale orders from files', diff --git a/sale_order_import/models/__init__.py b/sale_order_import/models/__init__.py index 8a0dc04e1f..c065ca3ff6 100644 --- a/sale_order_import/models/__init__.py +++ b/sale_order_import/models/__init__.py @@ -1 +1,3 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + from . import sale diff --git a/sale_order_import/models/sale.py b/sale_order_import/models/sale.py index 4ea663c0ff..11451c4b0f 100644 --- a/sale_order_import/models/sale.py +++ b/sale_order_import/models/sale.py @@ -1,5 +1,5 @@ # © 2016-2017 Akretion (Alexis de Lattre ) -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). from odoo import models, _ diff --git a/sale_order_import/readme/CONTRIBUTORS.rst b/sale_order_import/readme/CONTRIBUTORS.rst index 97c5fe672b..b7091eba54 100644 --- a/sale_order_import/readme/CONTRIBUTORS.rst +++ b/sale_order_import/readme/CONTRIBUTORS.rst @@ -1,2 +1,3 @@ * Alexis de Lattre * Dennis Sluijk +* Andrea Stirpe diff --git a/sale_order_import/tests/__init__.py b/sale_order_import/tests/__init__.py index 111b7352d6..51360d0f1f 100644 --- a/sale_order_import/tests/__init__.py +++ b/sale_order_import/tests/__init__.py @@ -1,3 +1,4 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). from . import test_sale_order from . import test_order_import diff --git a/sale_order_import/tests/test_order_import.py b/sale_order_import/tests/test_order_import.py index 8f4cb35ab0..8d1715f338 100644 --- a/sale_order_import/tests/test_order_import.py +++ b/sale_order_import/tests/test_order_import.py @@ -1,6 +1,5 @@ -# -*- coding: utf-8 -*- # Copyright 2018 Akretion (Alexis de Lattre ) -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). from odoo.tests.common import TransactionCase @@ -10,11 +9,11 @@ class TestOrderImport(TransactionCase): def test_order_import(self): soio = self.env['sale.order.import'] parsed_order = { - 'partner': {'email': 'agrolait@yourcompany.example.com'}, + 'partner': {'email': 'deco.addict82@example.com'}, 'date': '2018-08-14', 'order_ref': 'TEST1242', 'lines': [{ - 'product': {'code': 'PROD_DEL'}, + 'product': {'code': 'FURN_8888'}, 'qty': 2, 'uom': {'unece_code': 'C62'}, 'price_unit': 12.42, @@ -34,13 +33,13 @@ def test_order_import(self): 'date': '2018-08-14', 'order_ref': 'TEST1242', 'lines': [{ - 'product': {'code': 'PROD_DEL'}, + 'product': {'code': 'FURN_8888'}, 'qty': 3, 'uom': {'unece_code': 'C62'}, 'price_unit': 12.42, }, { - 'product': {'code': 'PROD_DEL02'}, + 'product': {'code': 'FURN_9999'}, 'qty': 1, 'uom': {'unece_code': 'C62'}, 'price_unit': 1.42, diff --git a/sale_order_import/tests/test_sale_order.py b/sale_order_import/tests/test_sale_order.py index 023f1b8bea..55c23ced27 100644 --- a/sale_order_import/tests/test_sale_order.py +++ b/sale_order_import/tests/test_sale_order.py @@ -1,3 +1,5 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + from odoo.tests.common import TransactionCase from odoo import _ diff --git a/sale_order_import/wizard/__init__.py b/sale_order_import/wizard/__init__.py index e0ddf6156f..293838f7f5 100644 --- a/sale_order_import/wizard/__init__.py +++ b/sale_order_import/wizard/__init__.py @@ -1 +1,3 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + from . import sale_order_import diff --git a/sale_order_import/wizard/sale_order_import.py b/sale_order_import/wizard/sale_order_import.py index 9d0c074f84..1fb1ad6678 100644 --- a/sale_order_import/wizard/sale_order_import.py +++ b/sale_order_import/wizard/sale_order_import.py @@ -1,5 +1,5 @@ # © 2016-2017 Akretion (Alexis de Lattre ) -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). from odoo import models, fields, api, _ from odoo.tools import float_compare, float_is_zero, config @@ -38,7 +38,7 @@ class SaleOrderImport(models.TransientModel): ], string='Apply Prices From') # for state = update commercial_partner_id = fields.Many2one( - 'res.partner', string='Customer', readonly=True) + 'res.partner', string='Commercial Entity', readonly=True) partner_shipping_id = fields.Many2one( 'res.partner', string='Shipping Address', readonly=True) # amount_untaxed = fields.Float( @@ -319,7 +319,7 @@ def create_order_return_action(self, parsed_order, order_filename): self.ensure_one() order = self.create_order( parsed_order, self.price_source, order_filename) - order.message_post(_( + order.message_post(body=_( "Created automatically via file import (%s).") % self.order_filename) action = self.env['ir.actions.act_window'].for_xml_id( @@ -478,7 +478,7 @@ def update_order_button(self): logger.info( 'Quotation ID %d updated via import of file %s', order.id, self.order_filename) - order.message_post(_( + order.message_post(body=_( "This quotation has been updated automatically via the import of " "file %s") % self.order_filename) action = self.env['ir.actions.act_window'].for_xml_id( From 229cedfeaf763ab1170245f60ec86a08c00c3e5b Mon Sep 17 00:00:00 2001 From: sebalix Date: Thu, 14 May 2020 14:31:01 +0200 Subject: [PATCH 20/91] [IMP] sale_order_import: black, isort, prettier --- sale_order_import/__manifest__.py | 31 +- sale_order_import/models/sale.py | 12 +- sale_order_import/tests/test_order_import.py | 72 +- sale_order_import/tests/test_sale_order.py | 16 +- sale_order_import/wizard/sale_order_import.py | 629 ++++++++++-------- .../wizard/sale_order_import_view.xml | 171 +++-- 6 files changed, 531 insertions(+), 400 deletions(-) diff --git a/sale_order_import/__manifest__.py b/sale_order_import/__manifest__.py index d190a032bb..111312c5fd 100644 --- a/sale_order_import/__manifest__.py +++ b/sale_order_import/__manifest__.py @@ -2,20 +2,21 @@ # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). { - 'name': 'Sale Order Import', - 'version': '12.0.1.0.0', - 'category': 'Sales Management', - 'license': 'AGPL-3', - 'summary': 'Import RFQ or sale orders from files', - 'author': 'Akretion,Odoo Community Association (OCA)', - 'website': 'https://github.com/oca/edi', - 'depends': [ - 'sale_commercial_partner', - 'base_business_document_import', - 'onchange_helper', - ], - 'data': [ - 'wizard/sale_order_import_view.xml', + "name": "Sale Order Import", + "version": "12.0.1.0.0", + "category": "Sales Management", + "license": "AGPL-3", + "summary": "Import RFQ or sale orders from files", + "author": "Akretion,Odoo Community Association (OCA)", + "website": "https://github.com/oca/edi", + "depends": [ + # OCA/sale-workflow + "sale_commercial_partner", + # OCA/edi + "base_business_document_import", + # OCA/server-tools + "onchange_helper", ], - 'installable': True, + "data": ["wizard/sale_order_import_view.xml"], + "installable": True, } diff --git a/sale_order_import/models/sale.py b/sale_order_import/models/sale.py index 11451c4b0f..3d1e9f094c 100644 --- a/sale_order_import/models/sale.py +++ b/sale_order_import/models/sale.py @@ -1,24 +1,26 @@ # © 2016-2017 Akretion (Alexis de Lattre ) # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -from odoo import models, _ +from odoo import _, models class SaleOrder(models.Model): - _inherit = 'sale.order' + _inherit = "sale.order" def name_get(self): """Add amount_untaxed in name_get of sale orders""" res = super(SaleOrder, self).name_get() - if self._context.get('sale_order_show_amount'): + if self._context.get("sale_order_show_amount"): new_res = [] for (sale_id, name) in res: sale = self.browse(sale_id) # I didn't find a python method to easily display # a float + currency symbol (before or after) # depending on lang of context and currency - name += _(' Amount w/o tax: %s %s)') % ( - sale.amount_untaxed, sale.currency_id.name) + name += _(" Amount w/o tax: %s %s)") % ( + sale.amount_untaxed, + sale.currency_id.name, + ) new_res.append((sale_id, name)) return new_res else: diff --git a/sale_order_import/tests/test_order_import.py b/sale_order_import/tests/test_order_import.py index 8d1715f338..413a4aa8a7 100644 --- a/sale_order_import/tests/test_order_import.py +++ b/sale_order_import/tests/test_order_import.py @@ -5,48 +5,52 @@ class TestOrderImport(TransactionCase): - def test_order_import(self): - soio = self.env['sale.order.import'] + soio = self.env["sale.order.import"] parsed_order = { - 'partner': {'email': 'deco.addict82@example.com'}, - 'date': '2018-08-14', - 'order_ref': 'TEST1242', - 'lines': [{ - 'product': {'code': 'FURN_8888'}, - 'qty': 2, - 'uom': {'unece_code': 'C62'}, - 'price_unit': 12.42, - }], - 'chatter_msg': [], - 'doc_type': 'rfq', - } - order = soio.create_order(parsed_order, 'pricelist') - self.assertEquals(order.client_order_ref, parsed_order['order_ref']) + "partner": {"email": "deco.addict82@example.com"}, + "date": "2018-08-14", + "order_ref": "TEST1242", + "lines": [ + { + "product": {"code": "FURN_8888"}, + "qty": 2, + "uom": {"unece_code": "C62"}, + "price_unit": 12.42, + } + ], + "chatter_msg": [], + "doc_type": "rfq", + } + order = soio.create_order(parsed_order, "pricelist") + self.assertEquals(order.client_order_ref, parsed_order["order_ref"]) self.assertEquals( order.order_line[0].product_id.default_code, - parsed_order['lines'][0]['product']['code']) + parsed_order["lines"][0]["product"]["code"], + ) self.assertEquals(int(order.order_line[0].product_uom_qty), 2) # Now update the order parsed_order_up = { - 'partner': {'email': 'agrolait@yourcompany.example.com'}, - 'date': '2018-08-14', - 'order_ref': 'TEST1242', - 'lines': [{ - 'product': {'code': 'FURN_8888'}, - 'qty': 3, - 'uom': {'unece_code': 'C62'}, - 'price_unit': 12.42, + "partner": {"email": "agrolait@yourcompany.example.com"}, + "date": "2018-08-14", + "order_ref": "TEST1242", + "lines": [ + { + "product": {"code": "FURN_8888"}, + "qty": 3, + "uom": {"unece_code": "C62"}, + "price_unit": 12.42, }, { - 'product': {'code': 'FURN_9999'}, - 'qty': 1, - 'uom': {'unece_code': 'C62'}, - 'price_unit': 1.42, - }], - 'chatter_msg': [], - 'doc_type': 'rfq', - } - soio.update_order_lines(parsed_order_up, order, 'pricelist') + "product": {"code": "FURN_9999"}, + "qty": 1, + "uom": {"unece_code": "C62"}, + "price_unit": 1.42, + }, + ], + "chatter_msg": [], + "doc_type": "rfq", + } + soio.update_order_lines(parsed_order_up, order, "pricelist") self.assertEquals(len(order.order_line), 2) self.assertEquals(int(order.order_line[0].product_uom_qty), 3) diff --git a/sale_order_import/tests/test_sale_order.py b/sale_order_import/tests/test_sale_order.py index 55c23ced27..59dbc1bbd2 100644 --- a/sale_order_import/tests/test_sale_order.py +++ b/sale_order_import/tests/test_sale_order.py @@ -1,18 +1,16 @@ # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -from odoo.tests.common import TransactionCase from odoo import _ +from odoo.tests.common import TransactionCase class TestSaleOrder(TransactionCase): def test_name_get(self): - sale_order = self.env.ref('sale.sale_order_1') - name = sale_order.name + _(' Amount w/o tax: %s %s)') % ( - sale_order.amount_untaxed, sale_order.currency_id.name) - so = self.env['sale.order'].with_context( - sale_order_show_amount=True + sale_order = self.env.ref("sale.sale_order_1") + name = sale_order.name + _(" Amount w/o tax: %s %s)") % ( + sale_order.amount_untaxed, + sale_order.currency_id.name, ) - name_get_res = so.search([ - ('id', '=', sale_order.id) - ]).name_get() + so = self.env["sale.order"].with_context(sale_order_show_amount=True) + name_get_res = so.search([("id", "=", sale_order.id)]).name_get() self.assertEqual(name, name_get_res[0][1]) diff --git a/sale_order_import/wizard/sale_order_import.py b/sale_order_import/wizard/sale_order_import.py index 1fb1ad6678..3d48b05501 100644 --- a/sale_order_import/wizard/sale_order_import.py +++ b/sale_order_import/wizard/sale_order_import.py @@ -1,82 +1,92 @@ # © 2016-2017 Akretion (Alexis de Lattre ) # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -from odoo import models, fields, api, _ -from odoo.tools import float_compare, float_is_zero, config -from odoo.exceptions import UserError import logging import mimetypes -from lxml import etree from base64 import b64decode, b64encode +from lxml import etree + +from odoo import _, api, fields, models +from odoo.exceptions import UserError +from odoo.tools import config, float_compare, float_is_zero + logger = logging.getLogger(__name__) class SaleOrderImport(models.TransientModel): - _name = 'sale.order.import' - _description = 'Sale Order Import from Files' + _name = "sale.order.import" + _description = "Sale Order Import from Files" - state = fields.Selection([ - ('import', 'Import'), - ('update', 'Update'), - ], string='State', default="import") + state = fields.Selection( + [("import", "Import"), ("update", "Update")], string="State", default="import" + ) partner_id = fields.Many2one( - 'res.partner', string='Customer', domain=[('customer', '=', True)]) + "res.partner", string="Customer", domain=[("customer", "=", True)] + ) csv_import = fields.Boolean(default=False, readonly=True) order_file = fields.Binary( - string='Request for Quotation or Order', required=True, + string="Request for Quotation or Order", + required=True, help="Upload a Request for Quotation or an Order file. Supported " - "formats: CSV, XML and PDF (PDF with an embeded XML file).") - order_filename = fields.Char(string='Filename') - doc_type = fields.Selection([ - ('rfq', 'Request For Quotation'), - ('order', 'Sale Order'), - ], string='Document Type', readonly=True) - price_source = fields.Selection([ - ('pricelist', 'Pricelist'), - ('order', 'Customer Order'), - ], string='Apply Prices From') + "formats: CSV, XML and PDF (PDF with an embeded XML file).", + ) + order_filename = fields.Char(string="Filename") + doc_type = fields.Selection( + [("rfq", "Request For Quotation"), ("order", "Sale Order")], + string="Document Type", + readonly=True, + ) + price_source = fields.Selection( + [("pricelist", "Pricelist"), ("order", "Customer Order")], + string="Apply Prices From", + ) # for state = update commercial_partner_id = fields.Many2one( - 'res.partner', string='Commercial Entity', readonly=True) + "res.partner", string="Commercial Entity", readonly=True + ) partner_shipping_id = fields.Many2one( - 'res.partner', string='Shipping Address', readonly=True) + "res.partner", string="Shipping Address", readonly=True + ) # amount_untaxed = fields.Float( # string='Total Untaxed', digits=dp.get_precision('Account'), # readonly=True) - sale_id = fields.Many2one( - 'sale.order', string='Quotation to Update') + sale_id = fields.Many2one("sale.order", string="Quotation to Update") - @api.onchange('order_file') + @api.onchange("order_file") def order_file_change(self): if self.order_filename and self.order_file: filetype = mimetypes.guess_type(self.order_filename) - logger.debug('Order file mimetype: %s', filetype) - if filetype and filetype[0] in ('text/csv', 'text/plain'): + logger.debug("Order file mimetype: %s", filetype) + if filetype and filetype[0] in ("text/csv", "text/plain"): self.csv_import = True self.doc_type = False - elif filetype and filetype[0] in ['application/xml', 'text/xml']: + elif filetype and filetype[0] in ["application/xml", "text/xml"]: self.csv_import = False try: - xml_root = etree.fromstring( - b64decode(self.order_file)) - except: + xml_root = etree.fromstring(b64decode(self.order_file)) + except etree.XMLSyntaxError: raise UserError(_("This XML file is not XML-compliant")) doc_type = self.parse_xml_order(xml_root, detect_doc_type=True) self.doc_type = doc_type - elif filetype and filetype[0] == 'application/pdf': + elif filetype and filetype[0] == "application/pdf": self.csv_import = False doc_type = self.parse_pdf_order( - b64decode(self.order_file), detect_doc_type=True) + b64decode(self.order_file), detect_doc_type=True + ) self.doc_type = doc_type else: - return {'warning': { - 'title': _('Unsupported file format'), - 'message': _( - "This file '%s' is not recognised as a CSV, XML nor " - "PDF file. Please check the file and it's " - "extension.") % self.order_filename - }} + return { + "warning": { + "title": _("Unsupported file format"), + "message": _( + "This file '%s' is not recognised as a CSV, XML nor " + "PDF file. Please check the file and it's " + "extension." + ) + % self.order_filename, + } + } else: self.csv_import = False self.doc_type = False @@ -87,16 +97,22 @@ def get_xml_doc_type(self, xml_root): @api.model def parse_xml_order(self, xml_root, detect_doc_type=False): - raise UserError(_( - "This type of XML RFQ/order is not supported. Did you install " - "the module to support this XML format?")) + raise UserError( + _( + "This type of XML RFQ/order is not supported. Did you install " + "the module to support this XML format?" + ) + ) @api.model def parse_csv_order(self, order_file, partner): - assert partner, 'missing partner' - raise UserError(_( - "This type of CSV order is not supported. Did you install " - "the module to support CSV orders?")) + assert partner, "missing partner" + raise UserError( + _( + "This type of CSV order is not supported. Did you install " + "the module to support CSV orders?" + ) + ) @api.model def parse_pdf_order(self, order_file, detect_doc_type=False): @@ -105,19 +121,22 @@ def parse_pdf_order(self, order_file, detect_doc_type=False): """ xml_files_dict = self.get_xml_files_from_pdf(order_file) if not xml_files_dict: - raise UserError(_( - 'There are no embedded XML file in this PDF file.')) + raise UserError(_("There are no embedded XML file in this PDF file.")) for xml_filename, xml_root in xml_files_dict.items(): - logger.info('Trying to parse XML file %s', xml_filename) + logger.info("Trying to parse XML file %s", xml_filename) try: parsed_order = self.parse_xml_order( - xml_root, detect_doc_type=detect_doc_type) + xml_root, detect_doc_type=detect_doc_type + ) return parsed_order - except: + except (etree.LxmlError, UserError): continue - raise UserError(_( - "This type of XML RFQ/order is not supported. Did you install " - "the module to support this XML format?")) + raise UserError( + _( + "This type of XML RFQ/order is not supported. Did you install " + "the module to support this XML format?" + ) + ) # Format of parsed_order # { @@ -155,67 +174,83 @@ def parse_pdf_order(self, order_file, detect_doc_type=False): @api.model def _prepare_order(self, parsed_order, price_source): - soo = self.env['sale.order'] - bdio = self.env['business.document.import'] + soo = self.env["sale.order"] + bdio = self.env["business.document.import"] partner = bdio._match_partner( - parsed_order['partner'], parsed_order['chatter_msg'], - partner_type='customer') + parsed_order["partner"], + parsed_order["chatter_msg"], + partner_type="customer", + ) currency = bdio._match_currency( - parsed_order.get('currency'), parsed_order['chatter_msg']) + parsed_order.get("currency"), parsed_order["chatter_msg"] + ) if partner.property_product_pricelist.currency_id != currency: - raise UserError(_( - "The customer '%s' has a pricelist '%s' but the " - "currency of this order is '%s'.") % ( + raise UserError( + _( + "The customer '%s' has a pricelist '%s' but the " + "currency of this order is '%s'." + ) + % ( partner.display_name, partner.property_product_pricelist.display_name, - currency.name)) - if parsed_order.get('order_ref'): - existing_orders = soo.search([ - ('client_order_ref', '=', parsed_order['order_ref']), - ('partner_id', '=', partner.id), - ('state', '!=', 'cancel'), - ]) + currency.name, + ) + ) + if parsed_order.get("order_ref"): + existing_orders = soo.search( + [ + ("client_order_ref", "=", parsed_order["order_ref"]), + ("partner_id", "=", partner.id), + ("state", "!=", "cancel"), + ] + ) if existing_orders: - raise UserError(_( - "An order of customer '%s' with reference '%s' " - "already exists: %s (state: %s)") % ( + raise UserError( + _( + "An order of customer '%s' with reference '%s' " + "already exists: %s (state: %s)" + ) + % ( partner.display_name, - parsed_order['order_ref'], + parsed_order["order_ref"], existing_orders[0].name, - existing_orders[0].state)) + existing_orders[0].state, + ) + ) so_vals = { - 'partner_id': partner.id, - 'client_order_ref': parsed_order.get('order_ref'), - } - so_vals = soo.play_onchanges(so_vals, ['partner_id']) - so_vals['order_line'] = [] - if parsed_order.get('ship_to'): + "partner_id": partner.id, + "client_order_ref": parsed_order.get("order_ref"), + } + so_vals = soo.play_onchanges(so_vals, ["partner_id"]) + so_vals["order_line"] = [] + if parsed_order.get("ship_to"): shipping_partner = bdio._match_shipping_partner( - parsed_order['ship_to'], partner, parsed_order['chatter_msg']) - so_vals['partner_shipping_id'] = shipping_partner.id - if parsed_order.get('date'): - so_vals['date_order'] = parsed_order['date'] - for line in parsed_order['lines']: + parsed_order["ship_to"], partner, parsed_order["chatter_msg"] + ) + so_vals["partner_shipping_id"] = shipping_partner.id + if parsed_order.get("date"): + so_vals["date_order"] = parsed_order["date"] + for line in parsed_order["lines"]: # partner=False because we don't want to use product.supplierinfo product = bdio._match_product( - line['product'], parsed_order['chatter_msg'], seller=False) - uom = bdio._match_uom( - line.get('uom'), parsed_order['chatter_msg'], product) + line["product"], parsed_order["chatter_msg"], seller=False + ) + uom = bdio._match_uom(line.get("uom"), parsed_order["chatter_msg"], product) line_vals = self._prepare_create_order_line( - product, uom, so_vals, line, price_source) - so_vals['order_line'].append((0, 0, line_vals)) + product, uom, so_vals, line, price_source + ) + so_vals["order_line"].append((0, 0, line_vals)) return so_vals @api.model def create_order(self, parsed_order, price_source, order_filename=None): - soo = self.env['sale.order'] - bdio = self.env['business.document.import'] + soo = self.env["sale.order"] + bdio = self.env["business.document.import"] so_vals = self._prepare_order(parsed_order, price_source) order = soo.create(so_vals) - bdio.post_create_or_update( - parsed_order, order, doc_filename=order_filename) - logger.info('Sale Order ID %d created', order.id) + bdio.post_create_or_update(parsed_order, order, doc_filename=order_filename) + logger.info("Sale Order ID %d created", order.id) return order @api.model @@ -224,269 +259,329 @@ def create_order_ws(self, parsed_order, price_source, order_filename=None): webservice. Returns an ID to avoid this error: TypeError: sale.order(15,) is not JSON serializable""" order = self.create_order( - parsed_order, price_source, order_filename=order_filename) + parsed_order, price_source, order_filename=order_filename + ) return order.id @api.model def parse_order(self, order_file, order_filename, partner=False): - assert order_file, 'Missing order file' - assert order_filename, 'Missing order filename' + assert order_file, "Missing order file" + assert order_filename, "Missing order filename" filetype = mimetypes.guess_type(order_filename)[0] - logger.debug('Order file mimetype: %s', filetype) - if filetype in ('text/csv', 'text/plain'): + logger.debug("Order file mimetype: %s", filetype) + if filetype in ("text/csv", "text/plain"): if not partner: - raise UserError(_('Missing customer')) + raise UserError(_("Missing customer")) parsed_order = self.parse_csv_order(order_file, partner) - elif filetype in ['application/xml', 'text/xml']: + elif filetype in ["application/xml", "text/xml"]: try: xml_root = etree.fromstring(order_file) - except: + except etree.LxmlError: raise UserError(_("This XML file is not XML-compliant")) pretty_xml_string = etree.tostring( - xml_root, pretty_print=True, encoding='UTF-8', - xml_declaration=True) - logger.debug('Starting to import the following XML file:') + xml_root, pretty_print=True, encoding="UTF-8", xml_declaration=True + ) + logger.debug("Starting to import the following XML file:") logger.debug(pretty_xml_string) parsed_order = self.parse_xml_order(xml_root) - elif filetype == 'application/pdf': + elif filetype == "application/pdf": parsed_order = self.parse_pdf_order(order_file) else: - raise UserError(_( - "This file '%s' is not recognised as a CSV, XML nor PDF file. " - "Please check the file and it's extension.") % order_filename) - logger.debug('Result of order parsing: %s', parsed_order) - if 'attachments' not in parsed_order: - parsed_order['attachments'] = {} - parsed_order['attachments'][order_filename] = \ - b64encode(order_file) - if 'chatter_msg' not in parsed_order: - parsed_order['chatter_msg'] = [] + raise UserError( + _( + "This file '%s' is not recognised as a CSV, XML nor PDF file. " + "Please check the file and it's extension." + ) + % order_filename + ) + logger.debug("Result of order parsing: %s", parsed_order) + if "attachments" not in parsed_order: + parsed_order["attachments"] = {} + parsed_order["attachments"][order_filename] = b64encode(order_file) + if "chatter_msg" not in parsed_order: + parsed_order["chatter_msg"] = [] if ( - parsed_order.get('company') and - not config['test_enable'] and - not self._context.get('edi_skip_company_check')): - self.env['business.document.import']._check_company( - parsed_order['company'], parsed_order['chatter_msg']) + parsed_order.get("company") + and not config["test_enable"] + and not self._context.get("edi_skip_company_check") + ): + self.env["business.document.import"]._check_company( + parsed_order["company"], parsed_order["chatter_msg"] + ) return parsed_order def import_order_button(self): self.ensure_one() - bdio = self.env['business.document.import'] + bdio = self.env["business.document.import"] order_file_decoded = b64decode(self.order_file) parsed_order = self.parse_order( - order_file_decoded, self.order_filename, self.partner_id) - if not parsed_order.get('lines'): - raise UserError(_( - "This order doesn't have any line !")) + order_file_decoded, self.order_filename, self.partner_id + ) + if not parsed_order.get("lines"): + raise UserError(_("This order doesn't have any line !")) partner = bdio._match_partner( - parsed_order['partner'], [], partner_type='customer') + parsed_order["partner"], [], partner_type="customer" + ) commercial_partner = partner.commercial_partner_id partner_shipping_id = False - if parsed_order.get('ship_to'): + if parsed_order.get("ship_to"): partner_shipping_id = bdio._match_shipping_partner( - parsed_order['ship_to'], partner, []).id - existing_quotations = self.env['sale.order'].search([ - ('commercial_partner_id', '=', commercial_partner.id), - ('state', 'in', ('draft', 'sent'))]) + parsed_order["ship_to"], partner, [] + ).id + existing_quotations = self.env["sale.order"].search( + [ + ("commercial_partner_id", "=", commercial_partner.id), + ("state", "in", ("draft", "sent")), + ] + ) if existing_quotations: default_sale_id = False if len(existing_quotations) == 1: default_sale_id = existing_quotations[0].id - self.write({ - 'commercial_partner_id': commercial_partner.id, - 'partner_shipping_id': partner_shipping_id, - 'state': 'update', - 'sale_id': default_sale_id, - 'doc_type': parsed_order.get('doc_type'), - }) - action = self.env['ir.actions.act_window'].for_xml_id( - 'sale_order_import', 'sale_order_import_action') - action['res_id'] = self.id + self.write( + { + "commercial_partner_id": commercial_partner.id, + "partner_shipping_id": partner_shipping_id, + "state": "update", + "sale_id": default_sale_id, + "doc_type": parsed_order.get("doc_type"), + } + ) + action = self.env["ir.actions.act_window"].for_xml_id( + "sale_order_import", "sale_order_import_action" + ) + action["res_id"] = self.id return action else: - return self.create_order_return_action( - parsed_order, self.order_filename) + return self.create_order_return_action(parsed_order, self.order_filename) def create_order_button(self): self.ensure_one() parsed_order = self.parse_order( - b64decode(self.order_file), self.order_filename, - self.partner_id) - return self.create_order_return_action( - parsed_order, self.order_filename) + b64decode(self.order_file), self.order_filename, self.partner_id + ) + return self.create_order_return_action(parsed_order, self.order_filename) def create_order_return_action(self, parsed_order, order_filename): self.ensure_one() - order = self.create_order( - parsed_order, self.price_source, order_filename) - order.message_post(body=_( - "Created automatically via file import (%s).") - % self.order_filename) - action = self.env['ir.actions.act_window'].for_xml_id( - 'sale', 'action_quotations') - action.update({ - 'view_mode': 'form,tree,calendar,graph', - 'views': False, - 'view_id': False, - 'res_id': order.id, - }) + order = self.create_order(parsed_order, self.price_source, order_filename) + order.message_post( + body=_("Created automatically via file import (%s).") % self.order_filename + ) + action = self.env["ir.actions.act_window"].for_xml_id( + "sale", "action_quotations" + ) + action.update( + { + "view_mode": "form,tree,calendar,graph", + "views": False, + "view_id": False, + "res_id": order.id, + } + ) return action @api.model def _prepare_update_order_vals(self, parsed_order, order, partner): - bdio = self.env['business.document.import'] + bdio = self.env["business.document.import"] partner = bdio._match_partner( - parsed_order['partner'], parsed_order['chatter_msg'], - partner_type='customer') - vals = {'partner_id': partner.id} - if parsed_order.get('ship_to'): + parsed_order["partner"], + parsed_order["chatter_msg"], + partner_type="customer", + ) + vals = {"partner_id": partner.id} + if parsed_order.get("ship_to"): shipping_partner = bdio._match_shipping_partner( - parsed_order['ship_to'], partner, parsed_order['chatter_msg']) - vals['partner_shipping_id'] = shipping_partner.id - if parsed_order.get('order_ref'): - vals['client_order_ref'] = parsed_order['order_ref'] + parsed_order["ship_to"], partner, parsed_order["chatter_msg"] + ) + vals["partner_shipping_id"] = shipping_partner.id + if parsed_order.get("order_ref"): + vals["client_order_ref"] = parsed_order["order_ref"] return vals @api.model def _prepare_create_order_line( - self, product, uom, order, import_line, price_source): + self, product, uom, order, import_line, price_source + ): """the 'order' arg can be a recordset (in case of an update of a sale order) or a dict (in case of the creation of a new sale order)""" - solo = self.env['sale.order.line'] + solo = self.env["sale.order.line"] vals = { - 'product_id': product.id, - 'product_uom_qty': import_line['qty'], - 'product_uom': uom.id, + "product_id": product.id, + "product_uom_qty": import_line["qty"], + "product_uom": uom.id, } - if price_source == 'order': - vals['price_unit'] = import_line['price_unit'] # TODO : fix - elif price_source == 'pricelist': + if price_source == "order": + vals["price_unit"] = import_line["price_unit"] # TODO : fix + elif price_source == "pricelist": # product_id_change is played in the inherit of create() # of sale.order.line cf odoo/addons/sale/models/sale.py # but it is not enough: we also need to play _onchange_discount() # to have the right discount for pricelist - vals['order_id'] = order - vals = solo.play_onchanges(vals, ['product_id']) - vals.pop('order_id') + vals["order_id"] = order + vals = solo.play_onchanges(vals, ["product_id"]) + vals.pop("order_id") return vals @api.model def update_order_lines(self, parsed_order, order, price_source): - chatter = parsed_order['chatter_msg'] - solo = self.env['sale.order.line'] - dpo = self.env['decimal.precision'] - bdio = self.env['business.document.import'] - qty_prec = dpo.precision_get('Product UoS') - price_prec = dpo.precision_get('Product Price') + chatter = parsed_order["chatter_msg"] + solo = self.env["sale.order.line"] + dpo = self.env["decimal.precision"] + bdio = self.env["business.document.import"] + qty_prec = dpo.precision_get("Product UoS") + price_prec = dpo.precision_get("Product Price") existing_lines = [] for oline in order.order_line: # compute price unit without tax price_unit = 0.0 - if not float_is_zero( - oline.product_uom_qty, precision_digits=qty_prec): + if not float_is_zero(oline.product_uom_qty, precision_digits=qty_prec): qty = float(oline.product_uom_qty) price_unit = oline.price_subtotal / qty - existing_lines.append({ - 'product': oline.product_id or False, - 'name': oline.name, - 'qty': oline.product_uom_qty, - 'uom': oline.product_uom, - 'line': oline, - 'price_unit': price_unit, - }) + existing_lines.append( + { + "product": oline.product_id or False, + "name": oline.name, + "qty": oline.product_uom_qty, + "uom": oline.product_uom, + "line": oline, + "price_unit": price_unit, + } + ) compare_res = bdio.compare_lines( - existing_lines, parsed_order['lines'], chatter, - qty_precision=qty_prec, seller=False) + existing_lines, + parsed_order["lines"], + chatter, + qty_precision=qty_prec, + seller=False, + ) # NOW, we start to write/delete/create the order lines - for oline, cdict in compare_res['to_update'].items(): + for oline, cdict in compare_res["to_update"].items(): write_vals = {} # TODO: add support for price_source == order - if cdict.get('qty'): - chatter.append(_( - "The quantity has been updated on the order line " - "with product '%s' from %s to %s %s") % ( + if cdict.get("qty"): + chatter.append( + _( + "The quantity has been updated on the order line " + "with product '%s' from %s to %s %s" + ) + % ( oline.product_id.display_name, - cdict['qty'][0], cdict['qty'][1], - oline.product_uom.name)) - write_vals['product_uom_qty'] = cdict['qty'][1] - if price_source != 'order': + cdict["qty"][0], + cdict["qty"][1], + oline.product_uom.name, + ) + ) + write_vals["product_uom_qty"] = cdict["qty"][1] + if price_source != "order": new_price_unit = order.pricelist_id.with_context( - date=order.date_order, - uom=oline.product_uom.id).price_get( - oline.product_id.id, write_vals['product_uom_qty'], - order.partner_id.id)[order.pricelist_id.id] + date=order.date_order, uom=oline.product_uom.id + ).price_get( + oline.product_id.id, + write_vals["product_uom_qty"], + order.partner_id.id, + )[ + order.pricelist_id.id + ] if float_compare( - new_price_unit, oline.price_unit, - precision_digits=price_prec): - chatter.append(_( - "The unit price has been updated on the order " - "line with product '%s' from %s to %s %s") % ( + new_price_unit, oline.price_unit, precision_digits=price_prec + ): + chatter.append( + _( + "The unit price has been updated on the order " + "line with product '%s' from %s to %s %s" + ) + % ( oline.product_id.display_name, - oline.price_unit, new_price_unit, - order.currency_id.name)) - write_vals['price_unit'] = new_price_unit + oline.price_unit, + new_price_unit, + order.currency_id.name, + ) + ) + write_vals["price_unit"] = new_price_unit if write_vals: oline.write(write_vals) - if compare_res['to_remove']: + if compare_res["to_remove"]: to_remove_label = [ - '%s %s x %s' % ( - l.product_uom_qty, l.product_uom.name, l.product_id.name) - for l in compare_res['to_remove']] - chatter.append(_( - "%d order line(s) deleted: %s") % ( - len(compare_res['to_remove']), - ', '.join(to_remove_label))) - compare_res['to_remove'].unlink() - if compare_res['to_add']: + "%s %s x %s" + % (l.product_uom_qty, l.product_uom.name, l.product_id.name) + for l in compare_res["to_remove"] + ] + chatter.append( + _("%d order line(s) deleted: %s") + % (len(compare_res["to_remove"]), ", ".join(to_remove_label)) + ) + compare_res["to_remove"].unlink() + if compare_res["to_add"]: to_create_label = [] - for add in compare_res['to_add']: + for add in compare_res["to_add"]: line_vals = self._prepare_create_order_line( - add['product'], add['uom'], order, add['import_line'], - price_source) - line_vals['order_id'] = order.id + add["product"], add["uom"], order, add["import_line"], price_source + ) + line_vals["order_id"] = order.id new_line = solo.create(line_vals) - to_create_label.append('%s %s x %s' % ( - new_line.product_uom_qty, - new_line.product_uom.name, - new_line.name)) - chatter.append(_("%d new order line(s) created: %s") % ( - len(compare_res['to_add']), ', '.join(to_create_label))) + to_create_label.append( + "%s %s x %s" + % ( + new_line.product_uom_qty, + new_line.product_uom.name, + new_line.name, + ) + ) + chatter.append( + _("%d new order line(s) created: %s") + % (len(compare_res["to_add"]), ", ".join(to_create_label)) + ) return True def update_order_button(self): self.ensure_one() - bdio = self.env['business.document.import'] + bdio = self.env["business.document.import"] order = self.sale_id if not order: - raise UserError(_('You must select a quotation to update.')) + raise UserError(_("You must select a quotation to update.")) parsed_order = self.parse_order( - b64decode(self.order_file), self.order_filename, - self.partner_id) + b64decode(self.order_file), self.order_filename, self.partner_id + ) currency = bdio._match_currency( - parsed_order.get('currency'), parsed_order['chatter_msg']) + parsed_order.get("currency"), parsed_order["chatter_msg"] + ) if currency != order.currency_id: - raise UserError(_( - "The currency of the imported order (%s) is different from " - "the currency of the existing order (%s)") % ( - currency.name, order.currency_id.name)) + raise UserError( + _( + "The currency of the imported order (%s) is different from " + "the currency of the existing order (%s)" + ) + % (currency.name, order.currency_id.name) + ) vals = self._prepare_update_order_vals( - parsed_order, order, self.commercial_partner_id) + parsed_order, order, self.commercial_partner_id + ) if vals: order.write(vals) self.update_order_lines(parsed_order, order, self.price_source) bdio.post_create_or_update(parsed_order, order) logger.info( - 'Quotation ID %d updated via import of file %s', order.id, - self.order_filename) - order.message_post(body=_( - "This quotation has been updated automatically via the import of " - "file %s") % self.order_filename) - action = self.env['ir.actions.act_window'].for_xml_id( - 'sale', 'action_quotations') - action.update({ - 'view_mode': 'form,tree,calendar,graph', - 'views': False, - 'view_id': False, - 'res_id': order.id, - }) + "Quotation ID %d updated via import of file %s", + order.id, + self.order_filename, + ) + order.message_post( + body=_( + "This quotation has been updated automatically via the import of " + "file %s" + ) + % self.order_filename + ) + action = self.env["ir.actions.act_window"].for_xml_id( + "sale", "action_quotations" + ) + action.update( + { + "view_mode": "form,tree,calendar,graph", + "views": False, + "view_id": False, + "res_id": order.id, + } + ) return action diff --git a/sale_order_import/wizard/sale_order_import_view.xml b/sale_order_import/wizard/sale_order_import_view.xml index 6053d50b8f..25858084c1 100644 --- a/sale_order_import/wizard/sale_order_import_view.xml +++ b/sale_order_import/wizard/sale_order_import_view.xml @@ -1,76 +1,107 @@ - + - - - - sale.order.import.form - sale.order.import - -
- - - - -
-

Upload below the customer order or request for quotation as CSV, XML or PDF file. When you click on the Import button:

-
    -
  1. If it is a CSV file, you will have to manually select the customer. The CSV file should have 2 columns: the product reference or EAN13 (1st col) and then the product quantity (2nd col). It shouldn't have any header line and use semi-colon as field separator. The quantity shouldn't use any thousand separator ; if it is a decimal value, it should use dot as the decimal separator.
  2. -
  3. If it is an XML file, Odoo will parse it if the module that adds support for this XML format is installed. For the Universal Business Language format (UBL), you should install the module sale_order_import_ubl.
  4. -
  5. If it is a PDF file, Odoo will try to find an XML file in the attachments of the PDF file and then use this XML file to create the quotation.
  6. -
-
-
- -
-

Some quotations have been found for this customer ; one of them may correspond to the order or RFQ that you are importing. You can either select an existing quotation to update or create a new one.

-
-
- - - - - - - - - - - - - - - -
-
-
-
-
- - - Import RFQ or Order - sale.order.import - form - new - {'sale_order_show_amount': True} - - - - + + sale.order.import.form + sale.order.import + +
+ + + + +
+

Upload below the customer order or request for quotation as CSV, XML or PDF file. When you click on the Import button:

+
    +
  1. If it is a CSV file, you will have to manually select the customer. The CSV file should have 2 columns: the product reference or EAN13 (1st col) and then the product quantity (2nd col). It shouldn't have any header line and use semi-colon as field separator. The quantity shouldn't use any thousand separator ; if it is a decimal value, it should use dot as the decimal separator.
  2. +
  3. If it is an XML file, Odoo will parse it if the module that adds support for this XML format is installed. For the Universal Business Language format (UBL), you should install the module sale_order_import_ubl.
  4. +
  5. If it is a PDF file, Odoo will try to find an XML file in the attachments of the PDF file and then use this XML file to create the quotation.
  6. +
+
+
+ +
+

Some quotations have been found for this customer ; one of them may correspond to the order or RFQ that you are importing. You can either select an existing quotation to update or create a new one.

+
+
+ + + + + + + + + + + + + + + +
+
+
+
+
+ + Import RFQ or Order + sale.order.import + form + new + {'sale_order_show_amount': True} + +
From 78378d25a7b2ad939d59bd84d7a8f8c713e49d33 Mon Sep 17 00:00:00 2001 From: sebalix Date: Thu, 14 May 2020 14:45:25 +0200 Subject: [PATCH 21/91] [MIG] sale_order_import: Migration to 13.0 --- sale_order_import/__manifest__.py | 2 +- sale_order_import/models/sale.py | 2 +- sale_order_import/wizard/sale_order_import.py | 3 --- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/sale_order_import/__manifest__.py b/sale_order_import/__manifest__.py index 111312c5fd..704850ab66 100644 --- a/sale_order_import/__manifest__.py +++ b/sale_order_import/__manifest__.py @@ -3,7 +3,7 @@ { "name": "Sale Order Import", - "version": "12.0.1.0.0", + "version": "13.0.1.0.0", "category": "Sales Management", "license": "AGPL-3", "summary": "Import RFQ or sale orders from files", diff --git a/sale_order_import/models/sale.py b/sale_order_import/models/sale.py index 3d1e9f094c..efd22a60a5 100644 --- a/sale_order_import/models/sale.py +++ b/sale_order_import/models/sale.py @@ -9,7 +9,7 @@ class SaleOrder(models.Model): def name_get(self): """Add amount_untaxed in name_get of sale orders""" - res = super(SaleOrder, self).name_get() + res = super().name_get() if self._context.get("sale_order_show_amount"): new_res = [] for (sale_id, name) in res: diff --git a/sale_order_import/wizard/sale_order_import.py b/sale_order_import/wizard/sale_order_import.py index 3d48b05501..f91dd22be5 100644 --- a/sale_order_import/wizard/sale_order_import.py +++ b/sale_order_import/wizard/sale_order_import.py @@ -48,9 +48,6 @@ class SaleOrderImport(models.TransientModel): partner_shipping_id = fields.Many2one( "res.partner", string="Shipping Address", readonly=True ) - # amount_untaxed = fields.Float( - # string='Total Untaxed', digits=dp.get_precision('Account'), - # readonly=True) sale_id = fields.Many2one("sale.order", string="Quotation to Update") @api.onchange("order_file") From 49f3cac61bb94283a20f83c72b4d97351fff53cc Mon Sep 17 00:00:00 2001 From: sebalix Date: Thu, 14 May 2020 14:45:52 +0200 Subject: [PATCH 22/91] [UPD] README.rst --- sale_order_import/README.rst | 11 ++++++----- sale_order_import/static/description/index.html | 7 ++++--- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/sale_order_import/README.rst b/sale_order_import/README.rst index 32e959db7d..7c1ad5c6a8 100644 --- a/sale_order_import/README.rst +++ b/sale_order_import/README.rst @@ -14,13 +14,13 @@ Sale Order Import :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :alt: License: AGPL-3 .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fedi-lightgray.png?logo=github - :target: https://github.com/OCA/edi/tree/11.0/sale_order_import + :target: https://github.com/OCA/edi/tree/13.0/sale_order_import :alt: OCA/edi .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png - :target: https://translation.odoo-community.org/projects/edi-11-0/edi-11-0-sale_order_import + :target: https://translation.odoo-community.org/projects/edi-13-0/edi-13-0-sale_order_import :alt: Translate me on Weblate .. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png - :target: https://runbot.odoo-community.org/runbot/226/11.0 + :target: https://runbot.odoo-community.org/runbot/226/13.0 :alt: Try me on Runbot |badge1| |badge2| |badge3| |badge4| |badge5| @@ -54,7 +54,7 @@ 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 smashing it by providing a detailed and welcomed -`feedback `_. +`feedback `_. Do not contact contributors directly about support or help with technical issues. @@ -71,6 +71,7 @@ Contributors * Alexis de Lattre * Dennis Sluijk +* Andrea Stirpe Maintainers ~~~~~~~~~~~ @@ -85,6 +86,6 @@ 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/edi `_ project on GitHub. +This module is part of the `OCA/edi `_ project on GitHub. You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/sale_order_import/static/description/index.html b/sale_order_import/static/description/index.html index 53baadf52c..a30867b02e 100644 --- a/sale_order_import/static/description/index.html +++ b/sale_order_import/static/description/index.html @@ -367,7 +367,7 @@

Sale Order Import

!! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --> -

Beta License: AGPL-3 OCA/edi Translate me on Weblate Try me on Runbot

+

Beta License: AGPL-3 OCA/edi Translate me on Weblate Try me on Runbot

This module adds support for the import of electronic RFQ or orders. This module provides the base methods to import electronic orders ; it requires additional modules to support specific order formats:

  • module sale_order_import_csv: adds support for CSV orders,
  • @@ -401,7 +401,7 @@

    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 smashing it by providing a detailed and welcomed -feedback.

    +feedback.

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

    @@ -417,6 +417,7 @@

    Contributors

    @@ -426,7 +427,7 @@

    Maintainers

    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/edi project on GitHub.

    +

    This module is part of the OCA/edi project on GitHub.

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

    From 64d1c7a86d832712afae4004b091ef995823c6e4 Mon Sep 17 00:00:00 2001 From: oca-travis Date: Thu, 9 Jul 2020 09:53:41 +0000 Subject: [PATCH 23/91] [UPD] Update sale_order_import.pot --- sale_order_import/i18n/sale_order_import.pot | 205 +++++++++++-------- 1 file changed, 121 insertions(+), 84 deletions(-) diff --git a/sale_order_import/i18n/sale_order_import.pot b/sale_order_import/i18n/sale_order_import.pot index 99bb03f99e..e55dbfa3be 100644 --- a/sale_order_import/i18n/sale_order_import.pot +++ b/sale_order_import/i18n/sale_order_import.pot @@ -1,12 +1,12 @@ # Translation of Odoo Server. # This file contains the translation of the following modules: -# * sale_order_import +# * sale_order_import # msgid "" msgstr "" -"Project-Id-Version: Odoo Server 11.0\n" +"Project-Id-Version: Odoo Server 13.0\n" "Report-Msgid-Bugs-To: \n" -"Last-Translator: <>\n" +"Last-Translator: \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -14,115 +14,130 @@ msgstr "" "Plural-Forms: \n" #. module: sale_order_import -#: code:addons/sale_order_import/models/sale.py:20 -#: code:addons/sale_order_import/tests/test_sale_order.py:8 +#: code:addons/sale_order_import/models/sale.py:0 +#: code:addons/sale_order_import/tests/test_sale_order.py:0 #, python-format msgid " Amount w/o tax: %s %s)" msgstr "" #. module: sale_order_import -#: code:addons/sale_order_import/wizard/sale_order_import.py:452 +#: code:addons/sale_order_import/wizard/sale_order_import.py:0 #, python-format msgid "%d new order line(s) created: %s" msgstr "" #. module: sale_order_import -#: code:addons/sale_order_import/wizard/sale_order_import.py:435 +#: code:addons/sale_order_import/wizard/sale_order_import.py:0 #, python-format msgid "%d order line(s) deleted: %s" msgstr "" #. module: sale_order_import -#: code:addons/sale_order_import/wizard/sale_order_import.py:179 +#: code:addons/sale_order_import/wizard/sale_order_import.py:0 #, python-format -msgid "An order of customer '%s' with reference '%s' already exists: %s (state: %s)" +msgid "" +"An order of customer '%s' with reference '%s' already exists: %s (state: %s)" msgstr "" #. module: sale_order_import -#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import_price_source +#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import__price_source msgid "Apply Prices From" msgstr "" #. module: sale_order_import -#: model:ir.ui.view,arch_db:sale_order_import.sale_order_import_form +#: model_terms:ir.ui.view,arch_db:sale_order_import.sale_order_import_form msgid "Cancel" msgstr "" #. module: sale_order_import -#: model:ir.ui.view,arch_db:sale_order_import.sale_order_import_form +#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import__commercial_partner_id +msgid "Commercial Entity" +msgstr "" + +#. module: sale_order_import +#: model_terms:ir.ui.view,arch_db:sale_order_import.sale_order_import_form msgid "Create New" msgstr "" #. module: sale_order_import -#: code:addons/sale_order_import/wizard/sale_order_import.py:322 +#: code:addons/sale_order_import/wizard/sale_order_import.py:0 #, python-format msgid "Created automatically via file import (%s)." msgstr "" #. module: sale_order_import -#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import_create_uid +#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import__create_uid msgid "Created by" msgstr "" #. module: sale_order_import -#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import_create_date +#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import__create_date msgid "Created on" msgstr "" #. module: sale_order_import -#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import_csv_import +#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import__csv_import msgid "Csv Import" msgstr "" #. module: sale_order_import -#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import_commercial_partner_id -#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import_partner_id +#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import__partner_id msgid "Customer" msgstr "" #. module: sale_order_import -#: selection:sale.order.import,price_source:0 +#: model:ir.model.fields.selection,name:sale_order_import.selection__sale_order_import__price_source__order msgid "Customer Order" msgstr "" #. module: sale_order_import -#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import_display_name +#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import__display_name msgid "Display Name" msgstr "" #. module: sale_order_import -#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import_doc_type +#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import__doc_type msgid "Document Type" msgstr "" #. module: sale_order_import -#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import_order_filename +#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import__order_filename msgid "Filename" msgstr "" #. module: sale_order_import -#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import_id +#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import__id msgid "ID" msgstr "" #. module: sale_order_import -#: model:ir.ui.view,arch_db:sale_order_import.sale_order_import_form -msgid "If it is a CSV file, you will have to manually select the customer. The CSV file should have 2 columns: the product reference or EAN13 (1st col) and then the product quantity (2nd col). It shouldn't have any header line and use semi-colon as field separator. The quantity shouldn't use any thousand separator ; if it is a decimal value, it should use dot as the decimal separator." +#: model_terms:ir.ui.view,arch_db:sale_order_import.sale_order_import_form +msgid "" +"If it is a CSV file, you will have to manually select the customer. The CSV " +"file should have 2 columns: the product reference or EAN13 (1st col) and " +"then the product quantity (2nd col). It shouldn't have any header line and " +"use semi-colon as field separator. The quantity shouldn't use any thousand " +"separator ; if it is a decimal value, it should use dot as the decimal " +"separator." msgstr "" #. module: sale_order_import -#: model:ir.ui.view,arch_db:sale_order_import.sale_order_import_form -msgid "If it is a PDF file, Odoo will try to find an XML file in the attachments of the PDF file and then use this XML file to create the quotation." +#: model_terms:ir.ui.view,arch_db:sale_order_import.sale_order_import_form +msgid "" +"If it is a PDF file, Odoo will try to find an XML file in the attachments of" +" the PDF file and then use this XML file to create the quotation." msgstr "" #. module: sale_order_import -#: model:ir.ui.view,arch_db:sale_order_import.sale_order_import_form -msgid "If it is an XML file, Odoo will parse it if the module that adds support for this XML format is installed. For the" +#: model_terms:ir.ui.view,arch_db:sale_order_import.sale_order_import_form +msgid "" +"If it is an XML file, Odoo will parse it if the module that adds support for" +" this XML format is installed. For the" msgstr "" #. module: sale_order_import -#: model:ir.ui.view,arch_db:sale_order_import.sale_order_import_form -#: selection:sale.order.import,state:0 +#: model:ir.model.fields.selection,name:sale_order_import.selection__sale_order_import__state__import +#: model_terms:ir.ui.view,arch_db:sale_order_import.sale_order_import_form msgid "Import" msgstr "" @@ -133,58 +148,53 @@ msgid "Import RFQ or Order" msgstr "" #. module: sale_order_import -#: model:ir.ui.view,arch_db:sale_order_import.sale_order_import_form +#: model_terms:ir.ui.view,arch_db:sale_order_import.sale_order_import_form msgid "Import Sale Orders" msgstr "" #. module: sale_order_import -#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import___last_update +#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import____last_update msgid "Last Modified on" msgstr "" #. module: sale_order_import -#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import_write_uid +#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import__write_uid msgid "Last Updated by" msgstr "" #. module: sale_order_import -#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import_write_date +#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import__write_date msgid "Last Updated on" msgstr "" #. module: sale_order_import -#: code:addons/sale_order_import/wizard/sale_order_import.py:238 +#: code:addons/sale_order_import/wizard/sale_order_import.py:0 #, python-format msgid "Missing customer" msgstr "" #. module: sale_order_import -#: selection:sale.order.import,price_source:0 +#: model:ir.model.fields.selection,name:sale_order_import.selection__sale_order_import__price_source__pricelist msgid "Pricelist" msgstr "" #. module: sale_order_import -#: model:ir.model,name:sale_order_import.model_sale_order -msgid "Quotation" -msgstr "" - -#. module: sale_order_import -#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import_sale_id +#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import__sale_id msgid "Quotation to Update" msgstr "" #. module: sale_order_import -#: selection:sale.order.import,doc_type:0 +#: model:ir.model.fields.selection,name:sale_order_import.selection__sale_order_import__doc_type__rfq msgid "Request For Quotation" msgstr "" #. module: sale_order_import -#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import_order_file +#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import__order_file msgid "Request for Quotation or Order" msgstr "" #. module: sale_order_import -#: selection:sale.order.import,doc_type:0 +#: model:ir.model.fields.selection,name:sale_order_import.selection__sale_order_import__doc_type__order msgid "Sale Order" msgstr "" @@ -194,128 +204,155 @@ msgid "Sale Order Import from Files" msgstr "" #. module: sale_order_import -#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import_partner_shipping_id +#: model:ir.model,name:sale_order_import.model_sale_order +msgid "Sales Order" +msgstr "" + +#. module: sale_order_import +#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import__partner_shipping_id msgid "Shipping Address" msgstr "" #. module: sale_order_import -#: model:ir.ui.view,arch_db:sale_order_import.sale_order_import_form -msgid "Some quotations have been found for this customer ; one of them may correspond to the order or RFQ that you are importing. You can either select an existing quotation to update or create a new one." +#: model_terms:ir.ui.view,arch_db:sale_order_import.sale_order_import_form +msgid "" +"Some quotations have been found for this customer ; one of them may " +"correspond to the order or RFQ that you are importing. You can either select" +" an existing quotation to update or create a new one." msgstr "" #. module: sale_order_import -#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import_state +#: model:ir.model.fields,field_description:sale_order_import.field_sale_order_import__state msgid "State" msgstr "" #. module: sale_order_import -#: code:addons/sale_order_import/wizard/sale_order_import.py:468 +#: code:addons/sale_order_import/wizard/sale_order_import.py:0 #, python-format -msgid "The currency of the imported order (%s) is different from the currency of the existing order (%s)" +msgid "" +"The currency of the imported order (%s) is different from the currency of " +"the existing order (%s)" msgstr "" #. module: sale_order_import -#: code:addons/sale_order_import/wizard/sale_order_import.py:166 +#: code:addons/sale_order_import/wizard/sale_order_import.py:0 #, python-format -msgid "The customer '%s' has a pricelist '%s' but the currency of this order is '%s'." +msgid "" +"The customer '%s' has a pricelist '%s' but the currency of this order is " +"'%s'." msgstr "" #. module: sale_order_import -#: code:addons/sale_order_import/wizard/sale_order_import.py:405 +#: code:addons/sale_order_import/wizard/sale_order_import.py:0 #, python-format -msgid "The quantity has been updated on the order line with product '%s' from %s to %s %s" +msgid "" +"The quantity has been updated on the order line with product '%s' from %s to" +" %s %s" msgstr "" #. module: sale_order_import -#: code:addons/sale_order_import/wizard/sale_order_import.py:421 +#: code:addons/sale_order_import/wizard/sale_order_import.py:0 #, python-format -msgid "The unit price has been updated on the order line with product '%s' from %s to %s %s" +msgid "" +"The unit price has been updated on the order line with product '%s' from %s " +"to %s %s" msgstr "" #. module: sale_order_import -#: code:addons/sale_order_import/wizard/sale_order_import.py:108 +#: code:addons/sale_order_import/wizard/sale_order_import.py:0 #, python-format msgid "There are no embedded XML file in this PDF file." msgstr "" #. module: sale_order_import -#: code:addons/sale_order_import/wizard/sale_order_import.py:64 -#: code:addons/sale_order_import/wizard/sale_order_import.py:244 +#: code:addons/sale_order_import/wizard/sale_order_import.py:0 +#: code:addons/sale_order_import/wizard/sale_order_import.py:0 #, python-format msgid "This XML file is not XML-compliant" msgstr "" #. module: sale_order_import -#: code:addons/sale_order_import/wizard/sale_order_import.py:75 -#: code:addons/sale_order_import/wizard/sale_order_import.py:254 +#: code:addons/sale_order_import/wizard/sale_order_import.py:0 +#: code:addons/sale_order_import/wizard/sale_order_import.py:0 #, python-format -msgid "This file '%s' is not recognised as a CSV, XML nor PDF file. Please check the file and it's extension." +msgid "" +"This file '%s' is not recognised as a CSV, XML nor PDF file. Please check " +"the file and it's extension." msgstr "" #. module: sale_order_import -#: code:addons/sale_order_import/wizard/sale_order_import.py:279 +#: code:addons/sale_order_import/wizard/sale_order_import.py:0 #, python-format msgid "This order doesn't have any line !" msgstr "" #. module: sale_order_import -#: code:addons/sale_order_import/wizard/sale_order_import.py:481 +#: code:addons/sale_order_import/wizard/sale_order_import.py:0 #, python-format -msgid "This quotation has been updated automatically via the import of file %s" +msgid "" +"This quotation has been updated automatically via the import of file %s" msgstr "" #. module: sale_order_import -#: code:addons/sale_order_import/wizard/sale_order_import.py:97 +#: code:addons/sale_order_import/wizard/sale_order_import.py:0 #, python-format -msgid "This type of CSV order is not supported. Did you install the module to support CSV orders?" +msgid "" +"This type of CSV order is not supported. Did you install the module to " +"support CSV orders?" msgstr "" #. module: sale_order_import -#: code:addons/sale_order_import/wizard/sale_order_import.py:90 -#: code:addons/sale_order_import/wizard/sale_order_import.py:118 +#: code:addons/sale_order_import/wizard/sale_order_import.py:0 +#: code:addons/sale_order_import/wizard/sale_order_import.py:0 #, python-format -msgid "This type of XML RFQ/order is not supported. Did you install the module to support this XML format?" +msgid "" +"This type of XML RFQ/order is not supported. Did you install the module to " +"support this XML format?" msgstr "" #. module: sale_order_import -#: model:ir.ui.view,arch_db:sale_order_import.sale_order_import_form +#: model_terms:ir.ui.view,arch_db:sale_order_import.sale_order_import_form msgid "Universal Business Language" msgstr "" #. module: sale_order_import -#: code:addons/sale_order_import/wizard/sale_order_import.py:74 +#: code:addons/sale_order_import/wizard/sale_order_import.py:0 #, python-format msgid "Unsupported file format" msgstr "" #. module: sale_order_import -#: selection:sale.order.import,state:0 +#: model:ir.model.fields.selection,name:sale_order_import.selection__sale_order_import__state__update msgid "Update" msgstr "" #. module: sale_order_import -#: model:ir.ui.view,arch_db:sale_order_import.sale_order_import_form +#: model_terms:ir.ui.view,arch_db:sale_order_import.sale_order_import_form msgid "Update Existing" msgstr "" #. module: sale_order_import -#: model:ir.model.fields,help:sale_order_import.field_sale_order_import_order_file -msgid "Upload a Request for Quotation or an Order file. Supported formats: CSV, XML and PDF (PDF with an embeded XML file)." +#: model:ir.model.fields,help:sale_order_import.field_sale_order_import__order_file +msgid "" +"Upload a Request for Quotation or an Order file. Supported formats: CSV, XML" +" and PDF (PDF with an embeded XML file)." msgstr "" #. module: sale_order_import -#: model:ir.ui.view,arch_db:sale_order_import.sale_order_import_form -msgid "Upload below the customer order or request for quotation as CSV, XML or PDF file. When you click on the Import button:" +#: model_terms:ir.ui.view,arch_db:sale_order_import.sale_order_import_form +msgid "" +"Upload below the customer order or request for quotation as CSV, XML or PDF " +"file. When you click on the Import button:" msgstr "" #. module: sale_order_import -#: code:addons/sale_order_import/wizard/sale_order_import.py:461 +#: code:addons/sale_order_import/wizard/sale_order_import.py:0 #, python-format msgid "You must select a quotation to update." msgstr "" #. module: sale_order_import -#: model:ir.ui.view,arch_db:sale_order_import.sale_order_import_form -msgid "format (UBL), you should install the module sale_order_import_ubl." +#: model_terms:ir.ui.view,arch_db:sale_order_import.sale_order_import_form +msgid "" +"format (UBL), you should install the module sale_order_import_ubl." msgstr "" - From 9af622222421f1bffecda519d2c66c27c20de2a3 Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Thu, 9 Jul 2020 10:01:16 +0000 Subject: [PATCH 24/91] [UPD] README.rst --- sale_order_import/static/description/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sale_order_import/static/description/index.html b/sale_order_import/static/description/index.html index a30867b02e..31c7dc8dc5 100644 --- a/sale_order_import/static/description/index.html +++ b/sale_order_import/static/description/index.html @@ -3,7 +3,7 @@ - + Sale Order Import