<commit_before># -*- coding: utf-8 -*-
##############################################################################
#
#    Author: <NAME>
#    Copyright 2013 Camptocamp SA
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU Affero General Public License as
#    published by the Free Software Foundation, either version 3 of the
#    License, or (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU Affero General Public License for more details.
#
#    You should have received a copy of the GNU Affero General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################

import logging
import xmlrpclib
from datetime import datetime, timedelta
import openerp.addons.decimal_precision as dp
from openerp import models, fields, api, _
from openerp.addons.connector.connector import ConnectorUnit
from openerp.addons.connector.session import ConnectorSession
from openerp.addons.connector.exception import (NothingToDoJob,
                                                FailedJobError,
                                                IDMissingInBackend)
from openerp.addons.connector.queue.job import job
from openerp.addons.connector.unit.synchronizer import Exporter
from openerp.addons.connector.unit.mapper import (mapping,
                                                  ImportMapper
                                                  )
from openerp.addons.connector_ecommerce.unit.sale_order_onchange import (
    SaleOrderOnChange)
from openerp.addons.connector_ecommerce.sale import (ShippingLineBuilder,
                                                     CashOnDeliveryLineBuilder,
                                                     GiftOrderLineBuilder)
from .unit.backend_adapter import (GenericAdapter,
                                   MAGENTO_DATETIME_FORMAT,
                                   )
from .unit.import_synchronizer import (DelayedBatchImporter,
                                       MagentoImporter,
                                       )
from .unit.mapper import normalize_datetime
from .exception import OrderImportRuleRetry
from .backend import magento
from .connector import get_environment
from .partner import PartnerImportMapper

_logger = logging.getLogger(__name__)

ORDER_STATUS_MAPPING = {  # used in magentoerpconnect_order_comment
    'draft': 'pending',
    'manual': 'processing',
    'progress': 'processing',
    'shipping_except': 'processing',
    'invoice_except': 'processing',
    'done': 'complete',
    'cancel': 'canceled',
    'waiting_date': 'holded'
}


class MagentoSaleOrder(models.Model):
    _name = 'magento.sale.order'
    _inherit = 'magento.binding'
    _description = 'Magento Sale Order'
    _inherits = {'sale.order': 'openerp_id'}

    openerp_id = fields.Many2one(comodel_name='sale.order',
                                 string='Sale Order',
                                 required=True,
                                 ondelete='cascade')
    magento_order_line_ids = fields.One2many(
        comodel_name='magento.sale.order.line',
        inverse_name='magento_order_id',
        string='Magento Order Lines'
    )
    total_amount = fields.Float(
        string='Total amount',
        digits_compute=dp.get_precision('Account')
    )
    total_amount_tax = fields.Float(
        string='Total amount w. tax',
        digits_compute=dp.get_precision('Account')
    )
    magento_order_id = fields.Integer(string='Magento Order ID',
                                      help="'order_id' field in Magento")
    # when a sale order is modified, Magento creates a new one, cancels
    # the parent order and link the new one to the canceled parent
    magento_parent_id = fields.Many2one(comodel_name='magento.sale.order',
                                        string='Parent Magento Order')
    storeview_id = fields.Many2one(comodel_name='magento.storeview',
                                   string='Magento Storeview')
    store_id = fields.Many2one(related='storeview_id.store_id',
                               string='Storeview',
                               readonly=True)


class SaleOrder(models.Model):
    _inherit = 'sale.order'

    magento_bind_ids = fields.One2many(
        comodel_name='magento.sale.order',
        inverse_name='openerp_id',
        string="Magento Bindings",
    )

    @api.one
    @api.depends('magento_bind_ids', 'magento_bind_ids.magento_parent_id')
    def get_parent_id(self):
        """ Return the parent order.

        For Magento sales orders, the magento parent order is stored
        in the binding, get it from there.
        """
        super(SaleOrder, self).get_parent_id()
        for order in self:
            if not order.magento_bind_ids:
                continue
            # assume we only have 1 SO in OpenERP for 1 SO in Magento
            magento_order = order.magento_bind_ids[0]
            if magento_order.magento_parent_id:
                self.parent_id = magento_order.magento_parent_id.openerp_id

    @api.multi
    def write(self, vals):
        # cancel sales order on Magento (do not export the other
        # state changes, Magento handles them itself)
        if vals.get('state') == 'cancel':
            session = ConnectorSession(self.env.cr, self.env.uid,
                                       context=self.env.context)
            for order in self:
                old_state = order.state
                if old_state == 'cancel':
                    continue  # skip if already canceled
                for binding in order.magento_bind_ids:
                    export_state_change.delay(
                        session,
                        'magento.sale.order',
                        binding.id,
                        # so if the state changes afterwards,
                        # it won't be exported
                        allowed_states=['cancel'],
                        description="Cancel sales order %s" %
                                    binding.magento_id)
        return super(SaleOrder, self).write(vals)

    @api.multi
    def copy_quotation(self):
        self_copy = self.with_context(__copy_from_quotation=True)
        result = super(SaleOrder, self_copy).copy_quotation()
        # link binding of the canceled order to the new order, so the
        # operations done on the new order will be sync'ed with Magento
        new_id = result['res_id']
        binding_model = self.env['magento.sale.order']
        bindings = binding_model.search([('openerp_id', '=', self.id)])
        bindings.write({'openerp_id': new_id})
        session = ConnectorSession(self.env.cr, self.env.uid,
                                   context=self.env.context)
        for binding in bindings:
            # the sales' status on Magento is likely 'canceled'
            # so we will export the new status (pending, processing, ...)
            export_state_change.delay(
                session,
                'magento.sale.order',
                binding.id,
                description="Reopen sales order %s" % binding.magento_id)
        return result


