220 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			Python
		
	
	
			
		
		
	
	
			220 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			Python
		
	
	
# -*- coding: utf-8 -*-
 | 
						|
# Copyright 2004-2010 OpenERP SA
 | 
						|
# Copyright 2014 Angel Moya <angel.moya@domatix.com>
 | 
						|
# Copyright 2016 Carlos Dauden <carlos.dauden@tecnativa.com>
 | 
						|
# Copyright 2016-2017 LasLabs Inc.
 | 
						|
# Copyright 2015-2017 Tecnativa - Pedro M. Baeza
 | 
						|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
 | 
						|
 | 
						|
from dateutil.relativedelta import relativedelta
 | 
						|
 | 
						|
from odoo import api, fields, models
 | 
						|
from odoo.addons import decimal_precision as dp
 | 
						|
from odoo.exceptions import ValidationError
 | 
						|
from odoo.tools.translate import _
 | 
						|
 | 
						|
 | 
						|
class AccountAnalyticInvoiceLine(models.Model):
 | 
						|
    _name = 'account.analytic.invoice.line'
 | 
						|
    _order = "sequence,id"
 | 
						|
 | 
						|
    product_id = fields.Many2one(
 | 
						|
        'product.product',
 | 
						|
        string='Product',
 | 
						|
        required=True,
 | 
						|
    )
 | 
						|
    analytic_account_id = fields.Many2one(
 | 
						|
        'account.analytic.account',
 | 
						|
        string='Analytic Account',
 | 
						|
        required=True,
 | 
						|
        ondelete='cascade',
 | 
						|
    )
 | 
						|
    name = fields.Text(
 | 
						|
        string='Description',
 | 
						|
        required=True,
 | 
						|
    )
 | 
						|
    quantity = fields.Float(
 | 
						|
        default=1.0,
 | 
						|
        required=True,
 | 
						|
    )
 | 
						|
    uom_id = fields.Many2one(
 | 
						|
        'product.uom',
 | 
						|
        string='Unit of Measure',
 | 
						|
        required=True,
 | 
						|
    )
 | 
						|
    automatic_price = fields.Boolean(
 | 
						|
        string="Auto-price?",
 | 
						|
        help="If this is marked, the price will be obtained automatically "
 | 
						|
             "applying the pricelist to the product. If not, you will be "
 | 
						|
             "able to introduce a manual price",
 | 
						|
    )
 | 
						|
    specific_price = fields.Float(
 | 
						|
        string='Specific Price',
 | 
						|
    )
 | 
						|
    price_unit = fields.Float(
 | 
						|
        string='Unit Price',
 | 
						|
        compute="_compute_price_unit",
 | 
						|
        inverse="_inverse_price_unit",
 | 
						|
    )
 | 
						|
    price_subtotal = fields.Float(
 | 
						|
        compute='_compute_price_subtotal',
 | 
						|
        digits=dp.get_precision('Account'),
 | 
						|
        string='Sub Total',
 | 
						|
    )
 | 
						|
    discount = fields.Float(
 | 
						|
        string='Discount (%)',
 | 
						|
        digits=dp.get_precision('Discount'),
 | 
						|
        help='Discount that is applied in generated invoices.'
 | 
						|
             ' It should be less or equal to 100',
 | 
						|
    )
 | 
						|
    sequence = fields.Integer(
 | 
						|
        string="Sequence",
 | 
						|
        default=10,
 | 
						|
        help="Sequence of the contract line when displaying contracts",
 | 
						|
    )
 | 
						|
    date_from = fields.Date(
 | 
						|
        string='Date From',
 | 
						|
        compute='_compute_date_from',
 | 
						|
        help='Date from invoiced period',
 | 
						|
    )
 | 
						|
    date_to = fields.Date(
 | 
						|
        string='Date To',
 | 
						|
        compute='_compute_date_to',
 | 
						|
        help='Date to invoiced period',
 | 
						|
    )
 | 
						|
 | 
						|
    @api.depends(
 | 
						|
        'automatic_price',
 | 
						|
        'specific_price',
 | 
						|
        'product_id',
 | 
						|
        'quantity',
 | 
						|
        'analytic_account_id.pricelist_id',
 | 
						|
        'analytic_account_id.partner_id',
 | 
						|
    )
 | 
						|
    def _compute_price_unit(self):
 | 
						|
        """Get the specific price if no auto-price, and the price obtained
 | 
						|
        from the pricelist otherwise.
 | 
						|
        """
 | 
						|
        for line in self:
 | 
						|
            if line.automatic_price:
 | 
						|
                product = line.product_id.with_context(
 | 
						|
                    quantity=line.quantity,
 | 
						|
                    pricelist=line.analytic_account_id.pricelist_id.id,
 | 
						|
                    partner=line.analytic_account_id.partner_id.id,
 | 
						|
                    date=line.env.context.get('old_date', fields.Date.today()),
 | 
						|
                )
 | 
						|
                line.price_unit = product.price
 | 
						|
            else:
 | 
						|
                line.price_unit = line.specific_price
 | 
						|
 | 
						|
    # Tip in https://github.com/odoo/odoo/issues/23891#issuecomment-376910788
 | 
						|
    @api.onchange('price_unit')
 | 
						|
    def _inverse_price_unit(self):
 | 
						|
        """Store the specific price in the no auto-price records."""
 | 
						|
        for line in self.filtered(lambda x: not x.automatic_price):
 | 
						|
            line.specific_price = line.price_unit
 | 
						|
 | 
						|
    @api.multi
 | 
						|
    @api.depends('quantity', 'price_unit', 'discount')
 | 
						|
    def _compute_price_subtotal(self):
 | 
						|
        for line in self:
 | 
						|
            subtotal = line.quantity * line.price_unit
 | 
						|
            discount = line.discount / 100
 | 
						|
            subtotal *= 1 - discount
 | 
						|
            if line.analytic_account_id.pricelist_id:
 | 
						|
                cur = line.analytic_account_id.pricelist_id.currency_id
 | 
						|
                line.price_subtotal = cur.round(subtotal)
 | 
						|
            else:
 | 
						|
                line.price_subtotal = subtotal
 | 
						|
 | 
						|
    def _compute_date_from(self):
 | 
						|
        # When call from template line.analytic_account_id comodel is
 | 
						|
        # 'account.analytic.contract',
 | 
						|
        if self._name != 'account.analytic.invoice.line':
 | 
						|
            return
 | 
						|
        for line in self:
 | 
						|
            contract = line.analytic_account_id
 | 
						|
            date_start = (
 | 
						|
                self.env.context.get('old_date') or fields.Date.from_string(
 | 
						|
                    contract.recurring_next_date or fields.Date.today())
 | 
						|
            )
 | 
						|
            if contract.recurring_invoicing_type == 'pre-paid':
 | 
						|
                date_from = date_start
 | 
						|
            else:
 | 
						|
                date_from = (date_start - contract.get_relative_delta(
 | 
						|
                    contract.recurring_rule_type,
 | 
						|
                    contract.recurring_interval) + relativedelta(days=1))
 | 
						|
            line.date_from = fields.Date.to_string(date_from)
 | 
						|
 | 
						|
    def _compute_date_to(self):
 | 
						|
        # When call from template line.analytic_account_id comodel is
 | 
						|
        # 'account.analytic.contract',
 | 
						|
        if self._name != 'account.analytic.invoice.line':
 | 
						|
            return
 | 
						|
        for line in self:
 | 
						|
            contract = line.analytic_account_id
 | 
						|
            date_start = (
 | 
						|
                self.env.context.get('old_date') or fields.Date.from_string(
 | 
						|
                    contract.recurring_next_date or fields.Date.today())
 | 
						|
            )
 | 
						|
            next_date = (
 | 
						|
                self.env.context.get('next_date') or
 | 
						|
                date_start + contract.get_relative_delta(
 | 
						|
                    contract.recurring_rule_type, contract.recurring_interval)
 | 
						|
            )
 | 
						|
            if contract.recurring_invoicing_type == 'pre-paid':
 | 
						|
                date_to = next_date - relativedelta(days=1)
 | 
						|
            else:
 | 
						|
                date_to = date_start
 | 
						|
            line.date_to = fields.Date.to_string(date_to)
 | 
						|
 | 
						|
    @api.multi
 | 
						|
    @api.constrains('discount')
 | 
						|
    def _check_discount(self):
 | 
						|
        for line in self:
 | 
						|
            if line.discount > 100:
 | 
						|
                raise ValidationError(
 | 
						|
                    _("Discount should be less or equal to 100"))
 | 
						|
 | 
						|
    @api.multi
 | 
						|
    @api.onchange('product_id')
 | 
						|
    def _onchange_product_id(self):
 | 
						|
        if not self.product_id:
 | 
						|
            return {'domain': {'uom_id': []}}
 | 
						|
 | 
						|
        vals = {}
 | 
						|
        domain = {'uom_id': [
 | 
						|
            ('category_id', '=', self.product_id.uom_id.category_id.id)]}
 | 
						|
        if not self.uom_id or (self.product_id.uom_id.category_id.id !=
 | 
						|
                               self.uom_id.category_id.id):
 | 
						|
            vals['uom_id'] = self.product_id.uom_id
 | 
						|
 | 
						|
        if self.analytic_account_id._name == 'account.analytic.account':
 | 
						|
            date = (
 | 
						|
                self.analytic_account_id.recurring_next_date or
 | 
						|
                fields.Date.today()
 | 
						|
            )
 | 
						|
            partner = self.analytic_account_id.partner_id
 | 
						|
 | 
						|
        else:
 | 
						|
            date = fields.Date.today()
 | 
						|
            partner = self.env.user.partner_id
 | 
						|
 | 
						|
        product = self.product_id.with_context(
 | 
						|
            lang=partner.lang,
 | 
						|
            partner=partner.id,
 | 
						|
            quantity=self.quantity,
 | 
						|
            date=date,
 | 
						|
            pricelist=self.analytic_account_id.pricelist_id.id,
 | 
						|
            uom=self.uom_id.id
 | 
						|
        )
 | 
						|
 | 
						|
        name = product.name_get()[0][1]
 | 
						|
        if product.description_sale:
 | 
						|
            name += '\n' + product.description_sale
 | 
						|
        vals['name'] = name
 | 
						|
 | 
						|
        vals['price_unit'] = product.price
 | 
						|
        self.update(vals)
 | 
						|
        return {'domain': domain}
 |