Unstable fix

This commit is contained in:
oscar alvarez 2023-02-16 18:42:46 -05:00
parent 5dcbc05fab
commit a57f01d456
7 changed files with 235 additions and 101 deletions

View File

@ -18,6 +18,7 @@ def register():
production.Production,
production.ProductionDetailedStart,
production.ProcessProductionAsyncStart,
production.ProductionCost,
account.Move,
stock.Move,
ir.Cron,

11
bom.py
View File

@ -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

View File

@ -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'

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>