class MagentoSaleOrderLine(models.Model):
    _name = 'magento.sale.order.line'
    _inherit = 'magento.binding'
    _description = 'Magento Sale Order Line'
    _inherits = {'sale.order.line': 'openerp_id'}

    magento_order_id = fields.Many2one(comodel_name='magento.sale.order',
                                       string='Magento Sale Order',
                                       required=True,
                                       ondelete='cascade',
                                       select=True)
    openerp_id = fields.Many2one(comodel_name='sale.order.line',
                                 string='Sale Order Line',
                                 required=True,
                                 ondelete='cascade')
    backend_id = fields.Many2one(
        related='magento_order_id.backend_id',
        string='Magento Backend',
        readonly=True,
        # override 'magento.binding', can't be INSERTed if True:
        required=False,
    )
    tax_rate = fields.Float(string='Tax Rate',
                            digits_compute=dp.get_precision('Account'))
    notes = fields.Char()

    @api.model<commit_msg>Store the backend_id on sale order lines<commit_after># -*- coding: utf-8 -*-
##############################################################################
#
#    Author: <NAME>
#    Copyright 2013 Camptocamp SA
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU Affero General Public License as
#    published by the Free Software Foundation, either version 3 of the
#    License, or (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU Affero General Public License for more details.
#
#    You should have received a copy of the GNU Affero General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################

import logging
import xmlrpclib
from datetime import datetime, timedelta
import openerp.addons.decimal_precision as dp
from openerp import models, fields, api, _
from openerp.addons.connector.connector import ConnectorUnit
from openerp.addons.connector.session import ConnectorSession
from openerp.addons.connector.exception import (NothingToDoJob,
                                                FailedJobError,
                                                IDMissingInBackend)
from openerp.addons.connector.queue.job import job
from openerp.addons.connector.unit.synchronizer import Exporter
from openerp.addons.connector.unit.mapper import (mapping,
                                                  ImportMapper
                                                  )
from openerp.addons.connector_ecommerce.unit.sale_order_onchange import (
    SaleOrderOnChange)
from openerp.addons.connector_ecommerce.sale import (ShippingLineBuilder,
                                                     CashOnDeliveryLineBuilder,
                                                     GiftOrderLineBuilder)
from .unit.backend_adapter import (GenericAdapter,
                                   MAGENTO_DATETIME_FORMAT,
                                   )
from .unit.import_synchronizer import (DelayedBatchImporter,
                                       MagentoImporter,
                                       )
from .unit.mapper import normalize_datetime
from .exception import OrderImportRuleRetry
from .backend import magento
from .connector import get_environment
from .partner import PartnerImportMapper

_logger = logging.getLogger(__name__)

ORDER_STATUS_MAPPING = {  # used in magentoerpconnect_order_comment
    'draft': 'pending',
    'manual': 'processing',
    'progress': 'processing',
    'shipping_except': 'processing',
    'invoice_except': 'processing',
    'done': 'complete',
    'cancel': 'canceled',
    'waiting_date': 'holded'
}


