diff --git a/__init__.py b/__init__.py
index 29648d0..7b9e273 100644
--- a/__init__.py
+++ b/__init__.py
@@ -18,6 +18,7 @@ def register():
production.Production,
production.ProductionDetailedStart,
production.ProcessProductionAsyncStart,
+ production.ProductionCost,
account.Move,
stock.Move,
ir.Cron,
diff --git a/bom.py b/bom.py
index 34db442..b76dfca 100644
--- a/bom.py
+++ b/bom.py
@@ -5,14 +5,13 @@ from datetime import datetime, date
from trytond.pool import Pool, PoolMeta
from trytond.modules.product import round_price
-from trytond.transaction import Transaction
from trytond.model import fields, ModelSQL, ModelView
class BOM(metaclass=PoolMeta):
__name__ = 'production.bom'
- direct_costs = fields.One2Many('production.bom.direct_cost',
- 'bom', 'Direct Costs')
+ direct_costs = fields.One2Many('production.bom.direct_cost', 'bom',
+ 'Direct Costs')
@classmethod
def calc_cost_ldm(cls):
@@ -65,12 +64,12 @@ class BOMDirectCost(ModelSQL, ModelView):
product = fields.Many2One('product.product', 'Product', required=True,
domain=[('type', '!=', 'active')])
uom = fields.Many2One('product.uom', 'UoM')
- quantity = fields.Float('Quantity', required=True, digits=(16,2))
+ quantity = fields.Float('Quantity', required=True, digits=(16, 2))
notes = fields.Char('Notes')
kind = fields.Selection([
('labour', 'Labour'),
- ('imc', 'IMC'),
- ('', ''),
+ ('indirect', 'Indirect'),
+ ('service', 'Service'),
], 'Kind')
@staticmethod
diff --git a/production.py b/production.py
index 1c21666..7b4635f 100644
--- a/production.py
+++ b/production.py
@@ -3,15 +3,15 @@
from decimal import Decimal
from trytond.pool import PoolMeta, Pool
-from trytond.model import fields, ModelView
+from trytond.model import fields, ModelView, ModelSQL
from trytond.pyson import Eval
from trytond.transaction import Transaction
-from trytond.wizard import Wizard, StateTransition, StateReport, StateView, Button
+from trytond.wizard import (
+ Wizard, StateTransition, StateReport, StateView, Button)
from trytond.modules.company import CompanyReport
from trytond.i18n import gettext
from trytond.exceptions import UserError
from trytond.report import Report
-from trytond.modules.product import price_digits, round_price
ZERO = Decimal(0)
@@ -24,6 +24,14 @@ def round_dec(number):
class Production(metaclass=PoolMeta):
__name__ = 'production'
+ _analytic_dom = [
+ ('type', 'in', ['normal', 'distribution']),
+ ('company', '=', Eval('context', {}).get('company', -1)),
+ ('parent', '=', Eval('analytic_account')),
+ ]
+ _states = {
+ 'readonly': ~Eval('state').in_(['draft', 'request']),
+ }
in_account_move = fields.Many2One('account.move', 'In Account Move',
states={'readonly': True})
out_account_move = fields.Many2One('account.move', 'Out Account Move',
@@ -33,19 +41,32 @@ class Production(metaclass=PoolMeta):
warehouse_target = fields.Many2One('stock.location', 'Warehouse Target',
domain=[('type', '=', 'warehouse')])
warehouse_moves = fields.One2Many('stock.move', 'origin', 'Warehouse Moves')
+ costs = fields.One2Many('production.cost', 'production', 'Costs')
material_costs = fields.Numeric('Material Costs', digits=(16, 2),
- readonly=True)
- labour_costs = fields.Numeric('Labour Costs', digits=(16, 2), readonly=True)
- imc_costs = fields.Numeric('IMC Costs', digits=(16, 2), readonly=True)
- total_cost = fields.Numeric('Total Cost', digits=(16, 2), readonly=True)
- perfomance = fields.Function(fields.Numeric('Perfomance', digits=(16, 2)), 'get_perfomance')
+ states={'readonly': True})
+ labour_costs = fields.Numeric('Labour Costs', digits=(16, 2),
+ states={'readonly': True})
+ indirect_costs = fields.Numeric('Indirect Costs', digits=(16, 2),
+ states={'readonly': True})
+ services_costs = fields.Numeric('Services Costs', digits=(16, 2),
+ states={'readonly': True})
+ total_cost = fields.Function(fields.Numeric('Total Cost', digits=(16, 2)),
+ 'get_total_cost')
+ perfomance = fields.Function(fields.Numeric('Perfomance', digits=(16, 2)),
+ 'get_perfomance')
analytic_account = fields.Many2One('analytic_account.account',
- 'Analytic Account', domain=[
- ('type', 'in', ['normal', 'distribution']),
+ 'Analytic Account Main', domain=[
+ ('type', '=', 'view'),
('company', '=', Eval('context', {}).get('company', -1))
- ], states={
- 'readonly': ~Eval('state').in_(['draft', 'request']),
- })
+ ], states=_states)
+ analytic_account_materials = fields.Many2One('analytic_account.account',
+ 'Analytic Account Materials', domain=_analytic_dom)
+ analytic_account_labour = fields.Many2One('analytic_account.account',
+ 'Analytic Account Labour', domain=_analytic_dom)
+ analytic_account_indirect = fields.Many2One('analytic_account.account',
+ 'Analytic Account Indirect', domain=_analytic_dom)
+ analytic_account_services = fields.Many2One('analytic_account.account',
+ 'Analytic Account Services', domain=_analytic_dom)
@staticmethod
def default_warehouse_origin():
@@ -64,17 +85,18 @@ class Production(metaclass=PoolMeta):
@classmethod
def wait(cls, records):
super(Production, cls).wait(records)
- # for rec in records:
- # cls.create_account_move(rec, 'wait', 'in_account_move')
+ for rec in records:
+ # cls.create_account_move(rec, 'wait', 'in_account_move')
# FIXME
# rec.create_stock_move('in')
+ pass
@classmethod
def done(cls, records):
Move = Pool().get('stock.move')
super(Production, cls).done(records)
for rec in records:
- # cls.create_account_move(rec, 'done', 'out_account_move')
+ cls.create_account_move(rec, 'done', 'out_account_move')
for output in rec.outputs:
unit_price = rec._compute_unit_cost()
Move.write([output], {'unit_price': unit_price})
@@ -90,10 +112,18 @@ class Production(metaclass=PoolMeta):
new_cost_price = round_dec(total_cost / Decimal(self.quantity))
return new_cost_price
+ def get_total_cost(self, name=None):
+ return sum([
+ self.material_costs or 0,
+ self.indirect_costs or 0,
+ self.services_costs or 0,
+ self.labour_costs or 0,
+ ])
+
def get_perfomance(self, name=None):
res = Decimal(0)
if self.bom:
- origin = sum(p.quantity for p in self.bom.outputs ) * self.quantity
+ origin = sum(p.quantity for p in self.bom.outputs) * self.quantity
result = sum(p.quantity for p in self.outputs)
if result != 0 and origin != 0:
res = Decimal(str(round(result*100/origin, 2)))
@@ -108,56 +138,51 @@ class Production(metaclass=PoolMeta):
ProductCost.write(products, {'cost_price': new_cost})
@classmethod
- def get_production_lines(cls, bom, materials_cost, date_, factor, args=None):
+ def get_production_lines(cls, rec, date_, factor, args=None):
""" Get Production Account Move Lines """
- lines = []
+
cost_finished_production = []
values = {}
- output = bom.outputs[0]
+ output = rec.bom.outputs[0]
+
account_expense = output.product.account_expense_used
+ analytic_materials = args.get('analytic_materials', None)
materials_line = {
'description': '',
'account': account_expense.id,
'debit': 0,
- 'credit': materials_cost,
+ 'credit': rec.material_costs,
}
- analytic = args.get('analytic', None)
- cls.set_analytic_lines(materials_line, date_, analytic)
+ cls.set_analytic_lines(materials_line, date_, analytic_materials)
lines.append(materials_line)
- cost_finished_production.append(materials_cost)
+ cost_finished_production.append(rec.materials_cost)
- values = {
- 'total_labour': [],
- 'total_imc': [],
- }
-
- for dc in bom.direct_costs:
- account_id = dc.product.account_expense_used.id
- amount = Decimal(dc.quantity * factor) * dc.product.cost_price
- amount = Decimal(round(amount, 2))
- line_ = {
- 'description': '',
- 'account': account_id,
- 'debit': 0,
- 'credit': amount,
- }
-
- cls.set_analytic_lines(line_, date_, analytic)
+ account_id = None
+ lines = []
+ for cost in rec.costs:
+ amount = rec.amount
+ if cost.kind == 'labour':
+ account_id = cost.product.account_expense_used.id
+ values['total_labour'].append(rec.labour_costs)
+ line_ = {
+ 'description': '',
+ 'account': account_id,
+ 'debit': 0,
+ 'credit': rec.amount,
+ }
+ cls.set_analytic_lines(line_, date_, analytic_work_force)
+ lines.append(line_)
lines.append(line_)
cost_finished_production.append(amount)
- if dc.kind == 'labour':
- values['total_labour'].append(amount)
- elif dc.kind == 'imc':
- values['total_imc'].append(amount)
-
- account_stock = output.product.account_stock_used
- lines.append({
- 'description': '',
- 'account': account_stock.id,
- 'debit': sum(cost_finished_production),
- 'credit': 0,
- })
- return lines, values
+ #
+ # account_stock = output.product.account_stock_used
+ # lines.append({
+ # 'description': '',
+ # 'account': account_stock.id,
+ # 'debit': sum(cost_finished_production),
+ # 'credit': 0,
+ # })
+ # return lines, values
@classmethod
def get_consumption_lines(cls, inputs, factor, date_, args=None):
@@ -227,32 +252,39 @@ class Production(metaclass=PoolMeta):
raise UserError(
gettext('production_accounting.msg_planned_date_required')
)
- analytic_ctx = {'analytic': rec.analytic_account}
+ analytic_ctx = {
+ 'analytic_materials': rec.analytic_account_materials,
+ 'analytic_labour': rec.analytic_account_labour,
+ 'analytic_indirect': rec.analytic_account_indirect,
+ 'analytic_services': rec.analytic_account_services,
+ }
to_update = {}
output = rec.outputs[0]
factor = rec.quantity / output.quantity
- if kind == 'wait':
- date_ = rec.planned_date
- lines, values = cls.get_consumption_lines(
- rec.inputs, factor, date_, analytic_ctx
- )
- to_update['material_costs'] = values['material_costs']
+ # if kind == 'wait':
+ # date_ = rec.planned_date
+ # lines, values = cls.get_consumption_lines(
+ # rec.inputs, factor, date_, analytic_ctx
+ # )
+ # to_update['material_costs'] = values['material_costs']
if kind == 'done':
date_ = rec.effective_date
lines, values = cls.get_production_lines(
- rec.bom,
- rec.material_costs,
+ rec,
date_,
factor,
analytic_ctx
)
- total_labour = sum(values['total_labour'])
- total_imc = sum(values['total_imc'])
- to_update['material_costs'] = round_dec(rec.cost)
- to_update['labour_costs'] = round_dec(total_labour)
- to_update['imc_costs'] = round_dec(total_imc)
- to_update['total_cost'] = round_dec(total_imc + total_labour + rec.cost)
+ # total_labour = sum(values['total_labour'])
+ # total_indirect = sum(values['total_indirect'])
+ # total_services = sum(values['total_services'])
+ # to_update['material_costs'] = round_dec(rec.cost)
+ # to_update['labour_costs'] = round_dec(total_labour)
+ # to_update['indirect_costs'] = round_dec(total_indirect)
+ # to_update['services_costs'] = round_dec(total_services)
+ # to_update['total_cost'] = round_dec(
+ # total_indirect + total_labour + rec.cost)
period_id = Period.find(rec.company.id, date=date_)
@@ -267,8 +299,8 @@ class Production(metaclass=PoolMeta):
}])
Move.post([move])
- to_update[field] = move.id
- cls.write([rec], to_update)
+ # to_update[field] = move.id
+ # cls.write([rec], to_update)
def create_stock_move(self, kind, field=None):
pool = Pool()
@@ -321,6 +353,67 @@ class Production(metaclass=PoolMeta):
lines.append(analytic_line)
line['analytic_lines'] = [('create', lines)]
+ @fields.depends('inputs', 'material_costs')
+ def on_change_inputs(self, name=None):
+ res = []
+ for _input in self.inputs:
+ res.append(Decimal(_input.quantity) * _input.product.cost_price)
+ self.material_costs = sum(res)
+
+ @fields.depends('costs', 'indirect_costs', 'services_costs', 'labour_costs')
+ def on_change_costs(self, name=None):
+ indirect_costs = []
+ services_costs = []
+ labour_costs = []
+ for cost in self.costs:
+ if cost.kind == 'indirect':
+ indirect_costs.append(cost.amount)
+ elif cost.kind == 'labour':
+ labour_costs.append(cost.amount)
+ elif cost.kind == 'service':
+ services_costs.append(cost.amount)
+
+ self.indirect_costs = sum(indirect_costs)
+ self.services_costs = sum(services_costs)
+ self.labour_costs = sum(labour_costs)
+
+
+class ProductionCost(ModelSQL, ModelView):
+ "Production Cost"
+ __name__ = "production.cost"
+ production = fields.Many2One('production', 'Production', required=True,
+ ondelete='CASCADE')
+ product = fields.Many2One('product.product', 'Product', required=True,
+ domain=[('type', '!=', 'active')])
+ unit = fields.Many2One('product.uom', 'UoM')
+ effective_date = fields.Date('Effective Date')
+ quantity = fields.Float('Quantity', required=True, digits=(16, 2))
+ unit_price = fields.Numeric('Unit Price', required=True, digits=(16, 2))
+ amount = fields.Numeric('Amount', digits=(16, 2), states={'readonly': True})
+ analytic_account = fields.Many2One('analytic_account.account',
+ 'Analytic Account', domain=[
+ ('type', 'in', ['normal', 'distribution']),
+ ('company', '=', Eval('context', {}).get('company', -1)),
+ ('parent', '=', Eval('_parent_production', {}).get('analytic_account')),
+ ])
+ kind = fields.Selection([
+ ('labour', 'Labour'),
+ ('indirect', 'Indirect'),
+ ('service', 'Service'),
+ ], 'Kind', required=True)
+ notes = fields.Text('Notes')
+
+ @fields.depends('product', 'unit', 'unit_price')
+ def on_change_product(self, name=None):
+ if self.product:
+ self.unit = self.product.default_uom.id
+ self.unit_price = self.product.cost_price
+
+ @fields.depends('unit_price', 'amount', 'quantity')
+ def on_change_with_amount(self, name=None):
+ if self.unit_price and self.quantity:
+ return round(self.unit_price * Decimal(self.quantity), 2)
+
class ProductionReport(CompanyReport):
'Production Report'
diff --git a/production.xml b/production.xml
index efea57d..796ae82 100644
--- a/production.xml
+++ b/production.xml
@@ -72,5 +72,15 @@ this repository contains the full copyright notices and license terms. -->
sequence="2" id="menu_process_production_async"
action="wizard_process_production_async"/>
+