From d850270d0d3ce582e48d7afd23713fc47fccf391 Mon Sep 17 00:00:00 2001 From: Ainara Date: Wed, 5 Nov 2014 11:30:40 +0100 Subject: [PATCH] [ADD] mrp_production_real_cost --- mrp_production_real_cost/__init__.py | 19 +++ mrp_production_real_cost/__openerp__.py | 44 +++++ mrp_production_real_cost/i18n/es.po | 38 +++++ .../i18n/mrp_production_real_costs.pot | 38 +++++ mrp_production_real_cost/models/__init__.py | 22 +++ .../models/mrp_production.py | 47 ++++++ .../models/mrp_production_workcenter_line.py | 92 +++++++++++ .../models/project_task_work.py | 48 ++++++ mrp_production_real_cost/models/stock_move.py | 153 ++++++++++++++++++ .../views/mrp_production_view.xml | 16 ++ 10 files changed, 517 insertions(+) create mode 100644 mrp_production_real_cost/__init__.py create mode 100644 mrp_production_real_cost/__openerp__.py create mode 100644 mrp_production_real_cost/i18n/es.po create mode 100644 mrp_production_real_cost/i18n/mrp_production_real_costs.pot create mode 100644 mrp_production_real_cost/models/__init__.py create mode 100644 mrp_production_real_cost/models/mrp_production.py create mode 100644 mrp_production_real_cost/models/mrp_production_workcenter_line.py create mode 100644 mrp_production_real_cost/models/project_task_work.py create mode 100644 mrp_production_real_cost/models/stock_move.py create mode 100644 mrp_production_real_cost/views/mrp_production_view.xml diff --git a/mrp_production_real_cost/__init__.py b/mrp_production_real_cost/__init__.py new file mode 100644 index 000000000..324fad228 --- /dev/null +++ b/mrp_production_real_cost/__init__.py @@ -0,0 +1,19 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see http://www.gnu.org/licenses/. +# +############################################################################## + +from . import models diff --git a/mrp_production_real_cost/__openerp__.py b/mrp_production_real_cost/__openerp__.py new file mode 100644 index 000000000..c4d8bddcd --- /dev/null +++ b/mrp_production_real_cost/__openerp__.py @@ -0,0 +1,44 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see http://www.gnu.org/licenses/. +# +############################################################################## + +{ + "name": "Mrp Real Costs", + "version": "1.0", + "depends": ["analytic", + "project_timesheet", + "mrp_project_link", + "mrp_operations_time_control", + "stock_account", + "mrp_production_project_estimated_cost"], + "author": "OdooMRP team", + "category": "MRP", + "description": """ + - This module allows to control the real cost of a production order, + creating lines in the analytic account defined in the order that is + created by the module mrp_project_link. + + - Updates product standard price when a production orders final product + is done. + (Product stock * Product standard price + Production real cost) / + (Product stock + Final product quantity) + """, + 'data': ["views/mrp_production_view.xml"], + 'demo': [], + 'installable': True, + 'auto_install': False, +} diff --git a/mrp_production_real_cost/i18n/es.po b/mrp_production_real_cost/i18n/es.po new file mode 100644 index 000000000..3a5b746f5 --- /dev/null +++ b/mrp_production_real_cost/i18n/es.po @@ -0,0 +1,38 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * mrp_production_real_costs +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 8.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2014-11-11 15:06+0000\n" +"PO-Revision-Date: 2014-11-11 16:12+0100\n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: \n" + +#. module: mrp_production_real_costs +#: model:ir.model,name:mrp_production_real_costs.model_project_task_work +msgid "Project Task Work" +msgstr "Trabajo de tarea" + +#. module: mrp_production_real_costs +#: model:ir.model,name:mrp_production_real_costs.model_stock_move +msgid "Stock Move" +msgstr "Movimiento de existencias" + +#. module: mrp_production_real_costs +#: model:ir.model,name:mrp_production_real_costs.model_mrp_production_workcenter_line +msgid "Work Order" +msgstr "Orden de trabajo" + +#. module: mrp_production_real_costs +#: code:addons/mrp_production_real_costs/models/mrp_production_workcenter_line.py:59 +#, python-format +msgid "You must define a general account for this Workcenter: [%s] %s" +msgstr "Es necesario que defina una cuenta contable para este centro de producción: [%s] %s" + diff --git a/mrp_production_real_cost/i18n/mrp_production_real_costs.pot b/mrp_production_real_cost/i18n/mrp_production_real_costs.pot new file mode 100644 index 000000000..69ecdc8cb --- /dev/null +++ b/mrp_production_real_cost/i18n/mrp_production_real_costs.pot @@ -0,0 +1,38 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * mrp_production_real_costs +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 8.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2014-11-11 15:06+0000\n" +"PO-Revision-Date: 2014-11-11 16:09+0100\n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: \n" + +#. module: mrp_production_real_costs +#: model:ir.model,name:mrp_production_real_costs.model_project_task_work +msgid "Project Task Work" +msgstr "" + +#. module: mrp_production_real_costs +#: model:ir.model,name:mrp_production_real_costs.model_stock_move +msgid "Stock Move" +msgstr "" + +#. module: mrp_production_real_costs +#: model:ir.model,name:mrp_production_real_costs.model_mrp_production_workcenter_line +msgid "Work Order" +msgstr "" + +#. module: mrp_production_real_costs +#: code:addons/mrp_production_real_costs/models/mrp_production_workcenter_line.py:59 +#, python-format +msgid "You must define a general account for this Workcenter: [%s] %s" +msgstr "" + diff --git a/mrp_production_real_cost/models/__init__.py b/mrp_production_real_cost/models/__init__.py new file mode 100644 index 000000000..271c541d3 --- /dev/null +++ b/mrp_production_real_cost/models/__init__.py @@ -0,0 +1,22 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see http://www.gnu.org/licenses/. +# +############################################################################## + +from . import stock_move +from . import mrp_production_workcenter_line +from . import project_task_work +from . import mrp_production diff --git a/mrp_production_real_cost/models/mrp_production.py b/mrp_production_real_cost/models/mrp_production.py new file mode 100644 index 000000000..984ac116b --- /dev/null +++ b/mrp_production_real_cost/models/mrp_production.py @@ -0,0 +1,47 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see http://www.gnu.org/licenses/. +# +############################################################################## + +from openerp import models, fields, api + + +class MrpProduction(models.Model): + + _inherit = 'mrp.production' + + @api.one + @api.depends('analytic_line_ids', 'analytic_line_ids.amount', + 'product_qty') + def get_real_cost(self): + self.real_cost = sum([-line.amount for line in + self.analytic_line_ids]) + self.unit_real_cost = self.real_cost / self.product_qty + + real_cost = fields.Float("Total Real Cost", compute="get_real_cost", + store=True) + unit_real_cost = fields.Float("Unit Real Cost", compute="get_real_cost", + store=True) + + @api.multi + def action_production_end(self): + res = super(MrpProduction, self).action_production_end() + analytic_line_obj = self.env['account.analytic.line'] + for record in self: + cost_lines = analytic_line_obj.search([('mrp_production_id', '=', + record.id)]) + record.real_cost = sum([-line.amount for line in cost_lines]) + return res diff --git a/mrp_production_real_cost/models/mrp_production_workcenter_line.py b/mrp_production_real_cost/models/mrp_production_workcenter_line.py new file mode 100644 index 000000000..c58d3f0b9 --- /dev/null +++ b/mrp_production_real_cost/models/mrp_production_workcenter_line.py @@ -0,0 +1,92 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see http://www.gnu.org/licenses/. +# +############################################################################## + +from openerp import models, api +from datetime import datetime + + +class MrpProductionWorkcenterLine(models.Model): + + _inherit = 'mrp.production.workcenter.line' + + @api.multi + def _create_analytic_line(self): + self.ensure_one() + analytic_line_obj = self.env['account.analytic.line'] + property_obj = self.env['ir.property'] + task_obj = self.env['project.task'] + if self.workcenter_id.costs_hour > 0.0: + hour_uom = self.env.ref('product.product_uom_hour', False) + operation_line = self.operation_time_lines[-1] + production = self.production_id + workcenter = self.workcenter_id + product = workcenter.product_id + journal_id = workcenter.costs_journal_id or False + if not journal_id: + journal_id = self.env.ref( + 'mrp_production_project_estimated_cost.analytic_journal_' + 'machines', False) + analytic_account_id = production.analytic_account_id.id or False + task_id = False + if production: + task = task_obj.search([('mrp_production_id', '=', + production.id), + ('wk_order', '=', False)]) + if task: + task_id = task[0].id + name = ((production.name or '') + '-' + + (self.routing_wc_line.operation.code or '') + '-' + + (product.default_code or '')) + general_acc = ( + workcenter.costs_general_account_id.id or + product.property_account_expense.id or + product.categ_id.property_account_expense_categ.id or + property_obj.get('property_account_expense_categ', + 'product.category')) + price = workcenter.costs_hour + analytic_vals = {'name': name, + 'ref': name, + 'date': datetime.now().strftime('%Y-%m-%d'), + 'user_id': self.env.uid, + 'product_id': product.id, + 'product_uom_id': hour_uom.id, + 'amount': -(price * operation_line.uptime), + 'unit_amount': operation_line.uptime, + 'journal_id': journal_id.id, + 'account_id': analytic_account_id, + 'general_account_id': general_acc, + 'task_id': task_id, + 'mrp_production_id': production.id or False, + 'workorder': self.id, + 'estim_avg_cost': 0.0, + 'estim_std_cost': 0.0 + } + analytic_line = analytic_line_obj.create(analytic_vals) + return analytic_line + + @api.multi + def action_pause(self): + result = super(MrpProductionWorkcenterLine, self).action_pause() + self._create_analytic_line() + return result + + @api.multi + def action_done(self): + result = super(MrpProductionWorkcenterLine, self).action_done() + self._create_analytic_line() + return result diff --git a/mrp_production_real_cost/models/project_task_work.py b/mrp_production_real_cost/models/project_task_work.py new file mode 100644 index 000000000..b951b2a00 --- /dev/null +++ b/mrp_production_real_cost/models/project_task_work.py @@ -0,0 +1,48 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see http://www.gnu.org/licenses/. +# +############################################################################## + +from openerp import models, api + + +class ProjectTaskWork(models.Model): + + _inherit = 'project.task.work' + + @api.model + def create(self, vals): + result = super(ProjectTaskWork, self).create(vals) + if result.hr_analytic_timesheet_id and result.task_id: + analytic_line = result.hr_analytic_timesheet_id.line_id + task = result.task_id + if task.mrp_production_id or task.wk_order: + analytic_line.write({'mrp_production_id': + task.mrp_production_id.id, + 'workorder': task.wk_order.id}) + return result + + @api.model + def write(self, vals): + result = super(ProjectTaskWork, self).write(vals) + if 'hr_analytic_timesheet_id' in vals or 'task_id' in vals: + analytic_line = self.hr_analytic_timesheet_id.line_id + task = self.task_id + if task.mrp_production_id or task.wk_order: + analytic_line.write({'mrp_production_id': + task.mrp_production_id.id, + 'workorder': task.wk_order.id}) + return result diff --git a/mrp_production_real_cost/models/stock_move.py b/mrp_production_real_cost/models/stock_move.py new file mode 100644 index 000000000..e91979c78 --- /dev/null +++ b/mrp_production_real_cost/models/stock_move.py @@ -0,0 +1,153 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see http://www.gnu.org/licenses/. +# +############################################################################## + +from openerp import models, api +from datetime import datetime +import math + + +class StockMove(models.Model): + + _inherit = 'stock.move' + + @api.multi + def action_consume(self, product_qty, location_id=False, + restrict_lot_id=False, restrict_partner_id=False, + consumed_for=False): + task_obj = self.env['project.task'] + property_obj = self.env['ir.property'] + analytic_line_obj = self.env['account.analytic.line'] + result = super(StockMove, self).action_consume( + product_qty, location_id=location_id, + restrict_lot_id=restrict_lot_id, + restrict_partner_id=restrict_partner_id, consumed_for=consumed_for) + for record in self: + price = record.product_id.standard_price + if price > 0.0: + if record.production_id or record.raw_material_production_id: + product = record.product_id + journal_id = self.env.ref( + 'mrp_production_project_estimated_cost.analytic_' + 'journal_materials', False) + production_id = False + analytic_account_id = False + task_id = False + if record.production_id: + production = record.production_id + elif record.raw_material_production_id: + production = record.raw_material_production_id + if production: + production_id = production.id + analytic_account_id = production.analytic_account_id.id + task = task_obj.search( + [('mrp_production_id', '=', production_id), + ('wk_order', '=', False)]) + if task: + task_id = task[0].id + name = ( + (production.name or '') + '-' + + (record.work_order.routing_wc_line.operation.code or + '') + '-' + (product.default_code or '')) + general_account = ( + product.property_account_expense.id or + product.categ_id.property_account_expense_categ.id or + property_obj.get('property_account_expense_categ', + 'product.category')) + date = datetime.now().strftime('%Y-%m-%d') + uom_id = record.product_id.uom_id.id + if record.raw_material_production_id: + analytic_vals = {'name': name, + 'ref': name, + 'date': date, + 'user_id': self.env.uid, + 'product_id': product.id, + 'product_uom_id': uom_id, + 'amount': -(price * product_qty), + 'unit_amount': product_qty, + 'journal_id': journal_id.id, + 'account_id': analytic_account_id, + 'general_account_id': general_account, + 'task_id': task_id, + 'mrp_production_id': production_id, + 'workorder': record.work_order.id, + 'estim_avg_cost': 0.0, + 'estim_std_cost': 0.0 + } + analytic_line_obj.create(analytic_vals) + elif record.production_id: + amount = 0.0 + unit_amount = 0.0 + for wc in production.workcenter_lines: + cycle_cost = wc.workcenter_id.costs_cycle + cycle_units = wc.workcenter_id.capacity_per_cycle + cycle = int(math.ceil(product_qty / cycle_units)) + amount += cycle * cycle_cost + unit_amount += cycle + analytic_vals = {'name': name, + 'ref': name, + 'date': date, + 'user_id': self.env.uid, + 'product_id': product.id, + 'product_uom_id': uom_id, + 'amount': amount, + 'unit_amount': unit_amount, + 'journal_id': journal_id.id, + 'account_id': analytic_account_id, + 'general_account_id': general_account, + 'task_id': task_id, + 'mrp_production_id': production_id, + 'workorder': record.work_order.id, + 'estim_avg_cost': 0.0, + 'estim_std_cost': 0.0 + } + analytic_line_obj.create(analytic_vals) + return result + + @api.multi + def product_price_update_before_done(self): + analytic_line_obj = self.env['account.analytic.line'] + super(StockMove, self).product_price_update_before_done() + for move in self: + # adapt standard price on production final moves if the + # product cost_method is 'average' + if (move.production_id) and (move.product_id.cost_method == + 'average'): + analytic_lines = analytic_line_obj.search( + [('mrp_production_id', '=', move.production_id.id)]) + prod_total_cost = sum([-line.amount for line in + analytic_lines]) + product = move.product_id + product_avail = product.qty_available + amount_unit = product.standard_price + new_std_price = ((amount_unit * product_avail + + prod_total_cost) / (product_avail >= 0.0 or + 0.0 + move.product_qty)) + # Write the standard price, as SUPERUSER_ID because a warehouse + # manager may not have the right to write on products + product.sudo().write({'standard_price': new_std_price}) + + @api.multi + def get_unit_price(self): + self.ensure_one() + if self.production_id: + analytic_line_obj = self.env['account.analytic.line'] + analytic_lines = analytic_line_obj.search( + [('mrp_production_id', '=', self.production_id.id)]) + return sum([-line.amount for line in analytic_lines]) + else: + return super(StockMove, self).get_unit_price() diff --git a/mrp_production_real_cost/views/mrp_production_view.xml b/mrp_production_real_cost/views/mrp_production_view.xml new file mode 100644 index 000000000..bbeb27a2d --- /dev/null +++ b/mrp_production_real_cost/views/mrp_production_view.xml @@ -0,0 +1,16 @@ + + + + + mrp.production.project.form.view.inh.estimatedcost + mrp.production + + + + + + + + + +