class MagentoSaleOrder(models.Model):
    _name = 'magento.sale.order'
    _inherit = 'magento.binding'
    _description = 'Magento Sale Order'
    _inherits = {'sale.order': 'openerp_id'}

    openerp_id = fields.Many2one(comodel_name='sale.order',
                                 string='Sale Order',
                                 required=True,
                                 ondelete='cascade')
    magento_order_line_ids = fields.One2many(
        comodel_name='magento.sale.order.line',
        inverse_name='magento_order_id',
        string='Magento Order Lines'
    )
    total_amount = fields.Float(
        string='Total amount',
        digits_compute=dp.get_precision('Account')
    )
    total_amount_tax = fields.Float(
        string='Total amount w. tax',
        digits_compute=dp.get_precision('Account')
    )
    magento_order_id = fields.Integer(string='Magento Order ID',
                                      help="'order_id' field in Magento")
    # when a sale order is modified, Magento creates a new one, cancels
    # the parent order and link the new one to the canceled parent
    magento_parent_id = fields.Many2one(comodel_name='magento.sale.order',
                                        string='Parent Magento Order')
    storeview_id = fields.Many2one(comodel_name='magento.storeview',
                                   string='Magento Storeview')
    store_id = fields.Many2one(related='storeview_id.store_id',
                               string='Storeview',
                               readonly=True)


class SaleOrder(models.Model):
    _inherit = 'sale.order'

    magento_bind_ids = fields.One2many(
        comodel_name='magento.sale.order',
        inverse_name='openerp_id',
        string="Magento Bindings",
    )

    @api.one
    @api.depends('magento_bind_ids', 'magento_bind_ids.magento_parent_id')
    def get_parent_id(self):
        """ Return the parent order.

        For Magento sales orders, the magento parent order is stored
        in the binding, get it from there.
        """
        super(SaleOrder, self).get_parent_id()
        for order in self:
            if not order.magento_bind_ids:
                continue
            # assume we only have 1 SO in OpenERP for 1 SO in Magento
            magento_order = order.magento_bind_ids[0]
            if magento_order.magento_parent_id:
                self.parent_id = magento_order.magento_parent_id.openerp_id

    @api.multi
    def write(self, vals):
        # cancel sales order on Magento (do not export the other
        # state changes, Magento handles them itself)
        if vals.get('state') == 'cancel':
            session = ConnectorSession(self.env.cr, self.env.uid,
                                       context=self.env.context)
            for order in self:
                old_state = order.state
                if old_state == 'cancel':
                    continue  # skip if already canceled
                for binding in order.magento_bind_ids:
                    export_state_change.delay(
                        session,
                        'magento.sale.order',
                        binding.id,
                        # so if the state changes afterwards,
                        # it won't be exported
                        allowed_states=['cancel'],
                        description="Cancel sales order %s" %
                                    binding.magento_id)
        return super(SaleOrder, self).write(vals)

    @api.multi
    def copy_quotation(self):
        self_copy = self.with_context(__copy_from_quotation=True)
        result = super(SaleOrder, self_copy).copy_quotation()
        # link binding of the canceled order to the new order, so the
        # operations done on the new order will be sync'ed with Magento
        new_id = result['res_id']
        binding_model = self.env['magento.sale.order']
        bindings = binding_model.search([('openerp_id', '=', self.id)])
        bindings.write({'openerp_id': new_id})
        session = ConnectorSession(self.env.cr, self.env.uid,
                                   context=self.env.context)
        for binding in bindings:
            # the sales' status on Magento is likely 'canceled'
            # so we will export the new status (pending, processing, ...)
            export_state_change.delay(
                session,
                'magento.sale.order',
                binding.id,
                description="Reopen sales order %s" % binding.magento_id)
        return result


class MagentoSaleOrderLine(models.Model):
    _name = 'magento.sale.order.line'
    _inherit = 'magento.binding'
    _description = 'Magento Sale Order Line'
    _inherits = {'sale.order.line': 'openerp_id'}

    magento_order_id = fields.Many2one(comodel_name='magento.sale.order',
                                       string='Magento Sale Order',
                                       required=True,
                                       ondelete='cascade',
                                       select=True)
    openerp_id = fields.Many2one(comodel_name='sale.order.line',
                                 string='Sale Order Line',
                                 required=True,
                                 ondelete='cascade')
    backend_id = fields.Many2one(
        related='magento_order_id.backend_id',
        string='Magento Backend',
        readonly=True,
        store=True,
        # override 'magento.binding', can't be INSERTed if True:
        required=False,
    )
    tax_rate = fields.Float(string='Tax Rate',
                            digits_compute=dp.get_precision('Account'))
    notes = fields.Char()

    @api.model
{'fraction_non_alphanumeric': 0.09735388194242513, 'fraction_numerical': 0.0020936318697295725, 'mean_word_length': 1.9384825700615174, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 2}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 64, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4593737', 'n_tokens_mistral': 4567, 'n_tokens_neox': 4426, 'n_words': 1128}