From 8cce7538e6f81adc8411288430a350e98ef1349a Mon Sep 17 00:00:00 2001 From: Hugo Santos Date: Tue, 6 Mar 2018 17:59:37 +0100 Subject: [PATCH] [MIG] connector_magento to v11 --- .travis_build_doc | 7 ++ .travis_push_doc | 25 +++++ connector_magento/README.rst | 2 +- connector_magento/__manifest__.py | 2 +- .../components/backend_adapter.py | 6 +- connector_magento/components/importer.py | 6 +- .../data/connector_magento_data.xml | 12 +-- connector_magento/doc/conf.py | 16 +-- .../models/account_invoice/common.py | 4 +- .../models/account_invoice/exporter.py | 4 +- .../models/magento_backend/common.py | 16 +-- connector_magento/models/partner/common.py | 4 +- connector_magento/models/partner/importer.py | 3 +- connector_magento/models/product/common.py | 8 +- connector_magento/models/product/importer.py | 21 ++-- .../models/product_category/common.py | 4 +- .../models/product_category/importer.py | 2 +- connector_magento/models/sale_order/common.py | 4 +- .../models/sale_order/importer.py | 14 +-- .../models/stock_picking/common.py | 4 +- .../models/stock_picking/exporter.py | 4 +- connector_magento/tests/common.py | 28 ++--- .../tests/test_concurrent_sync.py | 4 +- .../tests/test_export_invoice.py | 10 +- .../tests/test_export_picking.py | 102 ++++++++++++------ .../tests/test_export_product_stock.py | 13 ++- .../tests/test_import_partner.py | 4 +- .../tests/test_import_product_image.py | 24 +++-- .../tests/test_related_action.py | 6 +- connector_magento/tests/test_sale_order.py | 23 ++-- oca_dependencies.txt | 8 ++ requirements.txt | 1 + 32 files changed, 238 insertions(+), 153 deletions(-) create mode 100755 .travis_build_doc create mode 100755 .travis_push_doc create mode 100644 oca_dependencies.txt create mode 100644 requirements.txt diff --git a/.travis_build_doc b/.travis_build_doc new file mode 100755 index 000000000..de315389d --- /dev/null +++ b/.travis_build_doc @@ -0,0 +1,7 @@ +#!/bin/bash + +cd ${TRAVIS_BUILD_DIR} + +sphinx-build -b html ./connector_magento/doc/ ${HOME}/doc +# sphinx-intl build +# sphinx-build -D language=fr -b html ./connector_magento/doc/ ${HOME}/doc/fr diff --git a/.travis_push_doc b/.travis_push_doc new file mode 100755 index 000000000..d8ca39f19 --- /dev/null +++ b/.travis_push_doc @@ -0,0 +1,25 @@ +#!/bin/bash + +set -o errexit -o nounset + +if [ $TRAVIS_BRANCH = "11.0" ] && [ $TRAVIS_PULL_REQUEST = false ]; then + cd ${TRAVIS_BUILD_DIR} + rev=$(git rev-parse --short HEAD) + + cd ${HOME}/doc + git init + git config user.name "Guewen Baconnier" + git config user.email "guewen.baconnier@camptocamp.com" + git remote add upstream https://${GH_TOKEN}@github.com/${TRAVIS_REPO_SLUG}.git + git fetch upstream + git reset upstream/gh-pages + + echo "odoo-magento-connector.com" > CNAME + touch .nojekyll + + touch . + git add -A . + git commit -m"rebuild pages at ${rev}" + + git push -q upstream HEAD:gh-pages +fi diff --git a/connector_magento/README.rst b/connector_magento/README.rst index d3fa32ae0..1cc90de33 100644 --- a/connector_magento/README.rst +++ b/connector_magento/README.rst @@ -8,7 +8,7 @@ Magento Connector This is the new release of the Open-Source connector linking Odoo and Magento also known under the name of **Magentoerpconnect**. It is -build on top of the `connector`_ framework. It is is structured so that +build on top of the `connector`_ framework. It is structured so that it can be extended or modified easily from separate addons, a factor of success when the implementations of Magento vary a lot. diff --git a/connector_magento/__manifest__.py b/connector_magento/__manifest__.py index 775a5eba0..ab652a438 100644 --- a/connector_magento/__manifest__.py +++ b/connector_magento/__manifest__.py @@ -4,7 +4,7 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). {'name': 'Magento Connector', - 'version': '10.0.1.0.0', + 'version': '11.0.1.0.0', 'category': 'Connector', 'depends': ['account', 'base_technical_user', diff --git a/connector_magento/components/backend_adapter.py b/connector_magento/components/backend_adapter.py index 1012dad7d..11479365c 100644 --- a/connector_magento/components/backend_adapter.py +++ b/connector_magento/components/backend_adapter.py @@ -4,7 +4,7 @@ import socket import logging -import xmlrpclib +import xmlrpc.client from odoo.addons.component.core import AbstractComponent from odoo.addons.queue_job.exception import RetryableJobError @@ -90,7 +90,7 @@ def call(self, method, arguments): start = datetime.now() try: result = self.api.call(method, arguments) - except: + except Exception: _logger.error("api.call('%s', %s) failed", method, arguments) raise else: @@ -104,7 +104,7 @@ def call(self, method, arguments): raise NetworkRetryableError( 'A network error caused the failure of the job: ' '%s' % err) - except xmlrpclib.ProtocolError as err: + except xmlrpc.client.ProtocolError as err: if err.errcode in [502, # Bad gateway 503, # Service unavailable 504]: # Gateway timeout diff --git a/connector_magento/components/importer.py b/connector_magento/components/importer.py index 26f31381b..a47adac6f 100644 --- a/connector_magento/components/importer.py +++ b/connector_magento/components/importer.py @@ -305,7 +305,7 @@ def run(self, external_id, binding, mapper=None): # find the translatable fields of the model fields = self.model.fields_get() - translatable_fields = [field for field, attrs in fields.iteritems() + translatable_fields = [field for field, attrs in list(fields.items()) if attrs.get('translate')] if mapper is None: @@ -316,9 +316,9 @@ def run(self, external_id, binding, mapper=None): for storeview in lang_storeviews: lang_record = self._get_magento_data(storeview.external_id) map_record = mapper.map_record(lang_record) - record = map_record.values() + record = list(map_record.values()) - data = dict((field, value) for field, value in record.iteritems() + data = dict((field, value) for field, value in list(record.items()) if field in translatable_fields) binding.with_context(connector_no_export=True, diff --git a/connector_magento/data/connector_magento_data.xml b/connector_magento/data/connector_magento_data.xml index dc35b11b7..b5f0be954 100644 --- a/connector_magento/data/connector_magento_data.xml +++ b/connector_magento/data/connector_magento_data.xml @@ -13,7 +13,7 @@ days -1 - + @@ -26,7 +26,7 @@ days -1 - + @@ -39,7 +39,7 @@ days -1 - + @@ -52,7 +52,7 @@ days -1 - + @@ -65,7 +65,7 @@ days -1 - + @@ -78,7 +78,7 @@ hours -1 - + diff --git a/connector_magento/doc/conf.py b/connector_magento/doc/conf.py index 519fc0305..c73c69591 100644 --- a/connector_magento/doc/conf.py +++ b/connector_magento/doc/conf.py @@ -131,8 +131,8 @@ def add_path(*paths): master_doc = 'index' # General information about the project. -project = u'Odoo Magento Connector' -copyright = u'2013-2015, Odoo Community Association (OCA)' +project = 'Odoo Magento Connector' +copyright = '2013-2015, Odoo Community Association (OCA)' # The version info for the project you're documenting, acts as # replacement for |version| and |release|, also used in various other @@ -320,8 +320,8 @@ def add_path(*paths): # start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ('index', 'odoo-magento-connector.tex', - u'Odoo Magento Connector Documentation', - u'Odoo Community Association (OCA)', 'manual'), + 'Odoo Magento Connector Documentation', + 'Odoo Community Association (OCA)', 'manual'), ] # The name of an image file (relative to this directory) to place at the @@ -351,8 +351,8 @@ def add_path(*paths): # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'odoo-magento-connector', - u'Odoo Magento Connector Documentation', - [u'Odoo Community Association (OCA)'], 1) + 'Odoo Magento Connector Documentation', + ['Odoo Community Association (OCA)'], 1) ] # If true, show URL addresses after external links. @@ -366,8 +366,8 @@ def add_path(*paths): # dir menu entry, description, category) texinfo_documents = [ ('index', 'Odoo Magento Connector', - u'Odoo Magento Connector Documentation', - u'Odoo Community Association (OCA)', 'Odoo Magento Connector', + 'Odoo Magento Connector Documentation', + 'Odoo Community Association (OCA)', 'Odoo Magento Connector', 'Connector between Odoo and Magento', 'Miscellaneous'), ] diff --git a/connector_magento/models/account_invoice/common.py b/connector_magento/models/account_invoice/common.py index 659423d5b..c6b9ceddc 100644 --- a/connector_magento/models/account_invoice/common.py +++ b/connector_magento/models/account_invoice/common.py @@ -4,7 +4,7 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) import logging -import xmlrpclib +import xmlrpc.client from odoo import api, models, fields from odoo.addons.component.core import Component from odoo.addons.queue_job.job import job, related_action @@ -70,7 +70,7 @@ class AccountInvoiceAdapter(Component): def _call(self, method, arguments): try: return super(AccountInvoiceAdapter, self)._call(method, arguments) - except xmlrpclib.Fault as err: + except xmlrpc.client.Fault as err: # this is the error in the Magento API # when the invoice does not exist if err.faultCode == 100: diff --git a/connector_magento/models/account_invoice/exporter.py b/connector_magento/models/account_invoice/exporter.py index a9ac873e0..a161879d7 100644 --- a/connector_magento/models/account_invoice/exporter.py +++ b/connector_magento/models/account_invoice/exporter.py @@ -4,7 +4,7 @@ import logging -import xmlrpclib +import xmlrpc.client from odoo import _ from odoo.addons.component.core import Component @@ -70,7 +70,7 @@ def run(self, binding): external_id = self._export_invoice(magento_order.external_id, lines_info, mail_notification) - except xmlrpclib.Fault as err: + except xmlrpc.client.Fault as err: # When the invoice is already created on Magento, it returns: # # We'll search the Magento invoice ID to store it in Odoo diff --git a/connector_magento/models/magento_backend/common.py b/connector_magento/models/magento_backend/common.py index 4a37d90c8..0e04cea68 100644 --- a/connector_magento/models/magento_backend/common.py +++ b/connector_magento/models/magento_backend/common.py @@ -9,9 +9,10 @@ from datetime import datetime, timedelta from odoo import models, fields, api, _ +from odoo.tools import ustr from odoo.exceptions import UserError -from odoo.addons.connector.checkpoint import checkpoint +from odoo.addons.connector.models.checkpoint import add_checkpoint from ...components.backend_adapter import MagentoLocation, MagentoAPI _logger = logging.getLogger(__name__) @@ -42,6 +43,7 @@ def _get_stock_field_id(self): limit=1) return field + name = fields.Char(string='Name', required=True) version = fields.Selection(selection='select_versions', required=True) location = fields.Char( string='Location', @@ -213,8 +215,8 @@ def work_on(self, model_name, **kwargs): def add_checkpoint(self, record): self.ensure_one() record.ensure_one() - return checkpoint.add_checkpoint(self.env, record._name, record.id, - self._name, self.id) + return add_checkpoint(self.env, record._name, record.id, + self._name, self.id) @api.multi def synchronize_metadata(self): @@ -229,11 +231,11 @@ def synchronize_metadata(self): self.env[model_name].import_batch(backend) return True except Exception as e: - _logger.error(e.message, exc_info=True) + _logger.error(ustr(e)) raise UserError( - _(u"Check your configuration, we can't get the data. " - u"Here is the error:\n%s") % - str(e).decode('utf-8', 'ignore')) + _("Check your configuration, we can't get the data. " + "Here is the error:\n%s") % + ustr(e)) @api.multi def import_partners(self): diff --git a/connector_magento/models/partner/common.py b/connector_magento/models/partner/common.py index 19f34d630..dd0faec60 100644 --- a/connector_magento/models/partner/common.py +++ b/connector_magento/models/partner/common.py @@ -4,7 +4,7 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). import logging -import xmlrpclib +import xmlrpc.client from odoo import models, fields, api from odoo.addons.queue_job.job import job from odoo.addons.component.core import Component @@ -153,7 +153,7 @@ class PartnerAdapter(Component): def _call(self, method, arguments): try: return super(PartnerAdapter, self)._call(method, arguments) - except xmlrpclib.Fault as err: + except xmlrpc.client.Fault as err: # this is the error in the Magento API # when the customer does not exist if err.faultCode == 102: diff --git a/connector_magento/models/partner/importer.py b/connector_magento/models/partner/importer.py index 789f1ea8b..6478a5f3b 100644 --- a/connector_magento/models/partner/importer.py +++ b/connector_magento/models/partner/importer.py @@ -249,7 +249,6 @@ class BaseAddressImportMapper(AbstractComponent): direct = [('postcode', 'zip'), ('city', 'city'), ('telephone', 'phone'), - ('fax', 'fax'), ('company', 'company'), ] @@ -284,7 +283,7 @@ def street(self, record): if len(lines) == 1: result = {'street': lines[0], 'street2': False} elif len(lines) >= 2: - result = {'street': lines[0], 'street2': u' - '.join(lines[1:])} + result = {'street': lines[0], 'street2': ' - '.join(lines[1:])} else: result = {} return result diff --git a/connector_magento/models/product/common.py b/connector_magento/models/product/common.py index 10da819f3..5b84f1945 100644 --- a/connector_magento/models/product/common.py +++ b/connector_magento/models/product/common.py @@ -4,7 +4,7 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). import logging -import xmlrpclib +import xmlrpc.client from collections import defaultdict @@ -19,7 +19,7 @@ def chunks(items, length): - for index in xrange(0, len(items), length): + for index in range(0, len(items), length): yield items[index:index + length] @@ -113,7 +113,7 @@ def recompute_magento_qty(self): for product in self: backends[product.backend_id].add(product.id) - for backend, product_ids in backends.iteritems(): + for backend, product_ids in list(backends.items()): self._recompute_magento_qty_backend(backend, self.browse(product_ids)) return True @@ -189,7 +189,7 @@ class ProductProductAdapter(Component): def _call(self, method, arguments): try: return super(ProductProductAdapter, self)._call(method, arguments) - except xmlrpclib.Fault as err: + except xmlrpc.client.Fault as err: # this is the error in the Magento API # when the product does not exist if err.faultCode == 101: diff --git a/connector_magento/models/product/importer.py b/connector_magento/models/product/importer.py index 937605337..7c4958e91 100644 --- a/connector_magento/models/product/importer.py +++ b/connector_magento/models/product/importer.py @@ -4,7 +4,9 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) import logging -import urllib2 +import urllib.request +import urllib.error +import urllib.parse import base64 import sys @@ -78,17 +80,20 @@ def priority(image): return sorted(images, key=priority) def _get_binary_image(self, image_data): - url = image_data['url'].encode('utf8') + url = image_data['url'] try: - request = urllib2.Request(url) + request = urllib.request.Request(url) if self.backend_record.auth_basic_username \ and self.backend_record.auth_basic_password: base64string = base64.b64encode( - '%s:%s' % (self.backend_record.auth_basic_username, - self.backend_record.auth_basic_password)) - request.add_header("Authorization", "Basic %s" % base64string) - binary = urllib2.urlopen(request) - except urllib2.HTTPError as err: + ("%s:%s" % (self.backend_record.auth_basic_username, + self.backend_record.auth_basic_password) + ).encode('utf-8') + ) + request.add_header("Authorization", "Basic %s" % ( + base64string.decode('utf-8'))) + binary = urllib.request.urlopen(request) + except urllib.error.HTTPError as err: if err.code == 404: # the image is just missing, we skip it return diff --git a/connector_magento/models/product_category/common.py b/connector_magento/models/product_category/common.py index 8d3d1f181..dca32eb35 100644 --- a/connector_magento/models/product_category/common.py +++ b/connector_magento/models/product_category/common.py @@ -4,7 +4,7 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). import logging -import xmlrpclib +import xmlrpc.client from odoo import models, fields from odoo.addons.connector.exception import IDMissingInBackend from odoo.addons.component.core import Component @@ -57,7 +57,7 @@ class ProductCategoryAdapter(Component): def _call(self, method, arguments): try: return super(ProductCategoryAdapter, self)._call(method, arguments) - except xmlrpclib.Fault as err: + except xmlrpc.client.Fault as err: # 101 is the error in the Magento API # when the category does not exist if err.faultCode == 102: diff --git a/connector_magento/models/product_category/importer.py b/connector_magento/models/product_category/importer.py index 0398d8fe6..5d8b2e519 100644 --- a/connector_magento/models/product_category/importer.py +++ b/connector_magento/models/product_category/importer.py @@ -39,7 +39,7 @@ def run(self, filters=None): base_priority = 10 def import_nodes(tree, level=0): - for node_id, children in tree.iteritems(): + for node_id, children in list(tree.items()): # By changing the priority, the top level category has # more chance to be imported before the childrens. # However, importers have to ensure that their parent is diff --git a/connector_magento/models/sale_order/common.py b/connector_magento/models/sale_order/common.py index 3cd706de0..c28667071 100644 --- a/connector_magento/models/sale_order/common.py +++ b/connector_magento/models/sale_order/common.py @@ -4,7 +4,7 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). import logging -import xmlrpclib +import xmlrpc.client import odoo.addons.decimal_precision as dp @@ -245,7 +245,7 @@ class SaleOrderAdapter(Component): def _call(self, method, arguments): try: return super(SaleOrderAdapter, self)._call(method, arguments) - except xmlrpclib.Fault as err: + except xmlrpc.client.Fault as err: # this is the error in the Magento API # when the sales order does not exist if err.faultCode == 100: diff --git a/connector_magento/models/sale_order/importer.py b/connector_magento/models/sale_order/importer.py index 0225a2b7e..43cda9bb2 100644 --- a/connector_magento/models/sale_order/importer.py +++ b/connector_magento/models/sale_order/importer.py @@ -220,7 +220,8 @@ def _add_store_credit_line(self, map_record, values): amount = float(record['customer_balance_amount']) if amount == 0.0: return values - line_builder = self.component(usage='order.line.builder.magento.store_credit') + line_builder = self.component( + usage='order.line.builder.magento.store_credit') line_builder.price_unit = amount line = (0, 0, line_builder.get_line()) values['order_line'].append(line) @@ -233,7 +234,8 @@ def _add_rewards_line(self, map_record, values): amount = float(record['reward_currency_amount']) if amount == 0.0: return values - line_builder = self.component(usage='order.line.builder.magento.rewards') + line_builder = self.component( + usage='order.line.builder.magento.rewards') line_builder.price_unit = amount line = (0, 0, line_builder.get_line()) values['order_line'].append(line) @@ -316,10 +318,10 @@ def sales_team(self, record): return {'team_id': team.id} @mapping - def project_id(self, record): - project_id = self.options.storeview.account_analytic_id - if project_id: - return {'project_id': project_id.id} + def analytic_account_id(self, record): + analytic_account_id = self.options.storeview.account_analytic_id + if analytic_account_id: + return {'analytic_account_id': analytic_account_id.id} @mapping def fiscal_position(self, record): diff --git a/connector_magento/models/stock_picking/common.py b/connector_magento/models/stock_picking/common.py index eb520c5c1..883d822ce 100644 --- a/connector_magento/models/stock_picking/common.py +++ b/connector_magento/models/stock_picking/common.py @@ -4,7 +4,7 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). import logging -import xmlrpclib +import xmlrpc.client from odoo import api, models, fields from odoo.addons.queue_job.job import job, related_action from odoo.addons.connector.exception import IDMissingInBackend @@ -80,7 +80,7 @@ class StockPickingAdapter(Component): def _call(self, method, arguments): try: return super(StockPickingAdapter, self)._call(method, arguments) - except xmlrpclib.Fault as err: + except xmlrpc.client.Fault as err: # this is the error in the Magento API # when the shipment does not exist if err.faultCode == 100: diff --git a/connector_magento/models/stock_picking/exporter.py b/connector_magento/models/stock_picking/exporter.py index 0d1d634a0..6ea3297a6 100644 --- a/connector_magento/models/stock_picking/exporter.py +++ b/connector_magento/models/stock_picking/exporter.py @@ -2,7 +2,7 @@ # Copyright 2013-2017 Camptocamp SA # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) -import xmlrpclib +import xmlrpc.client import odoo from odoo import _ @@ -83,7 +83,7 @@ def run(self, binding): "found: %s" % picking_method) try: external_id = self.backend_adapter.create(*args) - except xmlrpclib.Fault as err: + except xmlrpc.client.Fault as err: # When the shipping is already created on Magento, it returns: # diff --git a/connector_magento/tests/common.py b/connector_magento/tests/common.py index a98bd78b7..37a050dd5 100644 --- a/connector_magento/tests/common.py +++ b/connector_magento/tests/common.py @@ -10,7 +10,7 @@ Helpers usable in the tests """ -import xmlrpclib +import xmlrpc.client import logging import mock @@ -50,7 +50,7 @@ def getcode(self): @contextmanager def mock_urlopen_image(): - with mock.patch('urllib2.urlopen') as urlopen: + with mock.patch('urllib.request.urlopen') as urlopen: urlopen.return_value = MockResponseImage('') yield @@ -141,7 +141,7 @@ def mock_with_delay(self): yield delayable_cls, delayable def parse_cassette_request(self, body): - args, __ = xmlrpclib.loads(body) + args, __ = xmlrpc.client.loads(body) # the first argument is a hash, we don't mind return args[1:] @@ -213,7 +213,7 @@ def assert_records(self, expected_records, records): equals = [] for expected in expected_records: for record in records: - for field, value in expected._asdict().iteritems(): + for field, value in list(expected._asdict().items()): if not getattr(record, field) == value: break else: @@ -226,32 +226,32 @@ def assert_records(self, expected_records, records): for record in equals: # same records message.append( - u' ✓ {}({})'.format( + ' ✓ {}({})'.format( model_name, - u', '.join(u'%s: %s' % (field, getattr(record, field)) for - field in fields) + ', '.join('%s: %s' % (field, getattr(record, field)) for + field in fields) ) ) for expected in not_found: # missing records message.append( - u' - {}({})'.format( + ' - {}({})'.format( model_name, - u', '.join(u'%s: %s' % (k, v) for - k, v in expected._asdict().iteritems()) + ', '.join('%s: %s' % (k, v) for + k, v in list(expected._asdict().items())) ) ) for record in records: # extra records message.append( - u' + {}({})'.format( + ' + {}({})'.format( model_name, - u', '.join(u'%s: %s' % (field, getattr(record, field)) for - field in fields) + ', '.join('%s: %s' % (field, getattr(record, field)) for + field in fields) ) ) if not_found or records: - raise AssertionError(u'Records do not match:\n\n{}'.format( + raise AssertionError('Records do not match:\n\n{}'.format( '\n'.join(message) )) diff --git a/connector_magento/tests/test_concurrent_sync.py b/connector_magento/tests/test_concurrent_sync.py index 8c384d750..0c56eb2d2 100644 --- a/connector_magento/tests/test_concurrent_sync.py +++ b/connector_magento/tests/test_concurrent_sync.py @@ -6,7 +6,7 @@ from odoo import api from odoo.tests import common -from odoo.modules.registry import RegistryManager +from odoo.modules.registry import Registry from odoo.addons.queue_job.exception import RetryableJobError from odoo.addons.component.core import WorkContext @@ -18,7 +18,7 @@ class TestConcurrentSync(MagentoTestCase): def setUp(self): super(TestConcurrentSync, self).setUp() - self.registry2 = RegistryManager.get(common.get_db_name()) + self.registry2 = Registry.registries.get(common.get_db_name()) self.cr2 = self.registry2.cursor() self.env2 = api.Environment(self.cr2, self.env.uid, {}) diff --git a/connector_magento/tests/test_export_invoice.py b/connector_magento/tests/test_export_invoice.py index 030ac48f6..c0a8036f8 100644 --- a/connector_magento/tests/test_export_invoice.py +++ b/connector_magento/tests/test_export_invoice.py @@ -42,7 +42,7 @@ def test_export_invoice_on_validate_trigger(self): self._invoice_open() self.assertEqual(self.invoice.state, 'open') - self.assertEquals(len(self.invoice.magento_bind_ids), 1) + self.assertEqual(len(self.invoice.magento_bind_ids), 1) self.assertEqual(1, delayable_cls.call_count) delay_args, delay_kwargs = delayable_cls.call_args @@ -73,7 +73,7 @@ def test_export_invoice_on_paid_trigger(self): self._pay_and_reconcile() self.assertEqual(self.invoice.state, 'paid') - self.assertEquals(len(self.invoice.magento_bind_ids), 1) + self.assertEqual(len(self.invoice.magento_bind_ids), 1) self.assertEqual(1, delayable_cls.call_count) @@ -93,7 +93,7 @@ def test_export_invoice_on_payment_mode_validate_trigger(self): self._invoice_open() self.assertEqual(self.invoice.state, 'open') - self.assertEquals(len(self.invoice.magento_bind_ids), 1) + self.assertEqual(len(self.invoice.magento_bind_ids), 1) self.assertEqual(1, delayable_cls.call_count) delay_args, delay_kwargs = delayable_cls.call_args @@ -124,7 +124,7 @@ def test_export_invoice_on_payment_mode_paid_trigger(self): self._pay_and_reconcile() self.assertEqual(self.invoice.state, 'paid') - self.assertEquals(len(self.invoice.magento_bind_ids), 1) + self.assertEqual(len(self.invoice.magento_bind_ids), 1) self.assertEqual(1, delayable_cls.call_count) @@ -154,7 +154,7 @@ def test_export_invoice_job(self): self._invoice_open() invoice_binding = self.invoice.magento_bind_ids - self.assertEquals(len(invoice_binding), 1) + self.assertEqual(len(invoice_binding), 1) with recorder.use_cassette( 'test_export_invoice') as cassette: diff --git a/connector_magento/tests/test_export_picking.py b/connector_magento/tests/test_export_picking.py index 35e71e17a..b35f755bb 100644 --- a/connector_magento/tests/test_export_picking.py +++ b/connector_magento/tests/test_export_picking.py @@ -17,28 +17,46 @@ def setUp(self): self.order_binding.ignore_exception = True # generate sale's picking self.order_binding.odoo_id.action_confirm() + # Create inventory for add stock qty to lines + # With this commit https://goo.gl/fRTLM3 the moves that where + # force-assigned are not transferred in the picking + for line in self.order_binding.odoo_id.order_line: + if line.product_id.type == 'product': + inventory = self.env['stock.inventory'].create({ + 'name': 'Inventory for line %s' % line.name, + 'filter': 'product', + 'product_id': line.product_id.id, + 'line_ids': [(0, 0, { + 'product_id': line.product_id.id, + 'product_qty': line.product_uom_qty, + 'location_id': + self.env.ref('stock.stock_location_stock').id + })] + }) + inventory.action_done() self.picking = self.order_binding.picking_ids - self.assertEquals(len(self.picking), 1) + self.assertEqual(len(self.picking), 1) magento_shop = self.picking.sale_id.magento_bind_ids[0].store_id magento_shop.send_picking_done_mail = True def test_export_complete_picking_trigger(self): """ Trigger export of a complete picking """ - self.picking.force_assign() + self.picking.action_assign() with self.mock_with_delay() as (delayable_cls, delayable): # Deliver the entire picking, a 'magento.stock.picking' # should be created, then a job is generated that will export # the picking. Here the job is not created because we mock # 'with_delay()' - self.picking.do_transfer() - self.assertEquals(self.picking.state, 'done') + self.env['stock.immediate.transfer'].create( + {'pick_ids': [(4, self.picking.id)]}).process() + self.assertEqual(self.picking.state, 'done') picking_binding = self.env['magento.stock.picking'].search( [('odoo_id', '=', self.picking.id), ('backend_id', '=', self.backend.id)], ) - self.assertEquals(1, len(picking_binding)) - self.assertEquals('complete', picking_binding.picking_method) + self.assertEqual(1, len(picking_binding)) + self.assertEqual('complete', picking_binding.picking_method) self.assertEqual(1, delayable_cls.call_count) delay_args, delay_kwargs = delayable_cls.call_args @@ -50,19 +68,20 @@ def test_export_complete_picking_trigger(self): def test_export_complete_picking_job(self): """ Exporting a complete picking """ - self.picking.force_assign() + self.picking.action_assign() with self.mock_with_delay(): # Deliver the entire picking, a 'magento.stock.picking' # should be created, then a job is generated that will export # the picking. Here the job is not created because we mock # 'with_delay()' - self.picking.do_transfer() - self.assertEquals(self.picking.state, 'done') + self.env['stock.immediate.transfer'].create( + {'pick_ids': [(4, self.picking.id)]}).process() + self.assertEqual(self.picking.state, 'done') picking_binding = self.env['magento.stock.picking'].search( [('odoo_id', '=', self.picking.id), ('backend_id', '=', self.backend.id)], ) - self.assertEquals(1, len(picking_binding)) + self.assertEqual(1, len(picking_binding)) with recorder.use_cassette( 'test_export_picking_complete') as cassette: @@ -79,31 +98,42 @@ def test_export_complete_picking_job(self): ) # Check that we have received and bound the magento ID - self.assertEquals(picking_binding.external_id, '987654321') + self.assertEqual(picking_binding.external_id, '987654321') def test_export_partial_picking_trigger(self): """ Trigger export of a partial picking """ # Prepare a partial picking # The sale order contains 2 lines with 1 product each - self.picking.force_assign() - self.picking.do_prepare_partial() - self.picking.pack_operation_ids[0].product_qty = 1 - self.picking.pack_operation_ids[1].product_qty = 0 + self.picking.action_assign() + self.picking.move_lines[0].quantity_done = 1 + self.picking.move_lines[1].quantity_done = 0 + # Remove reservation for line index 1 + self.picking.move_lines[1].move_line_ids.unlink() with self.mock_with_delay() as (delayable_cls, delayable): # Deliver the entire picking, a 'magento.stock.picking' # should be created, then a job is generated that will export # the picking. Here the job is not created because we mock # 'with_delay()' - self.picking.do_transfer() - self.assertEquals(self.picking.state, 'done') + immediate_transfer = self.env['stock.immediate.transfer'].create( + {'pick_ids': [(4, self.picking.id)]}).process() + self.assertIsInstance(immediate_transfer, dict, + 'A backorder confirmation wizard action' + ' must be created') + self.assertEqual(immediate_transfer['res_model'], + 'stock.backorder.confirmation') + # Confirm backorder creation + self.env['stock.backorder.confirmation'].browse( + immediate_transfer['res_id']).process() + + self.assertEqual(self.picking.state, 'done') picking_binding = self.env['magento.stock.picking'].search( [('odoo_id', '=', self.picking.id), ('backend_id', '=', self.backend.id)], ) - self.assertEquals(1, len(picking_binding)) - self.assertEquals('partial', picking_binding.picking_method) + self.assertEqual(1, len(picking_binding)) + self.assertEqual('partial', picking_binding.picking_method) self.assertEqual(1, delayable_cls.call_count) delay_args, delay_kwargs = delayable_cls.call_args @@ -117,23 +147,23 @@ def test_export_partial_picking_job(self): """ Exporting a partial picking """ # Prepare a partial picking # The sale order contains 2 lines with 1 product each - self.picking.force_assign() - self.picking.do_prepare_partial() - self.picking.pack_operation_ids[0].product_qty = 1 - self.picking.pack_operation_ids[1].product_qty = 0 + self.picking.action_assign() + self.picking.move_lines[0].quantity_done = 1 + self.picking.move_lines[1].quantity_done = 0 with self.mock_with_delay(): # Deliver the entire picking, a 'magento.stock.picking' # should be created, then a job is generated that will export # the picking. Here the job is not created because we mock # 'with_delay()' - self.picking.do_transfer() - self.assertEquals(self.picking.state, 'done') + self.env['stock.immediate.transfer'].create( + {'pick_ids': [(4, self.picking.id)]}).process() + self.assertEqual(self.picking.state, 'done') picking_binding = self.env['magento.stock.picking'].search( [('odoo_id', '=', self.picking.id), ('backend_id', '=', self.backend.id)], ) - self.assertEquals(1, len(picking_binding)) + self.assertEqual(1, len(picking_binding)) with recorder.use_cassette( 'test_export_picking_partial') as cassette: @@ -150,21 +180,22 @@ def test_export_partial_picking_job(self): ) # Check that we have received and bound the magento ID - self.assertEquals(picking_binding.external_id, '987654321') + self.assertEqual(picking_binding.external_id, '987654321') def test_export_tracking_after_done_trigger(self): """ Trigger export of a tracking number """ - self.picking.force_assign() + self.picking.action_assign() with self.mock_with_delay(): - self.picking.do_transfer() - self.assertEquals(self.picking.state, 'done') + self.env['stock.immediate.transfer'].create( + {'pick_ids': [(4, self.picking.id)]}).process() + self.assertEqual(self.picking.state, 'done') picking_binding = self.env['magento.stock.picking'].search( [('odoo_id', '=', self.picking.id), ('backend_id', '=', self.backend.id)], ) - self.assertEquals(1, len(picking_binding)) + self.assertEqual(1, len(picking_binding)) with self.mock_with_delay() as (delayable_cls, delayable): self.picking.carrier_tracking_ref = 'XYZ' @@ -177,18 +208,19 @@ def test_export_tracking_after_done_trigger(self): def test_export_tracking_after_done_job(self): """ Job export of a tracking number """ - self.picking.force_assign() + self.picking.action_assign() with self.mock_with_delay(): - self.picking.do_transfer() - self.assertEquals(self.picking.state, 'done') + self.env['stock.immediate.transfer'].create( + {'pick_ids': [(4, self.picking.id)]}).process() + self.assertEqual(self.picking.state, 'done') self.picking.carrier_tracking_ref = 'XYZ' picking_binding = self.env['magento.stock.picking'].search( [('odoo_id', '=', self.picking.id), ('backend_id', '=', self.backend.id)], ) - self.assertEquals(1, len(picking_binding)) + self.assertEqual(1, len(picking_binding)) picking_binding.external_id = '100000035' with recorder.use_cassette( diff --git a/connector_magento/tests/test_export_product_stock.py b/connector_magento/tests/test_export_product_stock.py index bca93fec8..bf77ee324 100644 --- a/connector_magento/tests/test_export_product_stock.py +++ b/connector_magento/tests/test_export_product_stock.py @@ -83,8 +83,8 @@ def test_compute_new_qty_different_field(self): 'location_id': self.env.ref('stock.stock_location_stock').id, 'location_dest_id': customer_location.id, }) - outgoing.action_confirm() - outgoing.action_assign() + outgoing._action_confirm() + outgoing._action_assign() # the virtual is now 19, available still 30 self.assertEqual(product.qty_available, 30.0) @@ -145,9 +145,12 @@ def test_export_product_inventory_write(self): self.assertEqual((self.binding_product,), delay_args) self.assertEqual(20, delay_kwargs.get('priority')) - delayable.export_inventory.assert_called_with( - fields=['backorders', 'magento_qty', 'manage_stock'], - ) + cargs, ckwargs = delayable.export_inventory.call_args + self.assertFalse(cargs) + self.assertEqual(set(ckwargs.keys()), set(['fields'])) + self.assertEqual( + set(ckwargs['fields']), set([ + 'manage_stock', 'backorders', 'magento_qty'])) def test_export_product_inventory_write_job(self): with self.mock_with_delay(): diff --git a/connector_magento/tests/test_import_partner.py b/connector_magento/tests/test_import_partner.py index 0fb8c3cf4..a7c30a9cd 100644 --- a/connector_magento/tests/test_import_partner.py +++ b/connector_magento/tests/test_import_partner.py @@ -69,7 +69,7 @@ def test_import_partner_individual_2_addresses(self): ('backend_id', '=', self.backend.id)]) self.assertEqual(len(partner), 1) # Name of the billing address - self.assertEqual(partner.name, u'Tay Ray') + self.assertEqual(partner.name, 'Tay Ray') self.assertEqual(partner.type, 'contact') # billing address merged with the partner, # second address as a contact @@ -158,7 +158,7 @@ def test_import_partner_individual_2_addresses_multi_company(self): ('backend_id', '=', self.backend.id)]) self.assertEqual(len(partner), 1) # Name of the billing address - self.assertEqual(partner.name, u'Tay Ray') + self.assertEqual(partner.name, 'Tay Ray') self.assertEqual(partner.type, 'contact') # billing address merged with the partner, # second address as a contact diff --git a/connector_magento/tests/test_import_product_image.py b/connector_magento/tests/test_import_product_image.py index 1cdef3ad4..7698e2bdf 100644 --- a/connector_magento/tests/test_import_product_image.py +++ b/connector_magento/tests/test_import_product_image.py @@ -2,7 +2,9 @@ # Copyright 2015-2017 Camptocamp SA # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) -import urllib2 +import urllib.request +import urllib.error +import urllib.parse import mock from base64 import b64encode @@ -27,7 +29,7 @@ "\x00\x00\x00\x19tEXtComment\x00Created with GIMPW\x81" "\x0e\x17\x00\x00\x00\x12IDAT\x08\xd7cd\xf8\xcf\x00\x07L" "\x0c\x0c\xc4p\x002\xd2\x01\x07\xce\xee\xd0\xcf\x00\x00" - "\x00\x00IEND\xaeB`\x82") + "\x00\x00IEND\xaeB`\x82".encode('utf-8')) B64_PNG_IMG_4PX_GREEN = b64encode(PNG_IMG_4PX_GREEN) @@ -120,8 +122,8 @@ def test_image_priority(self): file3 = {'file': 'file3', 'types': ['thumbnail'], 'position': '4'} file4 = {'file': 'file4', 'types': [], 'position': '10'} images = [file2, file1, file4, file3] - self.assertEquals(self.image_importer._sort_images(images), - [file4, file3, file2, file1]) + self.assertEqual(self.image_importer._sort_images(images), + [file4, file3, file2, file1]) def test_import_images_404(self): """ An image responds a 404 error, skip and take the first valid """ @@ -137,10 +139,10 @@ def test_import_images_404(self): ) binding.with_context.return_value = binding_no_export - with mock.patch('urllib2.urlopen') as urlopen: + with mock.patch('urllib.request.urlopen') as urlopen: def image_url_response(url): - if url._Request__original in (url_tee1, url_tee2): - raise urllib2.HTTPError(url, 404, '404', None, None) + if url.full_url in (url_tee1, url_tee2): + raise urllib.error.HTTPError(url, 404, '404', None, None) else: return MockResponseImage(PNG_IMG_4PX_GREEN) urlopen.side_effect = image_url_response @@ -162,16 +164,16 @@ def test_import_images_403(self): '/i/n/ink-eater-krylon-bombear-destroyed-tee-1.jpg') url_tee2 = ('http://localhost:9100/media/catalog/product/' 'i/n/ink-eater-krylon-bombear-destroyed-tee-2.jpg') - with mock.patch('urllib2.urlopen') as urlopen: + with mock.patch('urllib.request.urlopen') as urlopen: def image_url_response(url): url = url.get_full_url() if url == url_tee2: - raise urllib2.HTTPError(url, 404, '404', None, None) + raise urllib.error.HTTPError(url, 404, '404', None, None) elif url == url_tee1: - raise urllib2.HTTPError(url, 403, '403', None, None) + raise urllib.error.HTTPError(url, 403, '403', None, None) else: return MockResponseImage(PNG_IMG_4PX_GREEN) urlopen.side_effect = image_url_response - with self.assertRaises(urllib2.HTTPError): + with self.assertRaises(urllib.error.HTTPError): self.image_importer.run(122, binding) diff --git a/connector_magento/tests/test_related_action.py b/connector_magento/tests/test_related_action.py index 0a53bad2b..8189f6742 100644 --- a/connector_magento/tests/test_related_action.py +++ b/connector_magento/tests/test_related_action.py @@ -33,7 +33,7 @@ def test_unwrap_binding(self): 'res_id': product.id, 'res_model': 'product.product', } - self.assertEquals(stored.open_related_action(), expected) + self.assertEqual(stored.open_related_action(), expected) def test_link(self): """ Open a related action opening an url on Magento """ @@ -49,7 +49,7 @@ def test_link(self): 'target': 'new', 'url': url, } - self.assertEquals(stored.open_related_action(), expected) + self.assertEqual(stored.open_related_action(), expected) def test_link_no_location(self): """ Related action opening an url, admin location is not configured """ @@ -59,5 +59,5 @@ def test_link_no_location(self): ) stored = job.db_record() msg = r'No admin URL configured.*' - with self.assertRaisesRegexp(exceptions.UserError, msg): + with self.assertRaisesRegex(exceptions.UserError, msg): stored.open_related_action() diff --git a/connector_magento/tests/test_sale_order.py b/connector_magento/tests/test_sale_order.py index 25453f15e..9c763a385 100644 --- a/connector_magento/tests/test_sale_order.py +++ b/connector_magento/tests/test_sale_order.py @@ -43,8 +43,7 @@ def test_import_sale_order_with_configurable(self): prod2 = self.env['magento.product.product'].search( [('external_id', '=', 302), ('backend_id', '=', self.backend.id)] ) - ship = self.env['product.product'].search([('name', '=', 'ups_GND')]) - + ship = binding.carrier_id.product_id expected = [ ExpectedOrderLine( product_id=prod1.odoo_id, @@ -60,7 +59,7 @@ def test_import_sale_order_with_configurable(self): ), ExpectedOrderLine( product_id=ship, - name='ups_GND', + name='Shipping Costs', price_unit=12.31, product_uom_qty=1., ), @@ -139,7 +138,7 @@ def test_import_sale_order_options(self): backend models (backend, wesite, store and storeview) """ binding = self._import_sale_order(100000201) - self.assertFalse(binding.project_id) + self.assertFalse(binding.analytic_account_id) self.assertFalse(binding.fiscal_position_id) # keep a reference to backend models the website storeview_id = binding.storeview_id @@ -154,8 +153,8 @@ def test_import_sale_order_options(self): self.backend.account_analytic_id = account_analytic_id self.backend.fiscal_position_id = fp1.id binding = self._import_sale_order(100000201) - self.assertEquals(binding.project_id, account_analytic_id) - self.assertEquals(binding.fiscal_position_id, fp1) + self.assertEqual(binding.analytic_account_id, account_analytic_id) + self.assertEqual(binding.fiscal_position_id, fp1) binding.odoo_id.unlink() binding.unlink() # define options at the website level @@ -165,8 +164,8 @@ def test_import_sale_order_options(self): website_id.specific_account_analytic_id = account_analytic_id website_id.specific_fiscal_position_id = fp2.id binding = self._import_sale_order(100000201) - self.assertEquals(binding.project_id, account_analytic_id) - self.assertEquals(binding.fiscal_position_id, fp2) + self.assertEqual(binding.analytic_account_id, account_analytic_id) + self.assertEqual(binding.fiscal_position_id, fp2) binding.odoo_id.unlink() binding.unlink() # define options at the store level @@ -176,8 +175,8 @@ def test_import_sale_order_options(self): store_id.specific_account_analytic_id = account_analytic_id store_id.specific_fiscal_position_id = fp3.id binding = self._import_sale_order(100000201) - self.assertEquals(binding.project_id, account_analytic_id) - self.assertEquals(binding.fiscal_position_id, fp3) + self.assertEqual(binding.analytic_account_id, account_analytic_id) + self.assertEqual(binding.fiscal_position_id, fp3) binding.odoo_id.unlink() binding.unlink() # define options at the storeview level @@ -187,8 +186,8 @@ def test_import_sale_order_options(self): storeview_id.specific_account_analytic_id = account_analytic_id storeview_id.specific_fiscal_position_id = fp4.id binding = self._import_sale_order(100000201) - self.assertEquals(binding.project_id, account_analytic_id) - self.assertEquals(binding.fiscal_position_id, fp4) + self.assertEqual(binding.analytic_account_id, account_analytic_id) + self.assertEqual(binding.fiscal_position_id, fp4) def test_sale_order_cancel_delay_job(self): """ Cancel an order, delay a cancel job """ diff --git a/oca_dependencies.txt b/oca_dependencies.txt new file mode 100644 index 000000000..82a7a455c --- /dev/null +++ b/oca_dependencies.txt @@ -0,0 +1,8 @@ +connector +connector-ecommerce +e-commerce +product-attribute +sale-workflow +server-tools +bank-payment +partner-contact diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 000000000..4ea77bd6e --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +vcrpy \ No newline at end of file