mirror of
https://github.com/bringout/oca-ocb-accounting.git
synced 2026-04-24 18:02:04 +02:00
Initial commit: Accounting packages
This commit is contained in:
commit
4ef34c2317
2661 changed files with 1709616 additions and 0 deletions
|
|
@ -0,0 +1,12 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
from . import analytic_account
|
||||
from . import mrp_bom
|
||||
from . import mrp_workcenter
|
||||
from . import mrp_workorder
|
||||
from . import mrp_production
|
||||
from . import mrp_routing
|
||||
from . import product
|
||||
from . import stock_move
|
||||
from . import stock_rule
|
||||
from . import account_move
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,28 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import models
|
||||
|
||||
from collections import defaultdict
|
||||
|
||||
|
||||
class AccountMoveLine(models.Model):
|
||||
_inherit = "account.move.line"
|
||||
|
||||
def _get_invoiced_qty_per_product(self):
|
||||
# Replace the kit-type products with their components
|
||||
qties = defaultdict(float)
|
||||
res = super()._get_invoiced_qty_per_product()
|
||||
invoiced_products = self.env['product.product'].concat(*res.keys())
|
||||
bom_kits = self.env['mrp.bom']._bom_find(invoiced_products, company_id=self.company_id[:1].id, bom_type='phantom')
|
||||
for product, qty in res.items():
|
||||
bom_kit = bom_kits[product]
|
||||
if bom_kit:
|
||||
invoiced_qty = product.uom_id._compute_quantity(qty, bom_kit.product_uom_id, round=False)
|
||||
factor = invoiced_qty / bom_kit.product_qty
|
||||
dummy, bom_sub_lines = bom_kit.explode(product, factor)
|
||||
for bom_line, bom_line_data in bom_sub_lines:
|
||||
qties[bom_line.product_id] += bom_line.product_uom_id._compute_quantity(bom_line_data['qty'], bom_line.product_id.uom_id)
|
||||
else:
|
||||
qties[product] += qty
|
||||
return qties
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
|
||||
|
||||
class AccountAnalyticAccount(models.Model):
|
||||
_inherit = 'account.analytic.account'
|
||||
_description = 'Analytic Account'
|
||||
|
||||
production_ids = fields.One2many('mrp.production', 'analytic_account_id', string='Manufacturing Orders')
|
||||
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_count = fields.Integer("BoM Count", compute='_compute_bom_count')
|
||||
workcenter_ids = fields.One2many('mrp.workcenter', 'costs_hour_account_id', string='Workcenters')
|
||||
workorder_count = fields.Integer("Work Order Count", compute='_compute_workorder_count')
|
||||
|
||||
@api.depends('production_ids')
|
||||
def _compute_production_count(self):
|
||||
for account in self:
|
||||
account.production_count = len(account.production_ids)
|
||||
|
||||
@api.depends('bom_ids')
|
||||
def _compute_bom_count(self):
|
||||
for account in self:
|
||||
account.bom_count = len(account.bom_ids)
|
||||
|
||||
@api.depends('workcenter_ids.order_ids', 'production_ids.workorder_ids')
|
||||
def _compute_workorder_count(self):
|
||||
for account in self:
|
||||
account.workorder_count = len(account.workcenter_ids.order_ids | account.production_ids.workorder_ids)
|
||||
|
||||
def action_view_mrp_production(self):
|
||||
self.ensure_one()
|
||||
result = {
|
||||
"type": "ir.actions.act_window",
|
||||
"res_model": "mrp.production",
|
||||
"domain": [['id', 'in', self.production_ids.ids]],
|
||||
"name": _("Manufacturing Orders"),
|
||||
'view_mode': 'tree,form',
|
||||
"context": {'default_analytic_account_id': self.id},
|
||||
}
|
||||
if len(self.production_ids) == 1:
|
||||
result['view_mode'] = 'form'
|
||||
result['res_id'] = self.production_ids.id
|
||||
return result
|
||||
|
||||
def action_view_mrp_bom(self):
|
||||
self.ensure_one()
|
||||
result = {
|
||||
"type": "ir.actions.act_window",
|
||||
"res_model": "mrp.bom",
|
||||
"domain": [['id', 'in', self.bom_ids.ids]],
|
||||
"name": _("Bills of Materials"),
|
||||
'view_mode': 'tree,form',
|
||||
"context": {'default_analytic_account_id': self.id},
|
||||
}
|
||||
if self.bom_count == 1:
|
||||
result['view_mode'] = 'form'
|
||||
result['res_id'] = self.bom_ids.id
|
||||
return result
|
||||
|
||||
def action_view_workorder(self):
|
||||
self.ensure_one()
|
||||
result = {
|
||||
"type": "ir.actions.act_window",
|
||||
"res_model": "mrp.workorder",
|
||||
"domain": [['id', 'in', (self.workcenter_ids.order_ids | self.production_ids.workorder_ids).ids]],
|
||||
"context": {"create": False},
|
||||
"name": _("Work Orders"),
|
||||
'view_mode': 'tree',
|
||||
}
|
||||
return result
|
||||
|
||||
|
||||
class AccountAnalyticLine(models.Model):
|
||||
_inherit = 'account.analytic.line'
|
||||
|
||||
category = fields.Selection(selection_add=[('manufacturing_order', 'Manufacturing Order')])
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import fields, models
|
||||
|
||||
class MrpBom(models.Model):
|
||||
_inherit = 'mrp.bom'
|
||||
|
||||
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.")
|
||||
|
|
@ -0,0 +1,116 @@
|
|||
# -*- 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
|
||||
|
||||
|
||||
class MrpProductionWorkcenterLineTime(models.Model):
|
||||
_inherit = 'mrp.workcenter.productivity'
|
||||
|
||||
# checked when a ongoing production posts journal entries for its costs.
|
||||
# This way, we can record one production's cost multiple times and only
|
||||
# consider new entries in the work centers time lines."
|
||||
cost_already_recorded = fields.Boolean('Cost Recorded')
|
||||
|
||||
|
||||
class MrpProduction(models.Model):
|
||||
_inherit = 'mrp.production'
|
||||
|
||||
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)
|
||||
|
||||
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):
|
||||
for order in self:
|
||||
if order.bom_id.analytic_account_id:
|
||||
order.analytic_account_id = order.bom_id.analytic_account_id
|
||||
|
||||
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
|
||||
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()
|
||||
return res
|
||||
|
||||
def action_view_stock_valuation_layers(self):
|
||||
self.ensure_one()
|
||||
domain = [('id', 'in', (self.move_raw_ids + self.move_finished_ids + self.scrap_ids.move_id).stock_valuation_layer_ids.ids)]
|
||||
action = self.env["ir.actions.actions"]._for_xml_id("stock_account.stock_valuation_layer_action")
|
||||
context = literal_eval(action['context'])
|
||||
context.update(self.env.context)
|
||||
context['no_at_date'] = True
|
||||
context['search_default_group_by_product_id'] = False
|
||||
return dict(action, domain=domain, context=context)
|
||||
|
||||
def action_view_analytic_account(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',
|
||||
}
|
||||
|
||||
def _cal_price(self, consumed_moves):
|
||||
"""Set a price unit on the finished move according to `consumed_moves`.
|
||||
"""
|
||||
super(MrpProduction, self)._cal_price(consumed_moves)
|
||||
work_center_cost = 0
|
||||
finished_move = self.move_finished_ids.filtered(
|
||||
lambda x: x.product_id == self.product_id and x.state not in ('done', 'cancel') and x.quantity_done > 0)
|
||||
if finished_move:
|
||||
finished_move.ensure_one()
|
||||
for work_order in self.workorder_ids:
|
||||
time_lines = work_order.time_ids.filtered(lambda t: t.date_end and not t.cost_already_recorded)
|
||||
work_center_cost += work_order._cal_cost(times=time_lines)
|
||||
time_lines.write({'cost_already_recorded': True})
|
||||
qty_done = finished_move.product_uom._compute_quantity(
|
||||
finished_move.quantity_done, finished_move.product_id.uom_id)
|
||||
extra_cost = self.extra_cost * qty_done
|
||||
total_cost = - sum(consumed_moves.sudo().stock_valuation_layer_ids.mapped('value')) + work_center_cost + extra_cost
|
||||
byproduct_moves = self.move_byproduct_ids.filtered(lambda m: m.state not in ('done', 'cancel') and m.quantity_done > 0)
|
||||
byproduct_cost_share = 0
|
||||
for byproduct in byproduct_moves:
|
||||
if byproduct.cost_share == 0:
|
||||
continue
|
||||
byproduct_cost_share += byproduct.cost_share
|
||||
if byproduct.product_id.cost_method in ('fifo', 'average'):
|
||||
byproduct.price_unit = total_cost * byproduct.cost_share / 100 / byproduct.product_uom._compute_quantity(byproduct.quantity_done, byproduct.product_id.uom_id)
|
||||
if finished_move.product_id.cost_method in ('fifo', 'average'):
|
||||
finished_move.price_unit = total_cost * float_round(1 - byproduct_cost_share / 100, precision_rounding=0.0001) / qty_done
|
||||
return True
|
||||
|
||||
def _get_backorder_mo_vals(self):
|
||||
res = super()._get_backorder_mo_vals()
|
||||
res['extra_cost'] = self.extra_cost
|
||||
return res
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import models
|
||||
|
||||
|
||||
class MrpRoutingWorkcenter(models.Model):
|
||||
_inherit = 'mrp.routing.workcenter'
|
||||
|
||||
def _total_cost_per_hour(self):
|
||||
self.ensure_one()
|
||||
return self.workcenter_id.costs_hour
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class MrpWorkcenter(models.Model):
|
||||
_inherit = 'mrp.workcenter'
|
||||
|
||||
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.")
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# 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)
|
||||
|
||||
def _compute_duration(self):
|
||||
res = super()._compute_duration()
|
||||
self._create_or_update_analytic_entry()
|
||||
return res
|
||||
|
||||
def _set_duration(self):
|
||||
res = super()._set_duration()
|
||||
self._create_or_update_analytic_entry()
|
||||
return res
|
||||
|
||||
def action_cancel(self):
|
||||
(self.mo_analytic_account_line_id | self.wc_analytic_account_line_id).unlink()
|
||||
return super().action_cancel()
|
||||
|
||||
def _prepare_analytic_line_values(self, account, unit_amount, amount):
|
||||
self.ensure_one()
|
||||
return {
|
||||
'name': _("[WC] %s", self.display_name),
|
||||
'amount': amount,
|
||||
'account_id': account.id,
|
||||
'unit_amount': unit_amount,
|
||||
'product_id': self.product_id.id,
|
||||
'product_uom_id': self.env.ref('uom.product_uom_hour').id,
|
||||
'company_id': self.company_id.id,
|
||||
'ref': self.production_id.name,
|
||||
'category': 'manufacturing_order',
|
||||
}
|
||||
|
||||
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):
|
||||
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:
|
||||
wc_currency = wc_account.currency_id or wo.company_id.currency_id
|
||||
is_zero = float_is_zero(value, precision_rounding=wc_currency.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
|
||||
|
||||
def unlink(self):
|
||||
(self.mo_analytic_account_line_id | self.wc_analytic_account_line_id).unlink()
|
||||
return super().unlink()
|
||||
110
odoo-bringout-oca-ocb-mrp_account/mrp_account/models/product.py
Normal file
110
odoo-bringout-oca-ocb-mrp_account/mrp_account/models/product.py
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import models
|
||||
from odoo.tools import float_round, groupby
|
||||
|
||||
|
||||
class ProductTemplate(models.Model):
|
||||
_name = 'product.template'
|
||||
_inherit = 'product.template'
|
||||
|
||||
def action_bom_cost(self):
|
||||
templates = self.filtered(lambda t: t.product_variant_count == 1 and t.bom_count > 0)
|
||||
if templates:
|
||||
return templates.mapped('product_variant_id').action_bom_cost()
|
||||
|
||||
def button_bom_cost(self):
|
||||
templates = self.filtered(lambda t: t.product_variant_count == 1 and t.bom_count > 0)
|
||||
if templates:
|
||||
return templates.mapped('product_variant_id').button_bom_cost()
|
||||
|
||||
|
||||
class ProductProduct(models.Model):
|
||||
_name = 'product.product'
|
||||
_inherit = 'product.product'
|
||||
|
||||
def button_bom_cost(self):
|
||||
self.ensure_one()
|
||||
self._set_price_from_bom()
|
||||
|
||||
def action_bom_cost(self):
|
||||
boms_to_recompute = self.env['mrp.bom'].search(['|', ('product_id', 'in', self.ids), '&', ('product_id', '=', False), ('product_tmpl_id', 'in', self.mapped('product_tmpl_id').ids)])
|
||||
for product in self:
|
||||
product._set_price_from_bom(boms_to_recompute)
|
||||
|
||||
def _set_price_from_bom(self, boms_to_recompute=False):
|
||||
self.ensure_one()
|
||||
bom = self.env['mrp.bom']._bom_find(self)[self]
|
||||
if bom:
|
||||
self.standard_price = self._compute_bom_price(bom, boms_to_recompute=boms_to_recompute)
|
||||
else:
|
||||
bom = self.env['mrp.bom'].search([('byproduct_ids.product_id', '=', self.id)], order='sequence, product_id, id', limit=1)
|
||||
if bom:
|
||||
price = self._compute_bom_price(bom, boms_to_recompute=boms_to_recompute, byproduct_bom=True)
|
||||
if price:
|
||||
self.standard_price = price
|
||||
|
||||
def _compute_average_price(self, qty_invoiced, qty_to_invoice, stock_moves, is_returned=False):
|
||||
self.ensure_one()
|
||||
if stock_moves.product_id == self:
|
||||
return super()._compute_average_price(qty_invoiced, qty_to_invoice, stock_moves, is_returned=is_returned)
|
||||
bom = self.env['mrp.bom']._bom_find(self, company_id=stock_moves.company_id.id, bom_type='phantom')[self]
|
||||
if not bom:
|
||||
return super()._compute_average_price(qty_invoiced, qty_to_invoice, stock_moves, is_returned=is_returned)
|
||||
value = 0
|
||||
dummy, bom_lines = bom.explode(self, 1)
|
||||
bom_lines = {line: data for line, data in bom_lines}
|
||||
for bom_line, moves_list in groupby(stock_moves.filtered(lambda sm: sm.state != 'cancel'), lambda sm: sm.bom_line_id):
|
||||
if bom_line not in bom_lines:
|
||||
for move in moves_list:
|
||||
component_quantity = next(
|
||||
(bml.product_qty for bml in move.product_id.bom_line_ids if bml in bom_lines),
|
||||
1
|
||||
)
|
||||
value += component_quantity * move.product_id._compute_average_price(qty_invoiced * move.product_qty, qty_to_invoice * move.product_qty, move, is_returned=is_returned)
|
||||
continue
|
||||
line_qty = bom_line.product_uom_id._compute_quantity(bom_lines[bom_line]['qty'], bom_line.product_id.uom_id)
|
||||
moves = self.env['stock.move'].concat(*moves_list)
|
||||
value += line_qty * bom_line.product_id._compute_average_price(qty_invoiced * line_qty, qty_to_invoice * line_qty, moves, is_returned=is_returned)
|
||||
return value
|
||||
|
||||
def _compute_bom_price(self, bom, boms_to_recompute=False, byproduct_bom=False):
|
||||
self.ensure_one()
|
||||
if not bom:
|
||||
return 0
|
||||
if not boms_to_recompute:
|
||||
boms_to_recompute = []
|
||||
total = 0
|
||||
for opt in bom.operation_ids:
|
||||
if opt._skip_operation_line(self):
|
||||
continue
|
||||
|
||||
duration_expected = (
|
||||
opt.workcenter_id._get_expected_duration(self) +
|
||||
opt.time_cycle * 100 / opt.workcenter_id.time_efficiency)
|
||||
total += (duration_expected / 60) * opt._total_cost_per_hour()
|
||||
|
||||
for line in bom.bom_line_ids:
|
||||
if line._skip_bom_line(self):
|
||||
continue
|
||||
|
||||
# Compute recursive if line has `child_line_ids`
|
||||
if line.child_bom_id and line.child_bom_id in boms_to_recompute:
|
||||
child_total = line.product_id._compute_bom_price(line.child_bom_id, boms_to_recompute=boms_to_recompute)
|
||||
total += line.product_id.uom_id._compute_price(child_total, line.product_uom_id) * line.product_qty
|
||||
else:
|
||||
total += line.product_id.uom_id._compute_price(line.product_id.standard_price, line.product_uom_id) * line.product_qty
|
||||
if byproduct_bom:
|
||||
byproduct_lines = bom.byproduct_ids.filtered(lambda b: b.product_id == self and b.cost_share != 0)
|
||||
product_uom_qty = 0
|
||||
for line in byproduct_lines:
|
||||
product_uom_qty += line.product_uom_id._compute_quantity(line.product_qty, self.uom_id, round=False)
|
||||
byproduct_cost_share = sum(byproduct_lines.mapped('cost_share'))
|
||||
if byproduct_cost_share and product_uom_qty:
|
||||
return total * byproduct_cost_share / 100 / product_uom_qty
|
||||
else:
|
||||
byproduct_cost_share = sum(bom.byproduct_ids.mapped('cost_share'))
|
||||
if byproduct_cost_share:
|
||||
total *= float_round(1 - byproduct_cost_share / 100, precision_rounding=0.0001)
|
||||
return bom.product_uom_id._compute_price(total / bom.product_qty, self.uom_id)
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import _, models
|
||||
|
||||
|
||||
class StockMove(models.Model):
|
||||
_inherit = "stock.move"
|
||||
|
||||
def _filter_anglo_saxon_moves(self, product):
|
||||
res = super(StockMove, self)._filter_anglo_saxon_moves(product)
|
||||
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:
|
||||
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_src_account(self, accounts_data):
|
||||
if not self.unbuild_id:
|
||||
return super()._get_src_account(accounts_data)
|
||||
else:
|
||||
return self.location_dest_id.valuation_out_account_id.id or accounts_data['stock_input'].id
|
||||
|
||||
def _get_dest_account(self, accounts_data):
|
||||
if not self.unbuild_id:
|
||||
return super()._get_dest_account(accounts_data)
|
||||
else:
|
||||
return self.location_id.valuation_in_account_id.id or accounts_data['stock_output'].id
|
||||
|
||||
def _is_returned(self, valued_type):
|
||||
if self.unbuild_id:
|
||||
return True
|
||||
return super()._is_returned(valued_type)
|
||||
|
||||
def _should_force_price_unit(self):
|
||||
self.ensure_one()
|
||||
return self.picking_type_id.code == 'mrp_operation' or super()._should_force_price_unit()
|
||||
|
||||
def _ignore_automatic_valuation(self):
|
||||
return bool(self.raw_material_production_id)
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import models
|
||||
|
||||
|
||||
class StockRule(models.Model):
|
||||
_inherit = 'stock.rule'
|
||||
|
||||
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
|
||||
return res
|
||||
Loading…
Add table
Add a link
Reference in a new issue