# -*- coding: utf-8 -*-
##############################################################################
#
#    Purchase - Package Quantity Module for Odoo
#    Copyright (C) 2016-Today Akretion (https://www.akretion.com)
#    @author Julien WESTE
#    @author Sylvain LE GAL (https://twitter.com/legalsylvain)
#
#    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/>.
#
##############################################################################

from math import ceil

from openerp.osv.osv import except_osv
from openerp import api, fields, models, _
import openerp.addons.decimal_precision as dp


class PurchaseOrderLine(models.Model):
    _inherit = 'purchase.order.line'

    @api.depends('product_qty', 'price_unit', 'taxes_id', 'price_policy')
    def _compute_amount(self):
        line_qty = {}
        for line in self:
            if line.price_policy == 'package':
                line_qty[line.id] = line.product_qty
                line.product_qty = line.product_qty_package

            super(PurchaseOrderLine, line)._compute_amount()

            # Restore the Quantity
            if line.price_policy == 'package':
                line.product_qty = line_qty[line.id]

    @api.model
    def _get_supplierinfovals(self, partner=False):
        if not partner:
            return False
        currency = partner.property_purchase_currency_id or\
            self.env.user.company_id.currency_id
        return {
            'name': partner.id,
            'sequence': max(self.product_id.seller_ids.mapped('sequence')) + 1
            if self.product_id.seller_ids else 1,
            'product_uom': self.product_uom.id,
            'min_qty': 0.0,
            'base_price': self.order_id.currency_id.compute(
                self.price_unit, currency),
            'price_policy': self.price_policy,
            'package_qty': self.package_qty or 1,
            'currency_id': currency.id,
            'delay': 0,
        }

    @api.model
    def _get_package_qty(self):
        if self.product_id and self.partner_id:
            partner = self.partner_id.parent_id or self.partner_id
            if partner in self.product_id.seller_ids.mapped('name'):
                for supplier in self.product_id.seller_ids:
                    if supplier.name == self.partner_id:
                        return supplier.package_qty
        return 1

    @api.model
    def _get_indicative_package(self):
        if self.product_id and self.partner_id:
            partner = self.partner_id.parent_id or self.partner_id
            if partner in self.product_id.seller_ids.mapped('name'):
                for supplier in self.product_id.seller_ids:
                    if supplier.name == self.partner_id:
                        return supplier.indicative_package
        return False

    package_qty = fields.Float(
        'Package Qty', default=lambda self: self._get_package_qty(),
        help="""The quantity of products in the supplier package.""")
    indicative_package = fields.Boolean(
        'Indicative Package',
        default=lambda self: self._get_indicative_package())
    product_qty_package = fields.Float(
        'Number of packages', help="""The number of packages you'll buy.""")
    product_qty = fields.Float(
        string='Quantity',
        digits=dp.get_precision('Product Unit of Measure'),
        required=True, _prefetch=False)       
    price_policy = fields.Selection(
        [('uom', 'per UOM'), ('package', 'per Package')], "Price Policy",
        default='uom', required=True)
    unit_price = fields.Float(
        string='Unit Price', compute="_compute_product_prices",
        digits=dp.get_precision('Product Price'))
    package_price = fields.Float(
        string='Package Price', compute="_compute_product_prices",
        digits=dp.get_precision('Product Price'))

    @api.multi
    @api.depends('package_qty', 'price_unit')
    def _compute_product_prices(self):
        for line in self:
            if line.price_policy == 'package':
                line.unit_price = line.package_qty and\
                    line.price_unit / line.package_qty or 0
                line.package_price = line.price_unit
            else:
                line.unit_price = line.price_unit
                line.package_price = line.price_unit * line.package_qty

    # Constraints section
    # TODO: Rewrite me in _contraint, if the Orm V8 allows param in message.
    @api.one
    @api.constrains('order_id', 'product_id', 'product_qty')
    def _check_purchase_qty(self):
        for pol in self:
            if pol.order_id.state not in ('draft', 'sent'):
                continue
            if not pol.product_id:
                return True
            supplier_id = pol.order_id.partner_id.id
            found = False
            for psi in pol.product_id.seller_ids:
                if psi.name.id == supplier_id:
                    package_qty = psi.package_qty
                    indicative = psi.indicative_package
                    found = True
                    break
            if not found:
                return True
            if not indicative:
                if (int(pol.product_qty / package_qty) !=
                        pol.product_qty / package_qty):
                    raise except_osv(
                        _("Package Error!"),
                        _(
                            """You have to buy a multiple of the package"""
                            """ qty or change the package settings in the"""
                            """ supplierinfo of the product for the"""
                            """ following line:"""
                            """ \n - Product: %s;"""
                            """ \n - Quantity: %s;"""
                            """ \n - Unit Price: %s;"""
                            """ \n - Package quantity: %s;""" % (
                                pol.product_id.name, pol.product_qty,
                                pol.price_unit, package_qty)))

    @api.model
    def create(self, vals):
        res = super(PurchaseOrderLine, self).create(vals)
        self._check_purchase_qty([res])
        return res

    @api.multi
    def write(self, vals):
        res = super(PurchaseOrderLine, self).write(vals)
        self._check_purchase_qty()
        return res

    # Views section
    @api.onchange('product_id')
    def onchange_product_id(self):
        res = super(PurchaseOrderLine, self).onchange_product_id()
        if self.product_id:
            for supplier in self.product_id.seller_ids:
                if self.partner_id and (supplier.name == self.partner_id):
                    self.package_qty = supplier.package_qty
                    self.indicative_package = supplier.indicative_package
                    self.product_qty = supplier.package_qty
                    self.product_qty_package = 1
                    self.price_policy = supplier.price_policy
                    break
        return res

    @api.onchange('product_qty', 'product_uom')
    def _onchange_quantity(self):
        if not self.product_id:
            return
        super(PurchaseOrderLine, self)._onchange_quantity()
        seller = self.product_id._select_seller(
            self.product_id,
            partner_id=self.partner_id,
            quantity=self.product_qty,
            date=self.order_id.date_order and self.order_id.date_order[:10],
            uom_id=self.product_uom)
        if not seller:
            return
        if seller.price_policy == "package":
            self.price_unit = seller.base_price
        res = {}
        if (not(self.indicative_package) and self.package_qty > 0 and
                int(self.product_qty / self.package_qty) !=
                self.product_qty / self.package_qty):
            res['warning'] = {
                'title': _('Warning!'),
                'message': _(
                    """The selected supplier only sells """
                    """this product by %s %s""") % (
                    self.package_qty,
                    self.product_uom.name)}
            self.product_qty = ceil(
                self.product_qty / self.package_qty) * self.package_qty
        if self.package_qty:
            self.product_qty_package = self.product_qty / self.package_qty
        self._compute_amount()
        return res

    @api.onchange('product_qty_package')
    def onchange_product_qty_package(self):
        if self.product_qty_package == int(self.product_qty_package):
            self.product_qty = self.package_qty * self.product_qty_package

    @api.onchange('package_qty')
    def onchange_package_qty(self):
        if not self.package_qty:
            self.package_qty = 1
        self.product_qty = self.package_qty * self.product_qty_package

    @api.multi
    def _create_stock_moves(self, picking):
        res = super(PurchaseOrderLine, self)._create_stock_moves(picking)
        for move in res:
            move.package_qty = move.purchase_line_id.package_qty
            move.product_qty_package = \
                move.purchase_line_id.product_qty_package
        return res