[IMP] mrp: New analytic for MRP
The (legacy) analytic account field on MOs, BOMs and workcenters has been replaced by the new "analytic distribution" field. The analytic line items are now created based on this distribution for the raw materials, workcenter costs and employee costs. A new analytic applicability (=domain) has been added for manufacturing orders as well. Community PR: https://github.com/odoo/odoo/pull/116477 Enterprise PR: https://github.com/odoo/enterprise/pull/38690 closes odoo/odoo#116477 Task: 3196563 Related: odoo/upgrade#4704 Related: odoo/enterprise#38690 Signed-off-by: Arnold Moyaux (arm) <arm@odoo.com>
This commit is contained in:
@@ -29,7 +29,7 @@ class AnalyticMixin(models.AbstractModel):
|
||||
FROM information_schema.tables
|
||||
WHERE table_name=%s '''
|
||||
self.env.cr.execute(query, [self._table])
|
||||
if self.env.cr.dictfetchone():
|
||||
if self.env.cr.dictfetchone() and self._fields['analytic_distribution'].store:
|
||||
query = f"""
|
||||
CREATE INDEX IF NOT EXISTS {self._table}_analytic_distribution_gin_index
|
||||
ON {self._table} USING gin(analytic_distribution);
|
||||
|
||||
@@ -8,11 +8,11 @@ class AccountAnalyticAccount(models.Model):
|
||||
_inherit = 'account.analytic.account'
|
||||
_description = 'Analytic Account'
|
||||
|
||||
production_ids = fields.One2many('mrp.production', 'analytic_account_id', string='Manufacturing Orders')
|
||||
production_ids = fields.Many2many('mrp.production')
|
||||
production_count = fields.Integer("Manufacturing Orders Count", compute='_compute_production_count')
|
||||
bom_ids = fields.One2many('mrp.bom', 'analytic_account_id', string='Bills of Materials')
|
||||
bom_ids = fields.Many2many('mrp.bom')
|
||||
bom_count = fields.Integer("BoM Count", compute='_compute_bom_count')
|
||||
workcenter_ids = fields.One2many('mrp.workcenter', 'costs_hour_account_id', string='Workcenters')
|
||||
workcenter_ids = fields.Many2many('mrp.workcenter')
|
||||
workorder_count = fields.Integer("Work Order Count", compute='_compute_workorder_count')
|
||||
|
||||
@api.depends('production_ids')
|
||||
@@ -77,3 +77,14 @@ class AccountAnalyticLine(models.Model):
|
||||
_inherit = 'account.analytic.line'
|
||||
|
||||
category = fields.Selection(selection_add=[('manufacturing_order', 'Manufacturing Order')])
|
||||
|
||||
class AccountAnalyticApplicability(models.Model):
|
||||
_inherit = 'account.analytic.applicability'
|
||||
_description = "Analytic Plan's Applicabilities"
|
||||
|
||||
business_domain = fields.Selection(
|
||||
selection_add=[
|
||||
('manufacturing_order', 'Manufacturing Order'),
|
||||
],
|
||||
ondelete={'manufacturing_order': 'cascade'},
|
||||
)
|
||||
|
||||
@@ -1,10 +1,48 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
import json
|
||||
|
||||
from odoo import models, api, fields
|
||||
|
||||
from odoo import fields, models
|
||||
|
||||
class MrpBom(models.Model):
|
||||
_inherit = 'mrp.bom'
|
||||
_name = 'mrp.bom'
|
||||
_inherit = ['mrp.bom', 'analytic.mixin']
|
||||
|
||||
analytic_account_id = fields.Many2one('account.analytic.account', 'Analytic Account', company_dependent=True, copy=True,
|
||||
help="Analytic account in which cost and revenue entries will take place for financial management of the manufacturing order.")
|
||||
# Company dependent JSON fields are not yet supported
|
||||
analytic_distribution_text = fields.Text(company_dependent=True)
|
||||
analytic_distribution = fields.Json(inverse="_inverse_analytic_distribution", store=False, precompute=False)
|
||||
analytic_account_ids = fields.Many2many('account.analytic.account', compute="_compute_analytic_account_ids", copy=True)
|
||||
|
||||
@api.depends_context('company')
|
||||
@api.depends('analytic_distribution_text')
|
||||
def _compute_analytic_distribution(self):
|
||||
for record in self:
|
||||
record.analytic_distribution = json.loads(record.analytic_distribution_text or '{}')
|
||||
|
||||
def _inverse_analytic_distribution(self):
|
||||
for record in self:
|
||||
record.analytic_distribution_text = json.dumps(record.analytic_distribution)
|
||||
|
||||
@api.depends('analytic_distribution')
|
||||
def _compute_analytic_account_ids(self):
|
||||
for record in self:
|
||||
record.analytic_account_ids = list(map(int, record.analytic_distribution.keys())) if record.analytic_distribution else []
|
||||
|
||||
@api.onchange('product_id')
|
||||
def _onchange_analytic_distribution(self):
|
||||
for record in self:
|
||||
if record.product_id:
|
||||
record.analytic_distribution = record.env['account.analytic.distribution.model']._get_distribution({
|
||||
"product_id": record.product_id.id,
|
||||
"product_categ_id": record.product_id.categ_id.id,
|
||||
"company_id": record.company_id.id,
|
||||
})
|
||||
|
||||
@api.constrains('analytic_distribution')
|
||||
def _check_analytic(self):
|
||||
for record in self:
|
||||
record.with_context({'validate_analytic': True})._validate_distribution(**{
|
||||
'product': record.product_id.id,
|
||||
'company_id': record.company_id.id,
|
||||
})
|
||||
|
||||
@@ -1,53 +1,60 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from ast import literal_eval
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.tools import float_is_zero, float_round
|
||||
from odoo.tools import float_round
|
||||
|
||||
|
||||
class MrpProduction(models.Model):
|
||||
_inherit = 'mrp.production'
|
||||
_name = 'mrp.production'
|
||||
_inherit = ['mrp.production', 'analytic.mixin']
|
||||
|
||||
extra_cost = fields.Float(copy=False, string='Extra Unit Cost')
|
||||
show_valuation = fields.Boolean(compute='_compute_show_valuation')
|
||||
analytic_account_id = fields.Many2one(
|
||||
'account.analytic.account', 'Analytic Account', copy=True,
|
||||
help="Analytic account in which cost and revenue entries will take\
|
||||
place for financial management of the manufacturing order.",
|
||||
compute='_compute_analytic_account_id', store=True, readonly=False)
|
||||
analytic_account_ids = fields.Many2many('account.analytic.account', compute='_compute_analytic_account_ids', store=True)
|
||||
|
||||
def _compute_show_valuation(self):
|
||||
for order in self:
|
||||
order.show_valuation = any(m.state == 'done' for m in order.move_finished_ids)
|
||||
|
||||
@api.depends('bom_id')
|
||||
def _compute_analytic_account_id(self):
|
||||
if self.bom_id.analytic_account_id:
|
||||
self.analytic_account_id = self.bom_id.analytic_account_id
|
||||
@api.depends('bom_id', 'product_id')
|
||||
def _compute_analytic_distribution(self):
|
||||
for record in self:
|
||||
if record.bom_id.analytic_distribution:
|
||||
record.analytic_distribution = record.bom_id.analytic_distribution
|
||||
else:
|
||||
record.analytic_distribution = record.env['account.analytic.distribution.model']._get_distribution({
|
||||
"product_id": record.product_id.id,
|
||||
"product_categ_id": record.product_id.categ_id.id,
|
||||
"company_id": record.company_id.id,
|
||||
})
|
||||
|
||||
@api.depends('analytic_distribution')
|
||||
def _compute_analytic_account_ids(self):
|
||||
for record in self:
|
||||
record.analytic_account_ids = list(
|
||||
map(int, record.analytic_distribution.keys())) if record.analytic_distribution else []
|
||||
|
||||
@api.constrains('analytic_distribution')
|
||||
def _check_analytic(self):
|
||||
for record in self:
|
||||
params = {'business_domain': 'manufacturing_order', 'company_id': record.company_id.id}
|
||||
if record.product_id:
|
||||
params['product'] = record.product_id.id
|
||||
record.with_context({'validate_analytic': True})._validate_distribution(**params)
|
||||
|
||||
def write(self, vals):
|
||||
origin_analytic_account = {production: production.analytic_account_id for production in self}
|
||||
res = super().write(vals)
|
||||
for production in self:
|
||||
if vals.get('name'):
|
||||
production.move_raw_ids.analytic_account_line_id.ref = production.display_name
|
||||
production.move_raw_ids.analytic_account_line_ids.ref = production.display_name
|
||||
for workorder in production.workorder_ids:
|
||||
workorder.mo_analytic_account_line_id.ref = production.display_name
|
||||
workorder.mo_analytic_account_line_id.name = _("[WC] %s", workorder.display_name)
|
||||
if 'analytic_account_id' in vals and production.state != 'draft':
|
||||
if vals['analytic_account_id'] and origin_analytic_account[production]:
|
||||
# Link the account analytic lines to the new AA
|
||||
production.move_raw_ids.analytic_account_line_id.write({'account_id': vals['analytic_account_id']})
|
||||
production.workorder_ids.mo_analytic_account_line_id.write({'account_id': vals['analytic_account_id']})
|
||||
elif vals['analytic_account_id'] and not origin_analytic_account[production]:
|
||||
# Create the account analytic lines if no AA is set in the MO
|
||||
production.move_raw_ids._account_analytic_entry_move()
|
||||
production.workorder_ids._create_or_update_analytic_entry()
|
||||
else:
|
||||
production.move_raw_ids.analytic_account_line_id.unlink()
|
||||
production.workorder_ids.mo_analytic_account_line_id.unlink()
|
||||
workorder.mo_analytic_account_line_ids.ref = production.display_name
|
||||
workorder.mo_analytic_account_line_ids.name = _("[WC] %s", workorder.display_name)
|
||||
if 'analytic_distribution' in vals and production.state != 'draft':
|
||||
production.move_raw_ids._account_analytic_entry_move()
|
||||
production.workorder_ids._create_or_update_analytic_entry()
|
||||
return res
|
||||
|
||||
def action_view_stock_valuation_layers(self):
|
||||
@@ -60,15 +67,14 @@ class MrpProduction(models.Model):
|
||||
context['search_default_group_by_product_id'] = False
|
||||
return dict(action, domain=domain, context=context)
|
||||
|
||||
def action_view_analytic_account(self):
|
||||
def action_view_analytic_accounts(self):
|
||||
self.ensure_one()
|
||||
return {
|
||||
"type": "ir.actions.act_window",
|
||||
"res_model": "account.analytic.account",
|
||||
'res_id': self.analytic_account_id.id,
|
||||
"context": {"create": False},
|
||||
"name": _("Analytic Account"),
|
||||
'view_mode': 'form',
|
||||
'domain': [('id', 'in', self.analytic_account_ids.ids)],
|
||||
"name": _("Analytic Accounts"),
|
||||
'view_mode': 'tree,form',
|
||||
}
|
||||
|
||||
def _cal_price(self, consumed_moves):
|
||||
|
||||
@@ -1,12 +1,23 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import fields, models
|
||||
from odoo import api, models, fields
|
||||
|
||||
|
||||
class MrpWorkcenter(models.Model):
|
||||
_inherit = 'mrp.workcenter'
|
||||
_name = 'mrp.workcenter'
|
||||
_inherit = ['mrp.workcenter', 'analytic.mixin']
|
||||
|
||||
costs_hour_account_id = fields.Many2one(
|
||||
'account.analytic.account', string='Analytic Account',
|
||||
help="Posts analytical accounting entries in real time for both component and operational costs.")
|
||||
costs_hour_account_ids = fields.Many2many('account.analytic.account', compute="_compute_costs_hour_account_ids", store=True)
|
||||
|
||||
@api.depends('analytic_distribution')
|
||||
def _compute_costs_hour_account_ids(self):
|
||||
for record in self:
|
||||
record.costs_hour_account_ids = list(map(int, record.analytic_distribution.keys())) if record.analytic_distribution else []
|
||||
|
||||
@api.constrains('analytic_distribution')
|
||||
def _check_analytic(self):
|
||||
for record in self:
|
||||
record.with_context({'validate_analytic': True})._validate_distribution(**{
|
||||
'company_id': record.company_id.id,
|
||||
})
|
||||
|
||||
@@ -2,14 +2,13 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import fields, models, _
|
||||
from odoo.tools import float_is_zero
|
||||
|
||||
|
||||
class MrpWorkorder(models.Model):
|
||||
_inherit = 'mrp.workorder'
|
||||
|
||||
mo_analytic_account_line_id = fields.Many2one('account.analytic.line', copy=False)
|
||||
wc_analytic_account_line_id = fields.Many2one('account.analytic.line', copy=False)
|
||||
mo_analytic_account_line_ids = fields.Many2many('account.analytic.line', 'mrp_workorder_mo_analytic_rel', copy=False)
|
||||
wc_analytic_account_line_ids = fields.Many2many('account.analytic.line', 'mrp_workorder_wc_analytic_rel', copy=False)
|
||||
|
||||
def _compute_duration(self):
|
||||
res = super()._compute_duration()
|
||||
@@ -22,15 +21,15 @@ class MrpWorkorder(models.Model):
|
||||
return res
|
||||
|
||||
def action_cancel(self):
|
||||
(self.mo_analytic_account_line_id | self.wc_analytic_account_line_id).unlink()
|
||||
(self.mo_analytic_account_line_ids | self.wc_analytic_account_line_ids).unlink()
|
||||
return super().action_cancel()
|
||||
|
||||
def _prepare_analytic_line_values(self, account, unit_amount, amount):
|
||||
def _prepare_analytic_line_values(self, account, amount, unit_amount):
|
||||
self.ensure_one()
|
||||
return {
|
||||
'name': _("[WC] %s", self.display_name),
|
||||
'amount': amount,
|
||||
'account_id': account.id,
|
||||
'account_id': account,
|
||||
'unit_amount': unit_amount,
|
||||
'product_id': self.product_id.id,
|
||||
'product_uom_id': self.env.ref('uom.product_uom_hour').id,
|
||||
@@ -40,43 +39,18 @@ class MrpWorkorder(models.Model):
|
||||
}
|
||||
|
||||
def _create_or_update_analytic_entry(self):
|
||||
wo_to_link_mo_analytic_line = self.env['mrp.workorder']
|
||||
wo_to_link_wc_analytic_line = self.env['mrp.workorder']
|
||||
mo_analytic_line_vals_list = []
|
||||
wc_analytic_line_vals_list = []
|
||||
for wo in self.filtered(lambda wo: wo.production_id.analytic_account_id or wo.workcenter_id.costs_hour_account_id):
|
||||
for wo in self.filtered(lambda wo: wo.id and (wo.production_id.analytic_distribution or wo.workcenter_id.analytic_distribution or wo.wc_analytic_account_line_ids or wo.mo_analytic_account_line_ids)):
|
||||
hours = wo.duration / 60.0
|
||||
value = -hours * wo.workcenter_id.costs_hour
|
||||
mo_account = wo.production_id.analytic_account_id
|
||||
wc_account = wo.workcenter_id.costs_hour_account_id
|
||||
if mo_account:
|
||||
mo_currency = mo_account.currency_id or wo.company_id.currency_id
|
||||
is_zero = float_is_zero(value, precision_rounding=mo_currency.rounding)
|
||||
if wo.mo_analytic_account_line_id:
|
||||
wo.mo_analytic_account_line_id.write({
|
||||
'unit_amount': hours,
|
||||
'amount': value if not is_zero else 0,
|
||||
})
|
||||
elif not is_zero:
|
||||
wo_to_link_mo_analytic_line += wo
|
||||
mo_analytic_line_vals_list.append(wo._prepare_analytic_line_values(mo_account, hours, value))
|
||||
if wc_account and wc_account != mo_account:
|
||||
is_zero = float_is_zero(value, precision_rounding=wc_account.currency_id.rounding)
|
||||
if wo.wc_analytic_account_line_id:
|
||||
wo.wc_analytic_account_line_id.write({
|
||||
'unit_amount': hours,
|
||||
'amount': value if not is_zero else 0,
|
||||
})
|
||||
elif not is_zero:
|
||||
wo_to_link_wc_analytic_line += wo
|
||||
wc_analytic_line_vals_list.append(wo._prepare_analytic_line_values(wc_account, hours, value))
|
||||
analytic_lines = self.env['account.analytic.line'].sudo().create(mo_analytic_line_vals_list + wc_analytic_line_vals_list)
|
||||
mo_analytic_lines, wc_analytic_lines = analytic_lines[:len(wo_to_link_mo_analytic_line)], analytic_lines[len(wo_to_link_mo_analytic_line):]
|
||||
for wo, analytic_line in zip(wo_to_link_mo_analytic_line, mo_analytic_lines):
|
||||
wo.mo_analytic_account_line_id = analytic_line
|
||||
for wo, analytic_line in zip(wo_to_link_wc_analytic_line, wc_analytic_lines):
|
||||
wo.wc_analytic_account_line_id = analytic_line
|
||||
|
||||
mo_analytic_line_vals = self.env['account.analytic.account']._perform_analytic_distribution(wo.production_id.analytic_distribution, value, hours, wo.mo_analytic_account_line_ids, wo)
|
||||
if mo_analytic_line_vals:
|
||||
wo.mo_analytic_account_line_ids += self.env['account.analytic.line'].sudo().create(mo_analytic_line_vals)
|
||||
|
||||
wc_analytic_line_vals = self.env['account.analytic.account']._perform_analytic_distribution(wo.workcenter_id.analytic_distribution, value, hours, wo.wc_analytic_account_line_ids, wo)
|
||||
if wc_analytic_line_vals:
|
||||
wo.wc_analytic_account_line_ids += self.env['account.analytic.line'].sudo().create(wc_analytic_line_vals)
|
||||
|
||||
def unlink(self):
|
||||
(self.mo_analytic_account_line_id | self.wc_analytic_account_line_id).unlink()
|
||||
(self.mo_analytic_account_line_ids | self.wc_analytic_account_line_ids).unlink()
|
||||
return super().unlink()
|
||||
|
||||
@@ -12,19 +12,19 @@ class StockMove(models.Model):
|
||||
res += self.filtered(lambda m: m.bom_line_id.bom_id.product_tmpl_id.id == product.product_tmpl_id.id)
|
||||
return res
|
||||
|
||||
def _generate_analytic_lines_data(self, unit_amount, amount):
|
||||
vals = super()._generate_analytic_lines_data(unit_amount, amount)
|
||||
if self.raw_material_production_id.analytic_account_id:
|
||||
def _generate_analytic_lines_data(self, unit_amount, amount, distribution, account, distribution_on_each_plan):
|
||||
vals = super()._generate_analytic_lines_data(unit_amount, amount, distribution, account, distribution_on_each_plan)
|
||||
if self._get_analytic_distribution():
|
||||
vals['name'] = _('[Raw] %s', self.product_id.display_name)
|
||||
vals['ref'] = self.raw_material_production_id.display_name
|
||||
vals['category'] = 'manufacturing_order'
|
||||
return vals
|
||||
|
||||
def _get_analytic_account(self):
|
||||
account = self.raw_material_production_id.analytic_account_id
|
||||
if account:
|
||||
return account
|
||||
return super()._get_analytic_account()
|
||||
def _get_analytic_distribution(self):
|
||||
distribution = self.raw_material_production_id.analytic_distribution
|
||||
if distribution:
|
||||
return distribution
|
||||
return super()._get_analytic_distribution()
|
||||
|
||||
def _ignore_automatic_valuation(self):
|
||||
return bool(self.raw_material_production_id)
|
||||
|
||||
@@ -9,6 +9,6 @@ class StockRule(models.Model):
|
||||
|
||||
def _prepare_mo_vals(self, product_id, product_qty, product_uom, location_id, name, origin, company_id, values, bom):
|
||||
res = super()._prepare_mo_vals(product_id, product_qty, product_uom, location_id, name, origin, company_id, values, bom)
|
||||
if not bom.analytic_account_id and values.get('analytic_account_id'):
|
||||
res['analytic_account_id'] = values.get('analytic_account_id').id
|
||||
if not bom.analytic_distribution and values.get('analytic_distribution'):
|
||||
res['analytic_distribution'] = values.get('analytic_distribution')
|
||||
return res
|
||||
|
||||
@@ -64,32 +64,30 @@ class TestAnalyticAccount(TestMrpAnalyticAccount):
|
||||
mo_form.product_id = self.product
|
||||
mo_form.bom_id = self.bom
|
||||
mo_form.product_qty = 10.0
|
||||
mo_form.analytic_account_id = self.analytic_account
|
||||
mo_form.analytic_distribution = {str(self.analytic_account.id): 100.0}
|
||||
mo = mo_form.save()
|
||||
mo.action_confirm()
|
||||
self.assertEqual(mo.state, 'confirmed')
|
||||
self.assertEqual(len(mo.move_raw_ids.analytic_account_line_id), 0)
|
||||
self.assertEqual(len(mo.move_raw_ids.analytic_account_line_ids), 0)
|
||||
# increase qty_producing to 5.0
|
||||
mo_form = Form(mo)
|
||||
mo_form.qty_producing = 5.0
|
||||
mo_form.save()
|
||||
self.assertEqual(mo.state, 'progress')
|
||||
self.assertEqual(mo.move_raw_ids.analytic_account_line_id.amount, -50.0)
|
||||
self.assertEqual(mo.move_raw_ids.analytic_account_line_ids.amount, -50.0)
|
||||
|
||||
# increase qty_producing to 10.0
|
||||
mo_form = Form(mo)
|
||||
mo_form.qty_producing = 10.0
|
||||
mo_form.save()
|
||||
# Hack to bypass test doing strange things
|
||||
mo._set_qty_producing()
|
||||
mo.workorder_ids.button_finish()
|
||||
self.assertEqual(mo.state, 'to_close')
|
||||
self.assertEqual(mo.move_raw_ids.analytic_account_line_id.amount, -100.0)
|
||||
self.assertEqual(mo.move_raw_ids.analytic_account_line_ids.amount, -100.0)
|
||||
|
||||
# mark as done
|
||||
mo.button_mark_done()
|
||||
self.assertEqual(mo.state, 'done')
|
||||
self.assertEqual(mo.move_raw_ids.analytic_account_line_id.amount, -100.0)
|
||||
self.assertEqual(mo.move_raw_ids.analytic_account_line_ids.amount, -100.0)
|
||||
|
||||
def test_mo_analytic_backorder(self):
|
||||
"""Test the analytic lines are correctly posted when backorder.
|
||||
@@ -99,65 +97,23 @@ class TestAnalyticAccount(TestMrpAnalyticAccount):
|
||||
mo_form.product_id = self.product
|
||||
mo_form.bom_id = self.bom
|
||||
mo_form.product_qty = 10.0
|
||||
mo_form.analytic_account_id = self.analytic_account
|
||||
mo_form.analytic_distribution = {str(self.analytic_account.id): 100.0}
|
||||
mo = mo_form.save()
|
||||
mo.action_confirm()
|
||||
self.assertEqual(mo.state, 'confirmed')
|
||||
self.assertEqual(len(mo.move_raw_ids.analytic_account_line_id), 0)
|
||||
self.assertEqual(len(mo.move_raw_ids.analytic_account_line_ids), 0)
|
||||
|
||||
# increase qty_producing to 5.0
|
||||
mo_form = Form(mo)
|
||||
mo_form.qty_producing = 5.0
|
||||
mo_form.save()
|
||||
self.assertEqual(mo.state, 'progress')
|
||||
self.assertEqual(mo.move_raw_ids.analytic_account_line_id.amount, -50.0)
|
||||
self.assertEqual(mo.move_raw_ids.analytic_account_line_ids.amount, -50.0)
|
||||
|
||||
backorder_wizard_dict = mo.button_mark_done()
|
||||
Form(self.env[(backorder_wizard_dict.get('res_model'))].with_context(backorder_wizard_dict['context'])).save().action_backorder()
|
||||
self.assertEqual(mo.state, 'done')
|
||||
self.assertEqual(mo.move_raw_ids.analytic_account_line_id.amount, -50.0)
|
||||
|
||||
def test_workcenter_same_analytic_account(self):
|
||||
"""Test when workcenter and MO are using the same analytic account, no
|
||||
duplicated lines will be post.
|
||||
"""
|
||||
# Required for `workorder_ids` to be visible in the view
|
||||
self.env.user.groups_id += self.env.ref('mrp.group_mrp_routings')
|
||||
# set wc analytic account to be the same of the one on the bom
|
||||
self.workcenter.costs_hour_account_id = self.analytic_account
|
||||
|
||||
# create a mo
|
||||
mo_form = Form(self.env['mrp.production'])
|
||||
mo_form.product_id = self.product
|
||||
mo_form.bom_id = self.bom
|
||||
mo_form.product_qty = 10.0
|
||||
mo_form.analytic_account_id = self.analytic_account
|
||||
mo = mo_form.save()
|
||||
mo.action_confirm()
|
||||
self.assertEqual(len(mo.workorder_ids.wc_analytic_account_line_id), 0)
|
||||
|
||||
# change duration to 60
|
||||
mo_form = Form(mo)
|
||||
with mo_form.workorder_ids.edit(0) as line_edit:
|
||||
line_edit.duration = 60.0
|
||||
mo_form.save()
|
||||
self.assertEqual(mo.workorder_ids.mo_analytic_account_line_id.amount, -10.0)
|
||||
self.assertEqual(len(mo.workorder_ids.wc_analytic_account_line_id), 0)
|
||||
|
||||
# change duration to 120
|
||||
with mo_form.workorder_ids.edit(0) as line_edit:
|
||||
line_edit.duration = 120.0
|
||||
mo_form.save()
|
||||
self.assertEqual(mo.workorder_ids.mo_analytic_account_line_id.amount, -20.0)
|
||||
self.assertEqual(len(mo.workorder_ids.wc_analytic_account_line_id), 0)
|
||||
|
||||
# mark as done
|
||||
mo_form.qty_producing = 10.0
|
||||
mo_form.save()
|
||||
mo.button_mark_done()
|
||||
self.assertEqual(mo.state, 'done')
|
||||
self.assertEqual(mo.workorder_ids.mo_analytic_account_line_id.amount, -20.0)
|
||||
self.assertEqual(len(mo.workorder_ids.wc_analytic_account_line_id), 0)
|
||||
self.assertEqual(mo.move_raw_ids.analytic_account_line_ids.amount, -50.0)
|
||||
|
||||
def test_workcenter_different_analytic_account(self):
|
||||
"""Test when workcenter and MO are using the same analytic account, no
|
||||
@@ -168,46 +124,46 @@ class TestAnalyticAccount(TestMrpAnalyticAccount):
|
||||
# set wc analytic account to be different from the one on the bom
|
||||
analytic_plan = self.env['account.analytic.plan'].create({'name': 'Plan Test', 'company_id': False})
|
||||
wc_analytic_account = self.env['account.analytic.account'].create({'name': 'wc_analytic_account', 'plan_id': analytic_plan.id})
|
||||
self.workcenter.costs_hour_account_id = wc_analytic_account
|
||||
self.workcenter.analytic_distribution = {str(wc_analytic_account.id): 100.0}
|
||||
|
||||
# create a mo
|
||||
mo_form = Form(self.env['mrp.production'])
|
||||
mo_form.product_id = self.product
|
||||
mo_form.bom_id = self.bom
|
||||
mo_form.product_qty = 10.0
|
||||
mo_form.analytic_account_id = self.analytic_account
|
||||
mo_form.analytic_distribution = {str(self.analytic_account.id): 100.0}
|
||||
mo = mo_form.save()
|
||||
mo.action_confirm()
|
||||
self.assertEqual(len(mo.workorder_ids.wc_analytic_account_line_id), 0)
|
||||
self.assertEqual(len(mo.workorder_ids.wc_analytic_account_line_ids), 0)
|
||||
|
||||
# change duration to 60
|
||||
mo_form = Form(mo)
|
||||
with mo_form.workorder_ids.edit(0) as line_edit:
|
||||
line_edit.duration = 60.0
|
||||
mo_form.save()
|
||||
self.assertEqual(mo.workorder_ids.mo_analytic_account_line_id.amount, -10.0)
|
||||
self.assertEqual(mo.workorder_ids.mo_analytic_account_line_id.account_id, self.analytic_account)
|
||||
self.assertEqual(mo.workorder_ids.wc_analytic_account_line_id.amount, -10.0)
|
||||
self.assertEqual(mo.workorder_ids.wc_analytic_account_line_id.account_id, wc_analytic_account)
|
||||
self.assertEqual(mo.workorder_ids.mo_analytic_account_line_ids.amount, -10.0)
|
||||
self.assertEqual(mo.workorder_ids.mo_analytic_account_line_ids.account_id, self.analytic_account)
|
||||
self.assertEqual(mo.workorder_ids.wc_analytic_account_line_ids.amount, -10.0)
|
||||
self.assertEqual(mo.workorder_ids.wc_analytic_account_line_ids.account_id, wc_analytic_account)
|
||||
|
||||
# change duration to 120
|
||||
with mo_form.workorder_ids.edit(0) as line_edit:
|
||||
line_edit.duration = 120.0
|
||||
mo_form.save()
|
||||
self.assertEqual(mo.workorder_ids.mo_analytic_account_line_id.amount, -20.0)
|
||||
self.assertEqual(mo.workorder_ids.mo_analytic_account_line_id.account_id, self.analytic_account)
|
||||
self.assertEqual(mo.workorder_ids.wc_analytic_account_line_id.amount, -20.0)
|
||||
self.assertEqual(mo.workorder_ids.wc_analytic_account_line_id.account_id, wc_analytic_account)
|
||||
self.assertEqual(mo.workorder_ids.mo_analytic_account_line_ids.amount, -20.0)
|
||||
self.assertEqual(mo.workorder_ids.mo_analytic_account_line_ids.account_id, self.analytic_account)
|
||||
self.assertEqual(mo.workorder_ids.wc_analytic_account_line_ids.amount, -20.0)
|
||||
self.assertEqual(mo.workorder_ids.wc_analytic_account_line_ids.account_id, wc_analytic_account)
|
||||
|
||||
# mark as done
|
||||
mo_form.qty_producing = 10.0
|
||||
mo_form.save()
|
||||
mo.button_mark_done()
|
||||
self.assertEqual(mo.state, 'done')
|
||||
self.assertEqual(mo.workorder_ids.mo_analytic_account_line_id.amount, -20.0)
|
||||
self.assertEqual(mo.workorder_ids.mo_analytic_account_line_id.account_id, self.analytic_account)
|
||||
self.assertEqual(mo.workorder_ids.wc_analytic_account_line_id.amount, -20.0)
|
||||
self.assertEqual(mo.workorder_ids.wc_analytic_account_line_id.account_id, wc_analytic_account)
|
||||
self.assertEqual(mo.workorder_ids.mo_analytic_account_line_ids.amount, -20.0)
|
||||
self.assertEqual(mo.workorder_ids.mo_analytic_account_line_ids.account_id, self.analytic_account)
|
||||
self.assertEqual(mo.workorder_ids.wc_analytic_account_line_ids.amount, -20.0)
|
||||
self.assertEqual(mo.workorder_ids.wc_analytic_account_line_ids.account_id, wc_analytic_account)
|
||||
|
||||
def test_changing_mo_analytic_account(self):
|
||||
""" Check if the MO account analytic lines are correctly updated
|
||||
@@ -220,46 +176,46 @@ class TestAnalyticAccount(TestMrpAnalyticAccount):
|
||||
mo_form.product_id = self.product
|
||||
mo_form.bom_id = self.bom
|
||||
mo_form.product_qty = 1
|
||||
mo_form.analytic_account_id = self.analytic_account
|
||||
mo_form.analytic_distribution = {str(self.analytic_account.id): 100.0}
|
||||
mo = mo_form.save()
|
||||
mo.action_confirm()
|
||||
self.assertEqual(mo.state, 'confirmed')
|
||||
self.assertEqual(len(mo.move_raw_ids.analytic_account_line_id), 0)
|
||||
self.assertEqual(len(mo.workorder_ids.mo_analytic_account_line_id), 0)
|
||||
self.assertEqual(len(mo.move_raw_ids.analytic_account_line_ids), 0)
|
||||
self.assertEqual(len(mo.workorder_ids.mo_analytic_account_line_ids), 0)
|
||||
|
||||
# Change duration to 60
|
||||
mo_form = Form(mo)
|
||||
with mo_form.workorder_ids.edit(0) as line_edit:
|
||||
line_edit.duration = 60.0
|
||||
mo_form.save()
|
||||
self.assertEqual(mo.workorder_ids.mo_analytic_account_line_id.account_id, self.analytic_account)
|
||||
self.assertEqual(mo.workorder_ids.mo_analytic_account_line_ids.account_id, self.analytic_account)
|
||||
|
||||
# Mark as done
|
||||
mo.button_mark_done()
|
||||
self.assertEqual(mo.state, 'done')
|
||||
self.assertEqual(len(mo.move_raw_ids.analytic_account_line_id), 1)
|
||||
self.assertEqual(len(mo.move_raw_ids.analytic_account_line_ids), 1)
|
||||
|
||||
# Create a new analytic account
|
||||
analytic_plan = self.env['account.analytic.plan'].create({'name': 'Plan Test', 'company_id': False})
|
||||
new_analytic_account = self.env['account.analytic.account'].create({'name': 'test_analytic_account_2', 'plan_id': analytic_plan.id})
|
||||
# Change the MO analytic account
|
||||
mo.analytic_account_id = new_analytic_account
|
||||
self.assertEqual(mo.move_raw_ids.analytic_account_line_id.account_id.id, new_analytic_account.id)
|
||||
self.assertEqual(mo.workorder_ids.mo_analytic_account_line_id.account_id.id, new_analytic_account.id)
|
||||
mo.analytic_distribution = {str(new_analytic_account.id): 100.0}
|
||||
self.assertEqual(mo.move_raw_ids.analytic_account_line_ids.account_id.id, new_analytic_account.id)
|
||||
self.assertEqual(mo.workorder_ids.mo_analytic_account_line_ids.account_id.id, new_analytic_account.id)
|
||||
|
||||
#Get the MO analytic account lines
|
||||
mo_analytic_account_raw_lines = mo.move_raw_ids.analytic_account_line_id
|
||||
mo_analytic_account_wc_lines = mo.workorder_ids.mo_analytic_account_line_id
|
||||
mo.analytic_account_id = False
|
||||
mo_analytic_account_raw_lines = mo.move_raw_ids.analytic_account_line_ids
|
||||
mo_analytic_account_wc_lines = mo.move_raw_ids.analytic_account_line_ids
|
||||
mo.analytic_distribution = {}
|
||||
# Check that the MO analytic account lines are deleted
|
||||
self.assertEqual(len(mo.move_raw_ids.analytic_account_line_id), 0)
|
||||
self.assertEqual(len(mo.workorder_ids.mo_analytic_account_line_id), 0)
|
||||
self.assertEqual(len(mo.move_raw_ids.analytic_account_line_ids), 0)
|
||||
self.assertEqual(len(mo.workorder_ids.mo_analytic_account_line_ids), 0)
|
||||
self.assertFalse(mo_analytic_account_raw_lines.exists())
|
||||
self.assertFalse(mo_analytic_account_wc_lines.exists())
|
||||
# Check that the AA lines are recreated correctly if we delete the AA, save the MO, and assign a new one
|
||||
mo.analytic_account_id = self.analytic_account
|
||||
self.assertEqual(len(mo.move_raw_ids.analytic_account_line_id), 1)
|
||||
self.assertEqual(len(mo.workorder_ids.mo_analytic_account_line_id), 1)
|
||||
mo.analytic_distribution = {str(self.analytic_account.id): 100.0}
|
||||
self.assertEqual(len(mo.move_raw_ids.analytic_account_line_ids), 1)
|
||||
self.assertEqual(len(mo.workorder_ids.mo_analytic_account_line_ids), 1)
|
||||
|
||||
def test_add_remove_wo_analytic_no_company(self):
|
||||
"""Test the addition and removal of work orders to a MO linked to
|
||||
@@ -275,7 +231,7 @@ class TestAnalyticAccount(TestMrpAnalyticAccount):
|
||||
# Create a mo linked to an analytic account with no associated company
|
||||
mo_no_company = self.env['mrp.production'].create({
|
||||
'product_id': self.product.id,
|
||||
'analytic_account_id': analytic_account_no_company.id,
|
||||
'analytic_distribution': {str(analytic_account_no_company.id): 100.0},
|
||||
'product_uom_id': self.bom.product_uom_id.id,
|
||||
})
|
||||
|
||||
@@ -289,7 +245,7 @@ class TestAnalyticAccount(TestMrpAnalyticAccount):
|
||||
})
|
||||
mo_no_c_form.save()
|
||||
self.assertTrue(mo_no_company.workorder_ids)
|
||||
self.assertEqual(wo.production_id.analytic_account_id, analytic_account_no_company)
|
||||
self.assertEqual(wo.production_id.analytic_account_ids, analytic_account_no_company)
|
||||
self.assertEqual(len(analytic_account_no_company.line_ids), 1)
|
||||
mo_no_company.workorder_ids.unlink()
|
||||
self.assertEqual(len(analytic_account_no_company.line_ids), 0)
|
||||
|
||||
@@ -6,7 +6,9 @@
|
||||
<field name="inherit_id" ref="mrp.mrp_bom_form_view"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='picking_type_id']" position="after">
|
||||
<field name="analytic_account_id" groups="analytic.group_analytic_accounting"/>
|
||||
<field name="analytic_distribution" widget="analytic_distribution"
|
||||
groups="analytic.group_analytic_accounting"
|
||||
options="{'product_field': 'product_id'}"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
@@ -5,7 +5,10 @@
|
||||
<field name="inherit_id" ref="mrp.mrp_production_tree_view"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="name" position="after">
|
||||
<field name="analytic_account_id" groups="analytic.group_analytic_accounting" optional="hide"/>
|
||||
<field name="analytic_distribution" widget="analytic_distribution"
|
||||
groups="analytic.group_analytic_accounting"
|
||||
options="{'product_field': 'product_id', 'business_domain': 'manufacturing_order'}"
|
||||
optional="hide" />
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
@@ -29,9 +32,9 @@
|
||||
</div>
|
||||
</button>
|
||||
<button class="oe_stat_button" type="object"
|
||||
name="action_view_analytic_account"
|
||||
name="action_view_analytic_accounts"
|
||||
icon="fa-bar-chart-o"
|
||||
attrs="{'invisible': ['|', ('analytic_account_id', '=', False), ('state', 'in', ['draft', 'cancel'])]}"
|
||||
attrs="{'invisible': ['|', ('analytic_distribution', '=', False), ('state', 'in', ['draft', 'cancel'])]}"
|
||||
groups="analytic.group_analytic_accounting">
|
||||
<div class="o_stat_info">
|
||||
<span class="o_stat_text">Analytic Account</span>
|
||||
@@ -41,7 +44,9 @@
|
||||
</xpath>
|
||||
<xpath expr="//page[@name='miscellaneous']//field[@name='date_deadline']" position="after">
|
||||
<t groups="stock.group_stock_manager">
|
||||
<field name="analytic_account_id" groups="analytic.group_analytic_accounting"/>
|
||||
<field name="analytic_distribution" widget="analytic_distribution"
|
||||
groups="analytic.group_analytic_accounting"
|
||||
options="{'product_field': 'product_id', 'business_domain': 'manufacturing_order'}"/>
|
||||
</t>
|
||||
</xpath>
|
||||
</field>
|
||||
|
||||
@@ -7,9 +7,6 @@
|
||||
'summary': "Monitor MRP using project",
|
||||
'category': 'Services/Project',
|
||||
'depends': ['mrp_account', 'project'],
|
||||
'demo': [
|
||||
'data/project_mrp_demo.xml',
|
||||
],
|
||||
'auto_install': True,
|
||||
'license': 'LGPL-3',
|
||||
}
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
<record id="mrp.mrp_production_3" model="mrp.production">
|
||||
<field name="analytic_account_id" ref="project.analytic_office_design"/>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
@@ -15,7 +15,7 @@ class Project(models.Model):
|
||||
def action_view_mrp_production(self):
|
||||
self.ensure_one()
|
||||
action = self.env['ir.actions.actions']._for_xml_id('mrp.mrp_production_action')
|
||||
action['domain'] = [('analytic_account_id', '=', self.analytic_account_id.id)]
|
||||
action['domain'] = [('id', 'in', self.analytic_account_id.production_ids.ids)]
|
||||
action['context'] = {'default_analytic_account_id': self.analytic_account_id.id}
|
||||
if self.production_count == 1:
|
||||
action['view_mode'] = 'form'
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
from . import account_chart_template
|
||||
from . import account_move
|
||||
from . import analytic_account
|
||||
from . import product
|
||||
from . import stock_move
|
||||
from . import stock_location
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import models
|
||||
from odoo.tools import float_compare, float_is_zero
|
||||
|
||||
|
||||
class AccountAnalyticPlan(models.Model):
|
||||
_inherit = 'account.analytic.plan'
|
||||
|
||||
def _calculate_distribution_amount(self, amount, percentage, distribution_on_each_plan):
|
||||
existing_amount = distribution_on_each_plan.get(self, 0)
|
||||
distribution_plan = existing_amount + percentage
|
||||
decimal_precision = self.env['decimal.precision'].precision_get('Percentage Analytic')
|
||||
if float_compare(distribution_plan, 100, precision_digits=decimal_precision) == 0:
|
||||
distributed_amount = amount * (100 - existing_amount) / 100.0
|
||||
else:
|
||||
distributed_amount = amount * percentage / 100.0
|
||||
distribution_on_each_plan[self] = distribution_plan
|
||||
return distributed_amount
|
||||
|
||||
class AccountAnalyticAccount(models.Model):
|
||||
_inherit = 'account.analytic.account'
|
||||
|
||||
def _perform_analytic_distribution(self, distribution, amount, unit_amount, lines, obj, additive=False):
|
||||
"""
|
||||
Redistributes the analytic lines to match the given distribution:
|
||||
- For account_ids where lines already exist, the amount and unit_amount of these lines get updated,
|
||||
lines where the updated amount becomes zero get unlinked.
|
||||
- For account_ids where lines don't exist yet, the line values to create them are returned,
|
||||
lines where the amount becomes zero are not included.
|
||||
|
||||
:param distribution: the desired distribution to match the analytic lines to
|
||||
:param amount: the total amount to distribute over the analytic lines
|
||||
:param unit_amount: the total unit amount (will not be distributed)
|
||||
:param lines: the (current) analytic account lines that need to be matched to the new distribution
|
||||
:param obj: the object on which _prepare_analytic_line_values(account_id, amount, unit_amount) will be
|
||||
called to get the template for the values of new analytic line objects
|
||||
:param additive: if True, the unit_amount and (distributed) amount get added to the existing lines
|
||||
|
||||
:returns: a list of dicts containing the values for new analytic lines that need to be created
|
||||
:rtype: dict
|
||||
"""
|
||||
distribution_on_each_plan = {}
|
||||
processed_distributions = []
|
||||
for existing_aal in lines:
|
||||
if distribution and existing_aal.account_id.id in [int(i) for i in distribution]:
|
||||
# Update the existing AAL for this account
|
||||
percentage = distribution[str(existing_aal.account_id.id)]
|
||||
new_amount = existing_aal.account_id.root_plan_id._calculate_distribution_amount(amount, percentage, distribution_on_each_plan)
|
||||
new_unit_amount = amount
|
||||
if additive:
|
||||
new_amount += existing_aal.amount
|
||||
new_unit_amount += existing_aal.unit_amount
|
||||
currency = existing_aal.account_id.currency_id or obj.company_id.currency_id
|
||||
if float_is_zero(new_amount, precision_rounding=currency.rounding):
|
||||
existing_aal.unlink()
|
||||
else:
|
||||
existing_aal.amount = new_amount
|
||||
existing_aal.unit_amount = new_unit_amount
|
||||
processed_distributions.append(existing_aal.account_id.id)
|
||||
else:
|
||||
# Delete the existing AAL if it is no longer present in the new distribution
|
||||
existing_aal.unlink()
|
||||
lines_to_link = []
|
||||
if not distribution:
|
||||
return []
|
||||
for account_id, percentage in distribution.items():
|
||||
# Only create a new AAL if an existing one was not already modified before
|
||||
account_id = int(account_id)
|
||||
if account_id not in processed_distributions:
|
||||
account = self.browse(account_id)
|
||||
new_amount = account.root_plan_id._calculate_distribution_amount(amount, percentage, distribution_on_each_plan)
|
||||
currency = account.currency_id or obj.company_id.currency_id
|
||||
if not float_is_zero(new_amount, precision_rounding=currency.rounding):
|
||||
lines_to_link.append(obj._prepare_analytic_line_values(account.id, new_amount, unit_amount))
|
||||
return lines_to_link
|
||||
@@ -5,7 +5,7 @@ from collections import defaultdict
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.tools import float_compare, float_is_zero, float_round, OrderedSet
|
||||
from odoo.tools import float_is_zero, float_round, float_compare, OrderedSet
|
||||
|
||||
import logging
|
||||
_logger = logging.getLogger(__name__)
|
||||
@@ -18,8 +18,7 @@ class StockMove(models.Model):
|
||||
help='Trigger a decrease of the delivered/received quantity in the associated Sale Order/Purchase Order')
|
||||
account_move_ids = fields.One2many('account.move', 'stock_move_id')
|
||||
stock_valuation_layer_ids = fields.One2many('stock.valuation.layer', 'stock_move_id')
|
||||
analytic_account_line_id = fields.Many2one(
|
||||
'account.analytic.line', copy=False)
|
||||
analytic_account_line_ids = fields.Many2many('account.analytic.line', copy=False)
|
||||
|
||||
def _filter_anglo_saxon_moves(self, product):
|
||||
return self.filtered(lambda m: m.product_id.id == product.id)
|
||||
@@ -31,7 +30,7 @@ class StockMove(models.Model):
|
||||
return action_data
|
||||
|
||||
def _action_cancel(self):
|
||||
self.analytic_account_line_id.unlink()
|
||||
self.analytic_account_line_ids.unlink()
|
||||
return super()._action_cancel()
|
||||
|
||||
def _should_force_price_unit(self):
|
||||
@@ -407,7 +406,7 @@ class StockMove(models.Model):
|
||||
|
||||
def _prepare_analytic_lines(self):
|
||||
self.ensure_one()
|
||||
if not self._get_analytic_account():
|
||||
if not self._get_analytic_distribution() and not self.analytic_account_line_ids:
|
||||
return False
|
||||
|
||||
if self.state in ['cancel', 'draft']:
|
||||
@@ -429,24 +428,19 @@ class StockMove(models.Model):
|
||||
elif sum(self.stock_valuation_layer_ids.mapped('quantity')):
|
||||
amount = sum(self.stock_valuation_layer_ids.mapped('value'))
|
||||
unit_amount = - sum(self.stock_valuation_layer_ids.mapped('quantity'))
|
||||
if self.analytic_account_line_id:
|
||||
self.analytic_account_line_id.unit_amount = unit_amount
|
||||
self.analytic_account_line_id.amount = amount
|
||||
return False
|
||||
elif amount:
|
||||
return self._generate_analytic_lines_data(
|
||||
unit_amount, amount)
|
||||
|
||||
return self.env['account.analytic.account']._perform_analytic_distribution(
|
||||
self._get_analytic_distribution(), amount, unit_amount, self.analytic_account_line_ids, self)
|
||||
|
||||
def _ignore_automatic_valuation(self):
|
||||
return False
|
||||
|
||||
def _generate_analytic_lines_data(self, unit_amount, amount):
|
||||
def _prepare_analytic_line_values(self, account, amount, unit_amount):
|
||||
self.ensure_one()
|
||||
account_id = self._get_analytic_account()
|
||||
return {
|
||||
'name': self.name,
|
||||
'amount': amount,
|
||||
'account_id': account_id.id,
|
||||
'account_id': account,
|
||||
'unit_amount': unit_amount,
|
||||
'product_id': self.product_id.id,
|
||||
'product_uom_id': self.product_id.uom_id.id,
|
||||
@@ -532,18 +526,10 @@ class StockMove(models.Model):
|
||||
}
|
||||
|
||||
def _account_analytic_entry_move(self):
|
||||
analytic_lines_vals = []
|
||||
moves_to_link = []
|
||||
for move in self:
|
||||
analytic_line_vals = move._prepare_analytic_lines()
|
||||
if not analytic_line_vals:
|
||||
continue
|
||||
moves_to_link.append(move.id)
|
||||
analytic_lines_vals.append(analytic_line_vals)
|
||||
analytic_lines = self.env['account.analytic.line'].sudo().create(analytic_lines_vals)
|
||||
for move_id, analytic_line in zip(moves_to_link, analytic_lines):
|
||||
self.env['stock.move'].browse(
|
||||
move_id).analytic_account_line_id = analytic_line
|
||||
if analytic_line_vals:
|
||||
move.analytic_account_line_ids += self.env['account.analytic.line'].sudo().create(analytic_line_vals)
|
||||
|
||||
def _account_entry_move(self, qty, description, svl_id, cost):
|
||||
""" Accounting Valuation Entries """
|
||||
@@ -593,7 +579,7 @@ class StockMove(models.Model):
|
||||
|
||||
return am_vals
|
||||
|
||||
def _get_analytic_account(self):
|
||||
def _get_analytic_distribution(self):
|
||||
return False
|
||||
|
||||
def _get_related_invoices(self): # To be overridden in purchase and sale_stock
|
||||
|
||||
Reference in New Issue
Block a user