trytonpsk-production_accoun.../production.py

993 lines
35 KiB
Python

# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from decimal import Decimal
from datetime import date
from sql import Table
from trytond.pool import PoolMeta, Pool
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.modules.company import CompanyReport
from trytond.i18n import gettext
from trytond.exceptions import UserError
from trytond.report import Report
ZERO = Decimal(0)
def round_dec(number):
if not isinstance(number, Decimal):
number = Decimal(number)
return Decimal(number.quantize(Decimal('.01')))
class SubProduction(ModelSQL, ModelView):
"Sub Production"
__name__ = "production.sub"
production = fields.Many2One('production', 'Production', required=True,
ondelete='CASCADE')
product = fields.Many2One('product.product', 'Product', required=True)
effective_date = fields.Date('Effect. Date', required=True)
quantity = fields.Float('Quantity', required=True)
inputs = fields.One2Many('production.sub.in', 'sub', 'Sub. Inputs')
employee = fields.Many2One('company.employee', 'Employee')
state = fields.Selection([
('draft', 'Draft'),
('in_progress', 'In progress'),
('finished', 'Finished'),
('cancelled', 'Canceled'),
], 'State')
notes = fields.Char('Notes')
@staticmethod
def default_effective_date():
return date.today()
class SubProductionIn(ModelSQL, ModelView):
"Sub Production In"
__name__ = "production.sub.in"
sub = fields.Many2One('production.sub', 'Production Sub', required=True,
ondelete='CASCADE')
product = fields.Many2One('product.product', 'product', required=True)
move = fields.Many2One('stock.move', 'Move')
quantity = fields.Float('Quantity', required=True)
notes = fields.Char('Notes')
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',
states={'readonly': True})
warehouse_origin = fields.Many2One('stock.location', 'Warehouse Origin',
domain=[('type', '=', 'warehouse')])
warehouse_target = fields.Many2One('stock.location', 'Warehouse Target',
domain=[('type', '=', 'warehouse')])
warehouse_moves = fields.One2Many('stock.move', 'origin', 'Warehouse Moves')
subs = fields.One2Many('production.sub', 'production', 'Sub Productions')
costs = fields.One2Many('production.cost', 'production', 'Costs')
material_costs = fields.Numeric('Material Costs', digits=(16, 2),
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 Main', domain=[
('type', '=', 'view'),
('company', '=', Eval('context', {}).get('company', -1))
], 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)
subproductions = fields.One2Many('production.sub', 'production',
'Sub-Productions')
account_production = fields.Many2One('account.account',
'Account Production', domain=[
('closed', '!=', True),
('type.stock', '=', True),
('company', '=', Eval('context', {}).get('company', -1)),
])
@classmethod
def __setup__(cls):
super(Production, cls).__setup__()
cls._buttons.update({
'generate_sub': {
'invisible': Eval('state').in_(['done']),
},
'post_production': {
'invisible': Eval('state').in_(['done']),
}
})
@classmethod
@ModelView.button
def generate_sub(cls, records):
pool = Pool()
BOMOutput = pool.get('production.bom.output')
Sub = pool.get('production.sub')
to_create = []
for pcc in records:
for input in pcc.inputs:
outputs = BOMOutput.search([
('product', '=', input.product.id),
('bom.is_subproduction', '=', True),
])
if not outputs:
continue
output = outputs[0]
to_create = {
'production': pcc.id,
'product': input.product.id,
'quantity': input.quantity,
'state': 'draft',
}
_inputs_to_create = []
for _input in output.bom.inputs:
_inputs_to_create.append({
'product': _input.product.id,
'quantity': _input.quantity * input.quantity,
})
to_create['inputs'] = [('create', _inputs_to_create)]
if to_create:
Sub.create([to_create])
@classmethod
@ModelView.button
def post_production(cls, records):
for rec in records:
cls.create_account_move(rec, 'partial')
@staticmethod
def default_warehouse_origin():
config = Pool().get('production.configuration')(1)
if config.warehouse_origin:
return config.warehouse_origin.id
return
@staticmethod
def default_warehouse_target():
config = Pool().get('production.configuration')(1)
if config.warehouse_origin:
return config.warehouse_target.id
return
@classmethod
def wait(cls, records):
super(Production, cls).wait(records)
for rec in records:
# cls.create_account_move(rec, 'wait', 'in_account_move')
# FIXME
# rec.create_stock_move('in')
pass
@classmethod
def run(cls, records):
super(Production, cls).run(records)
for rec in records:
rec.create_stock_move_sub()
@classmethod
def done(cls, records):
pool = Pool()
Move = pool.get('stock.move')
super(Production, cls).done(records)
Config = pool.get('production.configuration')
config = Config.get_configuration()
if config.production_accounting:
for rec in records:
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})
rec.update_product_cost(output.product)
#rec.create_stock_move('out')
def _compute_unit_cost(self):
total_cost = self.total_cost
if not total_cost and self.cost:
total_cost = self.cost
# ouput = self.outputs[0]
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
result = sum(p.quantity for p in self.outputs)
if result != 0 and origin != 0:
res = Decimal(str(round(result*100/origin, 2)))
return res
def update_product_cost(self, product):
ProductCost = Pool().get('product.cost_price')
products = ProductCost.search([
('product', '=', product.id),
])
new_cost = self._compute_unit_cost()
ProductCost.write(products, {'cost_price': new_cost})
@classmethod
def get_production_lines(cls, rec, date_, factor):
""" Get Production Account Move Lines """
cost_production = []
output = rec.bom.outputs[0]
lines = []
company = Transaction().context.get('company')
for input in rec.inputs:
account_id = input.product.account_expense_used.id
amount = round(input.product.cost_price * Decimal(input.quantity), 2)
line = {
'description': input.product.rec_name,
'account': account_id,
'debit': 0,
'credit': amount,
'party': company,
}
cost_production.append(amount or 0)
cls.set_analytic_lines(line, date_, rec.analytic_account_materials)
lines.append(line)
account_id = None
for cost in rec.costs:
if cost.account_move:
continue
account_id = cost.product.account_expense_used.id
line_ = {
'description': cost.product.rec_name,
'account': account_id,
'debit': 0,
'credit': cost.amount,
'party': company,
}
cls.set_analytic_lines(line_, date_, cost.analytic_account)
lines.append(line_)
cost_production.append(cost.amount or 0)
output_account_category = output.product.account_category.account_stock_in
stock_in = {
'description': output.product.rec_name,
'account': output_account_category.id if output_account_category else None,
'debit': sum(cost_production),
'credit': 0,
'party': company,
}
lines.append(stock_in)
return lines
@classmethod
def get_production_lines_partial(cls, rec, date_, factor, kind=None):
""" Get Production Account Move Lines Partial """
cost_production = []
output = rec.bom.outputs[0]
lines = []
company = Transaction().context.get('company')
account_id = None
for cost in rec.costs:
if cost.account_move:
continue
account_id = cost.product.account_expense_used.id
if not cost.amount:
continue
line_ = {
'description': cost.product.rec_name,
'account': account_id,
'debit': 0,
'credit': cost.amount or 0,
# 'party': company,
}
cls.set_analytic_lines(line_, date_, cost.analytic_account)
lines.append(line_)
cost_production.append(cost.amount or 0)
account_partial_stock = rec.account_production.id
stock_in = {
'description': output.product.rec_name,
'account': account_partial_stock,
'debit': sum(cost_production),
'credit': 0,
# 'party': company,
}
lines.append(stock_in)
print('lines....', lines)
return lines
@classmethod
def get_consumption_lines(cls, inputs, factor, date_, args=None):
""" Get Consumption Account Move Lines """
lines = []
costs_lines = {}
for _in in inputs:
product = _in.product
if product.cost_price == 0:
continue
category = product.template.account_category
if not category or not category.account_stock:
raise UserError(gettext(
'production_accounting.msg_category_account_stock',
product=product.rec_name
))
account_stock_id = category.account_stock.id
credit = round_dec(product.cost_price * Decimal(_in.quantity) * Decimal(factor))
credit = round(credit, 0)
_line = {
'description': product.template.name,
'account': account_stock_id,
'debit': 0,
'credit': credit
}
lines.append(_line)
account_expense_id = category.account_expense.id
try:
costs_lines[account_expense_id].append(credit)
except Exception as e:
costs_lines[account_expense_id] = [credit]
material_costs = []
for account_id, ldebit in costs_lines.items():
debit = sum(ldebit)
material_costs.append(debit)
line_ = {
'description': '',
'account': account_id,
'debit': debit,
'credit': 0,
}
analytic = args.get('analytic', None)
cls.set_analytic_lines(line_, date_, analytic)
lines.append(line_)
values = {'material_costs': round_dec(sum(material_costs))}
return lines, values
@classmethod
def create_account_move(cls, rec, kind, field=None):
pool = Pool()
Move = pool.get('account.move')
Journal = pool.get('account.journal')
Period = pool.get('account.period')
journals = Journal.search([
('code', '=', 'STO')
])
if journals:
journal = journals[0]
if not rec.planned_date:
raise UserError(
gettext('production_accounting.msg_planned_date_required')
)
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 == 'done':
date_ = rec.effective_date
lines = cls.get_production_lines(rec, date_, factor)
if kind == 'partial':
date_ = date.today()
lines = cls.get_production_lines_partial(rec, date_, factor, kind)
period_id = Period.find(rec.company.id, date=date_)
move, = Move.create([{
'journal': journal.id,
'period': period_id,
'date': date_,
'state': 'draft',
'lines': [('create', lines)],
'origin': str(rec),
'description': rec.number,
}])
Move.post([move])
cls.write([rec], {'out_account_move': move.id})
for cost in rec.costs:
if cost.account_move:
continue
cost.account_move = move.id
cost.save()
def create_stock_move(self, kind, field=None):
pool = Pool()
Move = pool.get('stock.move')
to_create = []
warehouse_pdc = self.warehouse.storage_location.id
if kind == 'in':
records = self.inputs
from_warehouse = self.warehouse_origin
to_warehouse = self.warehouse
date_ = self.planned_date
elif kind == 'out':
records = self.outputs
from_warehouse = self.warehouse
to_warehouse = self.warehouse_target
date_ = self.effective_date
from_location_id = from_warehouse.storage_location.id
to_location_id = to_warehouse.storage_location.id
for rec in records:
to_create.append({
'product': rec.product,
'quantity': rec.quantity,
'from_location': from_location_id,
'to_location': to_location_id,
'origin': str(self),
'effective_date': date_,
'unit_price': rec.unit_price,
'uom': rec.uom.id,
'state': 'draft',
})
moves = Move.create(to_create)
Move.do(moves)
def create_stock_move_sub(self):
pool = Pool()
Move = pool.get('stock.move')
# to_create = []
moves = []
records = self.subs
from_warehouse = self.warehouse
to_warehouse = self.location
from_location_id = from_warehouse.storage_location.id
to_location_id = to_warehouse.id
for sub in records:
date_ = sub.effective_date
for input in sub.inputs:
if not input.move:
move = Move(
product=input.product,
quantity=float(str(round(input.quantity))),
from_location=from_location_id,
to_location=to_location_id,
origin=str(self),
effective_date=date_,
uom=input.product.default_uom.id,
state='draft',
)
# to_create.append(move)
Move.save([move])
moves.append(move)
input.move = move.id
input.save()
# moves = Move.save(to_create)
Move.do(moves) if len(moves) > 0 else None
@classmethod
def set_analytic_lines(cls, line, date, analytic_account):
"Yield analytic lines for the accounting line and the date"
if not analytic_account:
return
lines = []
amount = line['debit'] or line['credit']
for account, amount in analytic_account.distribute(amount):
analytic_line = {}
analytic_line['debit'] = amount if line['debit'] else Decimal(0)
analytic_line['credit'] = amount if line['credit'] else Decimal(0)
analytic_line['account'] = account
analytic_line['date'] = date
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:
quantity = _input.quantity if _input.quantity else 0
cost_price = _input.product.cost_price if _input.product else 0
res.append(Decimal(quantity) * 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 or 0)
elif cost.kind == 'labour':
labour_costs.append(cost.amount or 0)
elif cost.kind == 'service':
services_costs.append(cost.amount or 0)
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')
account_move = fields.Many2One('account.move', 'Account Move',
states={'readonly': True})
@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'
__name__ = 'production.report'
class DoneProductions(Wizard):
'Done Productions'
__name__ = 'production.done_productions'
start_state = 'done_productions'
done_productions = StateTransition()
@classmethod
def __setup__(cls):
super(DoneProductions, cls).__setup__()
def transition_done_productions(self):
Production = Pool().get('production')
ids = Transaction().context['active_ids']
if not ids:
return 'end'
productions = Production.browse(ids)
for prd in productions:
if prd.state != 'running':
continue
Production.done([prd])
return 'end'
class ProcessProductionAsyncStart(ModelView):
'Process Production Async Start'
__name__ = 'production.process_production_async.start'
company = fields.Many2One('company.company', 'Company', required=True)
shop = fields.Many2One('sale.shop', 'Shop', required=True)
date = fields.Date('Start Date', required=True)
@staticmethod
def default_company():
return Transaction().context.get('company')
class ProcessProductionAsync(Wizard):
'Process Production Async'
__name__ = 'production.process_production_async'
start = StateView(
'production.process_production_async.start',
'production_accounting.process_production_async_start_view_form', [
Button('Cancel', 'end', 'tryton-cancel'),
Button('Ok', 'accept', 'tryton-ok', default=True),
])
accept = StateTransition()
def create_move(self, lines, journal, period_id):
Move = Pool().get('account.move')
move, = Move.create([{
'journal': journal.id,
'period': period_id,
'date': self.start.date,
'state': 'draft',
'lines': [('create', lines)],
'description': '',
}])
Move.post([move])
print('Asiento creado No ', move.number)
def update_product_cost(self, producibles):
ProductAvgCost = Pool().get('product.average_cost')
Date = Pool().get('ir.date')
for product, value in producibles.items():
cost_price = value['bom'].compute_unit_cost()
values = {
'product': product.id,
'effective_date': Date.today(),
'cost_price': cost_price,
}
print('Actualizando update product cost.....', values)
ProductAvgCost.create([values])
def create_stock_moves(self, producibles):
Move = Pool().get('stock.move')
date_ = self.start.date
in_moves = []
out_moves = []
from_location_id = self.start.shop.warehouse.storage_location.id
to_location_id = self.start.shop.warehouse.production_location.id
for product, values in producibles.items():
output = values['bom'].outputs[0]
quantity = sum(values['quantity'])
factor = quantity / output.quantity
for input in values['bom'].inputs:
_product = input.product
qty = round(
input.quantity * factor,
_product.template.default_uom.digits
)
in_moves.append({
'product': _product.id,
'uom': _product.template.default_uom.id,
'effective_date': date_,
'quantity': qty,
'from_location': from_location_id,
'to_location': to_location_id,
})
out_moves.append({
'product': product.id,
'uom': product.template.default_uom.id,
'effective_date': date_,
'quantity': quantity,
'unit_price': product.cost_price,
'from_location': to_location_id,
'to_location': from_location_id,
})
res1 = Move.create(in_moves)
res2 = Move.create(out_moves)
Move.do(res1 + res2)
def create_production_moves(self, producibles, journal, period_id):
Production = Pool().get('production')
accounts = {}
lines = []
analytic = self.start.shop.analytic_account
date_ = self.start.date
company = self.start.company
for product, values in producibles.items():
quantity = sum(values['quantity'])
bom = values['bom']
output = bom.outputs[0]
inputs = bom.inputs
factor = quantity / output.quantity
for input in inputs:
_product = input.product
account_expense = _product.account_expense_used
account_stock = _product.account_stock_used
input_amount = Decimal(
input.quantity * float(_product.cost_price) * factor
)
try:
accounts[account_stock].append(input_amount)
except:
accounts[account_stock] = [input_amount]
material_costs = []
for acc, amount in accounts.items():
amount = Decimal(round(sum(amount), 2))
line_ = {
'description': '',
'account': acc.id,
'debit': 0,
'credit': amount,
'party': company
}
lines.append(line_)
material_costs.append(amount)
material_costs = sum(material_costs)
line_ = {
'description': '',
'account': account_expense.id,
'debit': material_costs,
'credit': 0,
'party': company,
}
Production.set_analytic_lines(line_, date_, analytic)
lines.append(line_)
self.create_move(lines, journal, period_id)
# Second move for load stock and discharge production
lines2 = []
dc_amount = []
for dc in bom.direct_costs:
account_id = dc.product.account_expense_used.id
amount = Decimal(dc.quantity) * dc.product.cost_price
amount = Decimal(round(float(amount) * factor, 2))
dc_amount.append(amount)
line_ = {
'description': '',
'account': account_id,
'debit': 0,
'credit': amount,
'party': company
}
Production.set_analytic_lines(line_, date_, analytic)
lines2.append(line_)
line_ = {
'description': '',
'account': account_expense.id,
'debit': 0,
'credit': material_costs,
'party': company
}
stock_amount = material_costs + sum(dc_amount)
lines2.append(line_)
line_ = {
'description': '',
'account': output.product.account_stock_used.id,
'debit': stock_amount,
'credit': 0,
'party': company
}
lines2.append(line_)
self.create_move(lines2, journal, period_id)
def transition_accept(self):
pool = Pool()
SaleLine = pool.get('sale.line')
Period = pool.get('account.period')
Journal = pool.get('account.journal')
Configuration = pool.get('production.configuration')
configuration = Configuration(1)
journals = Journal.search([
('code', '=', 'STO')
])
if journals:
journal = journals[0]
period_id = Period.find(self.start.company.id, date=self.start.date)
BOMOutput = pool.get('production.bom.output')
dom = [
('sale.state', 'in', ['processing', 'done']),
('sale.shop', '=', self.start.shop.id),
('sale.sale_date', '=', self.start.date),
('product.template.producible', '=', True),
('type', '=', 'line'),
('produced', '=', False),
]
lines = SaleLine.search(dom)
if not lines:
return 'end'
producibles = {}
def _add_producibles(product, qty, sub=False):
try:
producibles[product]['quantity'].append(qty)
bom = producibles[product]['bom']
output = bom.outputs[0]
except:
outputs = BOMOutput.search([
('product', '=', product.id),
('bom.active', '=', True)
])
if not outputs:
return
bom = outputs[0].bom
if sub and not bom.is_subproduction:
return
output = outputs[0]
producibles[product] = {
'quantity': [qty],
'bom': bom,
}
# Here we add all subproducts inside inputs too, because is
# neccesary for complete its production
for input in bom.inputs:
in_quantity, in_product = input.quantity, input.product
if in_product.template.producible:
_add_producibles(
in_product,
in_quantity * (qty / output.quantity),
sub=True)
for line in lines:
_add_producibles(line.product, line.quantity)
if configuration.production_accounting:
self.create_production_moves(producibles, journal, period_id)
self.update_product_cost(producibles)
self.create_stock_moves(producibles)
SaleLine.write(lines, {'produced': True})
return 'end'
class ProductionDetailedStart(ModelView):
'Production Detailed Start'
__name__ = 'production.detailed.start'
company = fields.Many2One('company.company', 'Company', required=True)
grouped = fields.Boolean('Grouped', help='Grouped by products')
start_date = fields.Date('Start Date')
end_date = fields.Date('End Date', required=True)
@staticmethod
def default_company():
return Transaction().context.get('company')
@staticmethod
def default_start_date():
Date = Pool().get('ir.date')
return Date.today()
@staticmethod
def default_end_date():
Date = Pool().get('ir.date')
return Date.today()
class ProductionDetailed(Wizard):
'Production Detailed'
__name__ = 'production.detailed'
start = StateView(
'production.detailed.start',
'production_accounting.production_detailed_start_view_form', [
Button('Cancel', 'end', 'tryton-cancel'),
Button('Print', 'print_', 'tryton-ok', default=True),
])
print_ = StateReport('production.detailed_report')
def do_print_(self, action):
data = {
'ids': [],
'company': self.start.company.id,
'start_date': self.start.start_date,
'end_date': self.start.end_date,
'grouped': self.start.grouped,
}
return action, data
def transition_print_(self):
return 'end'
class ProductionDetailedReport(Report):
'Production Detailed Report'
__name__ = 'production.detailed_report'
@classmethod
def get_context(cls, records, header, data):
report_context = super().get_context(records, header, data)
Production = Pool().get('production')
domain = [
('company', '=', data['company']),
('effective_date', '>=', data['start_date']),
('effective_date', '<=', data['end_date']),
]
fields_names = ['warehouse.name', 'location.name', 'effective_date',
'number', 'uom.name', 'quantity', 'cost', 'product.name', 'product.id']
productions = Production.search_read(domain, fields_names=fields_names)
if not data['grouped']:
records = productions
else:
records = {}
for p in productions:
key = str(p['product.']['id']) + p['location.']['name']
try:
records[key]['quantity'] += p['quantity']
records[key]['cost'] += p['cost']
except:
records[key] = p
records[key]['effective_date'] = None
records[key]['numero'] = None
records = records.values() if records else []
report_context['records'] = records
report_context['Decimal'] = Decimal
return report_context
class ProductionForceDraft(Wizard):
'Production Force Draft'
__name__ = 'production.force_draft'
start_state = 'force_draft'
force_draft = StateTransition()
def _reset_production(self, prod, stock_move, cursor):
# stock_move = Table('stock_move')
# cursor = Transaction().connection.cursor()
for rec in prod.outputs:
cursor.execute(*stock_move.update(
columns=[stock_move.state],
values=['draft'],
where=stock_move.id == rec.id)
)
inputs = [rec.id for rec in prod.inputs]
if inputs:
cursor.execute(*stock_move.update(
columns=[stock_move.state],
values=['draft'],
where=stock_move.id.in_(inputs))
)
prod.state = 'draft'
prod.save()
def _reset_subproduction(self, subprod, stock_move, cursor):
inputs = [rec.move.id if rec.move else None for rec in subprod.inputs]
if inputs:
cursor.execute(*stock_move.update(
columns=[stock_move.state],
values=['draft'],
where=stock_move.id.in_(inputs))
)
def transition_force_draft(self):
id_ = Transaction().context['active_id']
Production = Pool().get('production')
stock_move = Table('stock_move')
cursor = Transaction().connection.cursor()
if id_:
prod = Production(id_)
self._reset_production(prod, stock_move, cursor)
for sub in prod.subs:
self._reset_subproduction(sub, stock_move, cursor)
return 'end'