Unstable fix
This commit is contained in:
parent
5dcbc05fab
commit
a57f01d456
|
@ -18,6 +18,7 @@ def register():
|
|||
production.Production,
|
||||
production.ProductionDetailedStart,
|
||||
production.ProcessProductionAsyncStart,
|
||||
production.ProductionCost,
|
||||
account.Move,
|
||||
stock.Move,
|
||||
ir.Cron,
|
||||
|
|
11
bom.py
11
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
|
||||
|
|
235
production.py
235
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'
|
||||
|
|
|
@ -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"/>
|
||||
|
||||
<record model="ir.ui.view" id="production_cost_view_form">
|
||||
<field name="model">production.cost</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">production_cost_form</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="production_cost_view_tree">
|
||||
<field name="model">production.cost</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="name">production_cost_tree</field>
|
||||
</record>
|
||||
</data>
|
||||
</tryton>
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
<?xml version="1.0"?>
|
||||
<form>
|
||||
<label name="kind"/>
|
||||
<field name="kind"/>
|
||||
<label name="effective_date"/>
|
||||
<field name="effective_date"/>
|
||||
<label name="product"/>
|
||||
<field name="product"/>
|
||||
<label name="unit"/>
|
||||
<field name="unit" widget="selection"/>
|
||||
<label name="unit_price"/>
|
||||
<field name="unit_price"/>
|
||||
<label name="quantity"/>
|
||||
<field name="quantity"/>
|
||||
<label name="amount"/>
|
||||
<field name="amount"/>
|
||||
<label name="analytic_account" widget="selection"/>
|
||||
<field name="analytic_account" widget="selection"/>
|
||||
<newline />
|
||||
<field name="notes" colspan="4"/>
|
||||
</form>
|
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0"?>
|
||||
<tree editable="1">
|
||||
<field name="kind"/>
|
||||
<field name="effective_date"/>
|
||||
<field name="product"/>
|
||||
<field name="quantity"/>
|
||||
<field name="unit" widget="selection"/>
|
||||
<field name="unit_price"/>
|
||||
<field name="amount"/>
|
||||
<field name="analytic_account" widget="selection" expand="1"/>
|
||||
</tree>
|
|
@ -2,14 +2,6 @@
|
|||
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
this repository contains the full copyright notices and license terms. -->
|
||||
<data>
|
||||
<xpath expr="/form/notebook/page[@id='other']/field[@name='effective_date']" position="replace">
|
||||
</xpath>
|
||||
<xpath expr="/form/notebook/page[@id='other']/label[@name='effective_date']" position="replace">
|
||||
</xpath>
|
||||
<xpath expr="/form/notebook/page[@id='other']/field[@name='effective_start_date']" position="replace">
|
||||
</xpath>
|
||||
<xpath expr="/form/notebook/page[@id='other']/label[@name='effective_start_date']" position="replace">
|
||||
</xpath>
|
||||
<xpath expr="/form/notebook/page[@id='other']/field[@name='cost']" position="after">
|
||||
<newline />
|
||||
<label name="warehouse_origin"/>
|
||||
|
@ -20,30 +12,37 @@ this repository contains the full copyright notices and license terms. -->
|
|||
<field name="in_account_move"/>
|
||||
<label name="out_account_move"/>
|
||||
<field name="out_account_move"/>
|
||||
<label name="material_costs"/>
|
||||
<field name="material_costs"/>
|
||||
<label name="labour_costs"/>
|
||||
<field name="labour_costs"/>
|
||||
<label name="imc_costs"/>
|
||||
<field name="imc_costs"/>
|
||||
<label name="total_cost"/>
|
||||
<field name="total_cost"/>
|
||||
<label name="perfomance"/>
|
||||
<field name="perfomance"/>
|
||||
</xpath>
|
||||
<xpath expr="/form/field[@name='uom']" position="after">
|
||||
<label name="analytic_account"/>
|
||||
<field name="analytic_account"/>
|
||||
</xpath>
|
||||
<xpath expr="/form/field[@name='planned_start_date']" position="after">
|
||||
<label name="effective_date"/>
|
||||
<field name="effective_date"/>
|
||||
<label name="effective_start_date"/>
|
||||
<field name="effective_start_date"/>
|
||||
<label name="analytic_account" widget="selection"/>
|
||||
<field name="analytic_account" widget="selection"/>
|
||||
</xpath>
|
||||
<xpath expr="/form/notebook/page[@id='other']" position="after">
|
||||
<page string="Stock Moves" id="warehouse_stock_moves">
|
||||
<field name="warehouse_moves" colspan="4"/>
|
||||
</page>
|
||||
<page string="Costs" id="production_costs">
|
||||
<label name="analytic_account_materials"/>
|
||||
<field name="analytic_account_materials" widget="selection"/>
|
||||
<label name="analytic_account_labour"/>
|
||||
<field name="analytic_account_labour" widget="selection"/>
|
||||
<label name="analytic_account_indirect"/>
|
||||
<field name="analytic_account_indirect" widget="selection"/>
|
||||
<label name="analytic_account_services"/>
|
||||
<field name="analytic_account_services" widget="selection"/>
|
||||
<field name="costs" colspan="4"/>
|
||||
<group col="8" string="Costs Summary" colspan="4" id="costs_summary">
|
||||
<label name="material_costs"/>
|
||||
<field name="material_costs"/>
|
||||
<label name="labour_costs"/>
|
||||
<field name="labour_costs"/>
|
||||
<label name="indirect_costs"/>
|
||||
<field name="indirect_costs"/>
|
||||
<label name="total_cost"/>
|
||||
<field name="total_cost"/>
|
||||
</group>
|
||||
</page>
|
||||
</xpath>
|
||||
</data>
|
||||
|
|
Loading…
Reference in New Issue