implemented funcionality
This commit is contained in:
parent
c13730e4f7
commit
17c14ffd71
|
@ -2,7 +2,11 @@
|
|||
#copyright notices and license terms.
|
||||
|
||||
from trytond.pool import Pool
|
||||
from .plan import *
|
||||
|
||||
|
||||
def register():
|
||||
Pool.register(
|
||||
PlanOperationLine,
|
||||
Plan,
|
||||
module='product_cost_plan_operation', type_='model')
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
#
|
||||
msgid ""
|
||||
msgstr "Content-Type: text/plain; charset=utf-8\n"
|
||||
|
||||
msgctxt "field:product.cost.plan,operation_cost:"
|
||||
msgid "Operation Cost"
|
||||
msgstr "Cost operació"
|
||||
|
||||
msgctxt "field:product.cost.plan,operations:"
|
||||
msgid "Operation Lines"
|
||||
msgstr "Línies d'operacions"
|
||||
|
||||
msgctxt "field:product.cost.plan,route:"
|
||||
msgid "Route"
|
||||
msgstr "Ruta"
|
||||
|
||||
msgctxt "field:product.cost.plan.operation_line,cost:"
|
||||
msgid "Cost"
|
||||
msgstr "Cost"
|
||||
|
||||
msgctxt "field:product.cost.plan.operation_line,create_date:"
|
||||
msgid "Create Date"
|
||||
msgstr "Data de creació"
|
||||
|
||||
msgctxt "field:product.cost.plan.operation_line,create_uid:"
|
||||
msgid "Create User"
|
||||
msgstr "Usuari de creació"
|
||||
|
||||
msgctxt "field:product.cost.plan.operation_line,id:"
|
||||
msgid "ID"
|
||||
msgstr "Identificador"
|
||||
|
||||
msgctxt "field:product.cost.plan.operation_line,plan:"
|
||||
msgid "Plan"
|
||||
msgstr "Pla"
|
||||
|
||||
msgctxt "field:product.cost.plan.operation_line,quantity:"
|
||||
msgid "Quantity"
|
||||
msgstr "Quantitat"
|
||||
|
||||
msgctxt "field:product.cost.plan.operation_line,rec_name:"
|
||||
msgid "Name"
|
||||
msgstr "Nom del camp"
|
||||
|
||||
msgctxt "field:product.cost.plan.operation_line,route_operation:"
|
||||
msgid "Route Operation"
|
||||
msgstr "Ruta"
|
||||
|
||||
msgctxt "field:product.cost.plan.operation_line,unit_digits:"
|
||||
msgid "Unit Digits"
|
||||
msgstr "Decimals unitaris"
|
||||
|
||||
msgctxt "field:product.cost.plan.operation_line,uom:"
|
||||
msgid "Uom"
|
||||
msgstr "UdM"
|
||||
|
||||
msgctxt "field:product.cost.plan.operation_line,uom_category:"
|
||||
msgid "Uom Category"
|
||||
msgstr "Categoria d'UdM"
|
||||
|
||||
msgctxt "field:product.cost.plan.operation_line,work_center:"
|
||||
msgid "Work Center"
|
||||
msgstr "Centre de treball"
|
||||
|
||||
msgctxt "field:product.cost.plan.operation_line,write_date:"
|
||||
msgid "Write Date"
|
||||
msgstr "Data modificació"
|
||||
|
||||
msgctxt "field:product.cost.plan.operation_line,write_uid:"
|
||||
msgid "Write User"
|
||||
msgstr "Usuari modificació"
|
||||
|
||||
msgctxt "model:ir.action,name:act_product_cost_plan_operation_line"
|
||||
msgid "Product Cost Plan Operation"
|
||||
msgstr "Operació del Pla de costos del producte"
|
||||
|
||||
msgctxt "model:product.cost.plan.operation_line,name:"
|
||||
msgid "Product Cost Plan Operation Line"
|
||||
msgstr "Operació del Pla de costos del producte"
|
||||
|
||||
msgctxt "view:product.cost.plan.operation_line:"
|
||||
msgid "Product Cost Plan BOM"
|
||||
msgstr "LdM Pla de costos de producte"
|
||||
|
||||
msgctxt "view:product.cost.plan.operation_line:"
|
||||
msgid "Product Cost Plan Operation"
|
||||
msgstr "Operació del Pla de costos del producte"
|
||||
|
||||
msgctxt "view:product.cost.plan:"
|
||||
msgid "Operations"
|
||||
msgstr "Operacions"
|
|
@ -0,0 +1,91 @@
|
|||
#
|
||||
msgid ""
|
||||
msgstr "Content-Type: text/plain; charset=utf-8\n"
|
||||
|
||||
msgctxt "field:product.cost.plan,operation_cost:"
|
||||
msgid "Operation Cost"
|
||||
msgstr "Coste operación"
|
||||
|
||||
msgctxt "field:product.cost.plan,operations:"
|
||||
msgid "Operation Lines"
|
||||
msgstr "Lineas operaciones"
|
||||
|
||||
msgctxt "field:product.cost.plan,route:"
|
||||
msgid "Route"
|
||||
msgstr "Ruta"
|
||||
|
||||
msgctxt "field:product.cost.plan.operation_line,cost:"
|
||||
msgid "Cost"
|
||||
msgstr "Coste"
|
||||
|
||||
msgctxt "field:product.cost.plan.operation_line,create_date:"
|
||||
msgid "Create Date"
|
||||
msgstr "Fecha de creación"
|
||||
|
||||
msgctxt "field:product.cost.plan.operation_line,create_uid:"
|
||||
msgid "Create User"
|
||||
msgstr "Usuario de creación"
|
||||
|
||||
msgctxt "field:product.cost.plan.operation_line,id:"
|
||||
msgid "ID"
|
||||
msgstr "Identificador"
|
||||
|
||||
msgctxt "field:product.cost.plan.operation_line,plan:"
|
||||
msgid "Plan"
|
||||
msgstr "Plan"
|
||||
|
||||
msgctxt "field:product.cost.plan.operation_line,quantity:"
|
||||
msgid "Quantity"
|
||||
msgstr "Cantidad"
|
||||
|
||||
msgctxt "field:product.cost.plan.operation_line,rec_name:"
|
||||
msgid "Name"
|
||||
msgstr "Nom del camp"
|
||||
|
||||
msgctxt "field:product.cost.plan.operation_line,route_operation:"
|
||||
msgid "Route Operation"
|
||||
msgstr "Ruta de operación"
|
||||
|
||||
msgctxt "field:product.cost.plan.operation_line,unit_digits:"
|
||||
msgid "Unit Digits"
|
||||
msgstr "Dígitos unidad"
|
||||
|
||||
msgctxt "field:product.cost.plan.operation_line,uom:"
|
||||
msgid "Uom"
|
||||
msgstr "UdM"
|
||||
|
||||
msgctxt "field:product.cost.plan.operation_line,uom_category:"
|
||||
msgid "Uom Category"
|
||||
msgstr "Categoría de UdM"
|
||||
|
||||
msgctxt "field:product.cost.plan.operation_line,work_center:"
|
||||
msgid "Work Center"
|
||||
msgstr "Centro de trabajo"
|
||||
|
||||
msgctxt "field:product.cost.plan.operation_line,write_date:"
|
||||
msgid "Write Date"
|
||||
msgstr "Fecha modificación"
|
||||
|
||||
msgctxt "field:product.cost.plan.operation_line,write_uid:"
|
||||
msgid "Write User"
|
||||
msgstr "Usuario modificación"
|
||||
|
||||
msgctxt "model:ir.action,name:act_product_cost_plan_operation_line"
|
||||
msgid "Product Cost Plan Operation"
|
||||
msgstr "Operación de Plan de coste del producto"
|
||||
|
||||
msgctxt "model:product.cost.plan.operation_line,name:"
|
||||
msgid "Product Cost Plan Operation Line"
|
||||
msgstr "Operación de Plan de coste del producto"
|
||||
|
||||
msgctxt "view:product.cost.plan.operation_line:"
|
||||
msgid "Product Cost Plan BOM"
|
||||
msgstr "LdM Plan de coste del producto"
|
||||
|
||||
msgctxt "view:product.cost.plan.operation_line:"
|
||||
msgid "Product Cost Plan Operation"
|
||||
msgstr "Operación de Plan de coste del producto"
|
||||
|
||||
msgctxt "view:product.cost.plan:"
|
||||
msgid "Operations"
|
||||
msgstr "Operaciones"
|
152
plan.py
152
plan.py
|
@ -1,67 +1,113 @@
|
|||
from decimal import Decimal
|
||||
from trytond.model import ModelSQL, ModelView, fields
|
||||
from trytond.pool import Pool, PoolMeta
|
||||
from trytond.pyson import Eval
|
||||
|
||||
class PlanOperationLine:
|
||||
__all__ = ['PlanOperationLine', 'Plan']
|
||||
__metaclass__ = PoolMeta
|
||||
|
||||
|
||||
class PlanOperationLine(ModelSQL, ModelView):
|
||||
'Product Cost Plan Operation Line'
|
||||
__name__ = 'product.cost.plan.operation_line'
|
||||
|
||||
plan = fields.Many2One('product.cost.plan', 'Plan', required=True)
|
||||
product = fields.Many2One('product.product', 'Product', required=True)
|
||||
quantity = fields.Float('Quantity', required=True)
|
||||
product_cost_price = fields.Numeric('Product Cost Price', required=True)
|
||||
cost_price = fields.Numeric('Cost Price', required=True)
|
||||
work_center = fields.Many2One('production.work_center', 'Work Center',
|
||||
required=True)
|
||||
route_operation = fields.Many2One('production.route.operation',
|
||||
'Route Operation')
|
||||
uom_category = fields.Function(fields.Many2One(
|
||||
'product.uom.category', 'Uom Category', on_change_with=[
|
||||
'work_center']),
|
||||
'on_change_with_uom_category')
|
||||
uom = fields.Many2One('product.uom', 'Uom', required=True, domain=[
|
||||
('category', '=', Eval('uom_category')),
|
||||
], depends=['uom_category'], on_change_with=['work_center'])
|
||||
unit_digits = fields.Function(fields.Integer('Unit Digits',
|
||||
on_change_with=['uom']), 'on_change_with_unit_digits')
|
||||
quantity = fields.Float('Quantity', required=True,
|
||||
digits=(16, Eval('unit_digits', 2)), depends=['unit_digits'])
|
||||
cost = fields.Function(fields.Numeric('Cost', on_change_with=['quantity',
|
||||
'cost_price', 'uom', 'work_center']), 'on_change_with_cost')
|
||||
|
||||
def on_change_with_uom_category(self, name=None):
|
||||
if self.work_center:
|
||||
return self.work_center.uom.category.id
|
||||
|
||||
def on_change_with_uom(self):
|
||||
if self.work_center:
|
||||
return self.work_center.uom.id
|
||||
|
||||
def on_change_with_unit_digits(self, name=None):
|
||||
if self.uom:
|
||||
return self.uom.digits
|
||||
return 2
|
||||
|
||||
def on_change_with_cost(self, name=None):
|
||||
pool = Pool()
|
||||
Uom = pool.get('product.uom')
|
||||
quantity = Uom.compute_qty(self.uom, self.quantity,
|
||||
self.work_center.uom)
|
||||
return Decimal(str(quantity)) * self.work_center.cost_price
|
||||
|
||||
|
||||
class Plan:
|
||||
__name__ = 'product.cost.plan'
|
||||
|
||||
route = fields.Many2One('production.route', 'Route',
|
||||
on_change=['route', 'operations'], states={
|
||||
'readonly': Eval('state') != 'draft',
|
||||
}, depends=['state'])
|
||||
operations = fields.One2Many('product.cost.plan.operation_line', 'plan',
|
||||
'Operation Lines')
|
||||
'Operation Lines', states={
|
||||
'readonly': Eval('state') != 'draft',
|
||||
}, depends=['state'])
|
||||
operation_cost = fields.Function(fields.Numeric('Operation Cost',
|
||||
on_change_with=['operations']), 'on_change_with_operation_cost')
|
||||
|
||||
@classmethod
|
||||
def get_margins(cls, plans, names):
|
||||
res = {}
|
||||
for plan in plans:
|
||||
# TODO:
|
||||
pass
|
||||
return res
|
||||
def update_operations(self):
|
||||
pool = Pool()
|
||||
WorkCenter = pool.get('production.work_center')
|
||||
if not self.route:
|
||||
return {}
|
||||
operations = {
|
||||
'remove': [x.id for x in self.operations],
|
||||
'add': [],
|
||||
}
|
||||
changes = {
|
||||
'operations': operations,
|
||||
}
|
||||
for operation in self.route.operations:
|
||||
work_center = None
|
||||
if operation.work_center:
|
||||
work_center = operation.work_center
|
||||
elif operation.work_center_category:
|
||||
centers = WorkCenter.search([
|
||||
('category', '=', operation.work_center_category),
|
||||
], limit=1)
|
||||
if centers:
|
||||
work_center, = centers
|
||||
|
||||
@classmethod
|
||||
@ModelView.button
|
||||
@Workflow.transition('confirmed')
|
||||
def confirm(cls, productions):
|
||||
'''
|
||||
Create margins lines
|
||||
'''
|
||||
pass
|
||||
if not work_center:
|
||||
self.raise_user_error('no_work_center', operation.rec_name)
|
||||
|
||||
operations['add'].append({
|
||||
'work_center': work_center.id,
|
||||
'route_operation': operation.id,
|
||||
'uom': work_center.uom.id,
|
||||
'quantity': 0.0,
|
||||
})
|
||||
return changes
|
||||
|
||||
# Mòdul: product_cost_plan_teb
|
||||
def on_change_route(self):
|
||||
return self.update_operations()
|
||||
|
||||
class Plan:
|
||||
__name__ = 'product.cost.plan'
|
||||
party = fields.Many2One('party.party', 'Party')
|
||||
payment_term = fields.Many2One('account.invoice.payment_term',
|
||||
'Payment Term', on_change_with=['party'])
|
||||
commission = fields.Float('Commission %', required=True)
|
||||
# TODO: Add here??
|
||||
pallet = fields.Boolean('TEB Pallet?')
|
||||
pallet_quantity = fields.fields.Float('Quantity per Pallet')
|
||||
def on_change_with_total_cost(self, name=None):
|
||||
cost = super(Plan, self).on_change_with_total_cost(name)
|
||||
return cost + self.operation_cost
|
||||
|
||||
@staticmethod
|
||||
def default_commission():
|
||||
return 0.0
|
||||
|
||||
def on_change_with_payment_term(self):
|
||||
return (self.party.customer_payment_term.id
|
||||
if self.party and self.party.customer_payment_term else None)
|
||||
|
||||
@classmethod
|
||||
@ModelView.button
|
||||
@Workflow.transition('confirmed')
|
||||
def confirm(cls, productions):
|
||||
'''
|
||||
Create:
|
||||
- payment term cost/margin
|
||||
- commission cost/margin
|
||||
- pallet cost/margin
|
||||
'''
|
||||
pass
|
||||
|
||||
|
||||
# TODO: Comptabilitat analítica, despeses estructurals, transport
|
||||
def on_change_with_operation_cost(self, name=None):
|
||||
cost = Decimal('0.0')
|
||||
for operation in self.operations:
|
||||
cost += operation.cost
|
||||
return cost
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<tryton>
|
||||
<data>
|
||||
|
||||
<record model="ir.ui.view" id="product_cost_plan_view_form">
|
||||
<field name="model">product.cost.plan</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">cost_plan_form</field>
|
||||
<field name="inherit"
|
||||
ref="product_cost_plan.product_cost_plan_view_form"/>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="product_cost_plan_view_list">
|
||||
<field name="model">product.cost.plan</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="name">cost_plan_list</field>
|
||||
<field name="inherit"
|
||||
ref="product_cost_plan.product_cost_plan_view_list"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view"
|
||||
id="product_cost_plan_operation_line_view_form">
|
||||
<field name="model">product.cost.plan.operation_line</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">cost_plan_operation_line_form</field>
|
||||
</record>
|
||||
<record model="ir.ui.view"
|
||||
id="product_cost_plan_operation_line_view_list">
|
||||
<field name="model">product.cost.plan.operation_line</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="name">cost_plan_operation_line_list</field>
|
||||
</record>
|
||||
<record model="ir.action.act_window"
|
||||
id="act_product_cost_plan_operation_line">
|
||||
<field name="name">Product Cost Plan Operation</field>
|
||||
<field name="res_model">product.cost.plan.operation_line</field>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view"
|
||||
id="act_product_cost_plan_operation_line_view1">
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="view" ref="product_cost_plan_operation_line_view_list"/>
|
||||
<field name="act_window" ref="act_product_cost_plan_operation_line"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view"
|
||||
id="act_product_cost_plan_operation_line_view2">
|
||||
<field name="sequence" eval="20"/>
|
||||
<field name="view" ref="product_cost_plan_operation_line_view_form"/>
|
||||
<field name="act_window" ref="act_product_cost_plan_operation_line"/>
|
||||
</record>
|
||||
<record model="ir.model.access"
|
||||
id="access_product_cost_plan_operation_line">
|
||||
<field name="model"
|
||||
search="[('model', '=', 'product.cost.plan.operation_line')]"/>
|
||||
<field name="perm_read" eval="True"/>
|
||||
<field name="perm_write" eval="False"/>
|
||||
<field name="perm_create" eval="False"/>
|
||||
<field name="perm_delete" eval="False"/>
|
||||
</record>
|
||||
<record model="ir.model.access"
|
||||
id="access_product_cost_plan_operation_line_admin">
|
||||
<field name="model"
|
||||
search="[('model', '=', 'product.cost.plan.operation_line')]"/>
|
||||
<field name="group"
|
||||
ref="product_cost_plan.group_product_cost_plan_admin"/>
|
||||
<field name="perm_read" eval="True"/>
|
||||
<field name="perm_write" eval="True"/>
|
||||
<field name="perm_create" eval="True"/>
|
||||
<field name="perm_delete" eval="True"/>
|
||||
</record>
|
||||
</data>
|
||||
</tryton>
|
|
@ -0,0 +1,257 @@
|
|||
===================
|
||||
Production Scenario
|
||||
===================
|
||||
|
||||
=============
|
||||
General Setup
|
||||
=============
|
||||
|
||||
Imports::
|
||||
|
||||
>>> import datetime
|
||||
>>> from dateutil.relativedelta import relativedelta
|
||||
>>> from decimal import Decimal
|
||||
>>> from proteus import config, Model, Wizard
|
||||
>>> today = datetime.date.today()
|
||||
|
||||
Create database::
|
||||
|
||||
>>> config = config.set_trytond()
|
||||
>>> config.pool.test = True
|
||||
|
||||
Install production Module::
|
||||
|
||||
>>> Module = Model.get('ir.module.module')
|
||||
>>> modules = Module.find([('name', '=', 'product_cost_plan')])
|
||||
>>> Module.install([x.id for x in modules], config.context)
|
||||
>>> Wizard('ir.module.module.install_upgrade').execute('upgrade')
|
||||
|
||||
Create company::
|
||||
|
||||
>>> Currency = Model.get('currency.currency')
|
||||
>>> CurrencyRate = Model.get('currency.currency.rate')
|
||||
>>> Company = Model.get('company.company')
|
||||
>>> Party = Model.get('party.party')
|
||||
>>> company_config = Wizard('company.company.config')
|
||||
>>> company_config.execute('company')
|
||||
>>> company = company_config.form
|
||||
>>> party = Party(name='Dunder Mifflin')
|
||||
>>> party.save()
|
||||
>>> company.party = party
|
||||
>>> currencies = Currency.find([('code', '=', 'USD')])
|
||||
>>> if not currencies:
|
||||
... currency = Currency(name='Euro', symbol=u'$', code='USD',
|
||||
... rounding=Decimal('0.01'), mon_grouping='[3, 3, 0]',
|
||||
... mon_decimal_point=',')
|
||||
... currency.save()
|
||||
... CurrencyRate(date=today + relativedelta(month=1, day=1),
|
||||
... rate=Decimal('1.0'), currency=currency).save()
|
||||
... else:
|
||||
... currency, = currencies
|
||||
>>> company.currency = currency
|
||||
>>> company_config.execute('add')
|
||||
>>> company, = Company.find()
|
||||
|
||||
Reload the context::
|
||||
|
||||
>>> User = Model.get('res.user')
|
||||
>>> config._context = User.get_preferences(True, config.context)
|
||||
|
||||
Configuration production location::
|
||||
|
||||
>>> Location = Model.get('stock.location')
|
||||
>>> warehouse, = Location.find([('code', '=', 'WH')])
|
||||
>>> production_location, = Location.find([('code', '=', 'PROD')])
|
||||
>>> warehouse.production_location = production_location
|
||||
>>> warehouse.save()
|
||||
|
||||
Create product::
|
||||
|
||||
>>> ProductUom = Model.get('product.uom')
|
||||
>>> unit, = ProductUom.find([('name', '=', 'Unit')])
|
||||
>>> ProductTemplate = Model.get('product.template')
|
||||
>>> Product = Model.get('product.product')
|
||||
>>> product = Product()
|
||||
>>> template = ProductTemplate()
|
||||
>>> template.name = 'product'
|
||||
>>> template.default_uom = unit
|
||||
>>> template.type = 'goods'
|
||||
>>> template.list_price = Decimal(30)
|
||||
>>> template.cost_price = Decimal(20)
|
||||
>>> template.save()
|
||||
>>> product.template = template
|
||||
>>> product.save()
|
||||
|
||||
Create Components::
|
||||
|
||||
>>> meter, = ProductUom.find([('name', '=', 'Meter')])
|
||||
>>> centimeter, = ProductUom.find([('name', '=', 'centimeter')])
|
||||
>>> componentA = Product()
|
||||
>>> templateA = ProductTemplate()
|
||||
>>> templateA.name = 'component A'
|
||||
>>> templateA.default_uom = meter
|
||||
>>> templateA.type = 'goods'
|
||||
>>> templateA.list_price = Decimal(2)
|
||||
>>> templateA.cost_price = Decimal(1)
|
||||
>>> templateA.save()
|
||||
>>> componentA.template = templateA
|
||||
>>> componentA.save()
|
||||
|
||||
>>> componentB = Product()
|
||||
>>> templateB = ProductTemplate()
|
||||
>>> templateB.name = 'component B'
|
||||
>>> templateB.default_uom = meter
|
||||
>>> templateB.type = 'goods'
|
||||
>>> templateB.list_price = Decimal(2)
|
||||
>>> templateB.cost_price = Decimal(1)
|
||||
>>> templateB.save()
|
||||
>>> componentB.template = templateB
|
||||
>>> componentB.save()
|
||||
|
||||
>>> component1 = Product()
|
||||
>>> template1 = ProductTemplate()
|
||||
>>> template1.name = 'component 1'
|
||||
>>> template1.default_uom = unit
|
||||
>>> template1.type = 'goods'
|
||||
>>> template1.list_price = Decimal(5)
|
||||
>>> template1.cost_price = Decimal(2)
|
||||
>>> template1.save()
|
||||
>>> component1.template = template1
|
||||
>>> component1.save()
|
||||
|
||||
>>> component2 = Product()
|
||||
>>> template2 = ProductTemplate()
|
||||
>>> template2.name = 'component 2'
|
||||
>>> template2.default_uom = meter
|
||||
>>> template2.type = 'goods'
|
||||
>>> template2.list_price = Decimal(7)
|
||||
>>> template2.cost_price = Decimal(5)
|
||||
>>> template2.save()
|
||||
>>> component2.template = template2
|
||||
>>> component2.save()
|
||||
|
||||
Create Bill of Material::
|
||||
|
||||
>>> BOM = Model.get('production.bom')
|
||||
>>> BOMInput = Model.get('production.bom.input')
|
||||
>>> BOMOutput = Model.get('production.bom.output')
|
||||
>>> component_bom = BOM(name='component1')
|
||||
>>> input1 = BOMInput()
|
||||
>>> component_bom.inputs.append(input1)
|
||||
>>> input1.product = componentA
|
||||
>>> input1.quantity = 1
|
||||
>>> input2 = BOMInput()
|
||||
>>> component_bom.inputs.append(input2)
|
||||
>>> input2.product = componentB
|
||||
>>> input2.quantity = 1
|
||||
>>> output = BOMOutput()
|
||||
>>> component_bom.outputs.append(output)
|
||||
>>> output.product = component1
|
||||
>>> output.quantity = 1
|
||||
>>> component_bom.save()
|
||||
|
||||
>>> ProductBom = Model.get('product.product-production.bom')
|
||||
>>> component1.boms.append(ProductBom(bom=component_bom))
|
||||
>>> component1.save()
|
||||
|
||||
>>> bom = BOM(name='product')
|
||||
>>> input1 = BOMInput()
|
||||
>>> bom.inputs.append(input1)
|
||||
>>> input1.product = component1
|
||||
>>> input1.quantity = 5
|
||||
>>> input2 = BOMInput()
|
||||
>>> bom.inputs.append(input2)
|
||||
>>> input2.product = component2
|
||||
>>> input2.quantity = 150
|
||||
>>> input2.uom = centimeter
|
||||
>>> output = BOMOutput()
|
||||
>>> bom.outputs.append(output)
|
||||
>>> output.product = product
|
||||
>>> output.quantity = 1
|
||||
>>> bom.save()
|
||||
|
||||
>>> ProductBom = Model.get('product.product-production.bom')
|
||||
>>> product.boms.append(ProductBom(bom=bom))
|
||||
>>> product.save()
|
||||
|
||||
Create a cost plan for product (without child boms)::
|
||||
|
||||
>>> CostPlan = Model.get('product.cost.plan')
|
||||
>>> plan = CostPlan()
|
||||
>>> plan.product = product
|
||||
>>> len(plan.boms) == 1
|
||||
True
|
||||
>>> plan.boms[0].bom == None
|
||||
True
|
||||
>>> plan.quantity = 10
|
||||
>>> plan.save()
|
||||
>>> plan.state
|
||||
u'draft'
|
||||
>>> CostPlan.compute([plan.id], config.context)
|
||||
>>> plan.reload()
|
||||
>>> plan.state
|
||||
u'computed'
|
||||
>>> len(plan.products) == 2
|
||||
True
|
||||
>>> c1, = plan.products.find([
|
||||
... ('product', '=', component1.id),
|
||||
... ], limit=1)
|
||||
>>> c1.quantity == 50.0
|
||||
True
|
||||
>>> c2, = plan.products.find([
|
||||
... ('product', '=', component2.id),
|
||||
... ], limit=1)
|
||||
>>> c2.quantity == 1500.0
|
||||
True
|
||||
>>> cA = plan.products.find([
|
||||
... ('product', '=', componentA.id),
|
||||
... ], limit=1)
|
||||
>>> len(cA) == 0
|
||||
True
|
||||
>>> cB = plan.products.find([
|
||||
... ('product', '=', componentB.id),
|
||||
... ], limit=1)
|
||||
>>> len(cB) == 0
|
||||
True
|
||||
>>> plan.total_cost == Decimal('175.0')
|
||||
True
|
||||
|
||||
Create a cost plan for product (with child boms)::
|
||||
|
||||
>>> CostPlan = Model.get('product.cost.plan')
|
||||
>>> plan = CostPlan()
|
||||
>>> plan.product = product
|
||||
>>> len(plan.boms) == 1
|
||||
True
|
||||
>>> plan.quantity = 10
|
||||
>>> plan.save()
|
||||
>>> plan.state
|
||||
u'draft'
|
||||
>>> for product_bom in plan.boms:
|
||||
... product_bom.bom = product_bom.product.boms[0]
|
||||
... product_bom.save()
|
||||
>>> plan.reload()
|
||||
>>> CostPlan.compute([plan.id], config.context)
|
||||
>>> plan.reload()
|
||||
>>> plan.state
|
||||
u'computed'
|
||||
>>> len(plan.products) == 3
|
||||
True
|
||||
>>> cA, = plan.products.find([
|
||||
... ('product', '=', componentA.id),
|
||||
... ], limit=1)
|
||||
>>> cA.quantity == 50.0
|
||||
True
|
||||
>>> cB, = plan.products.find([
|
||||
... ('product', '=', componentB.id),
|
||||
... ], limit=1)
|
||||
>>> cB.quantity == 50.0
|
||||
True
|
||||
>>> c2, = plan.products.find([
|
||||
... ('product', '=', component2.id),
|
||||
... ], limit=1)
|
||||
>>> c2.quantity == 1500.0
|
||||
True
|
||||
>>> plan.total_cost == Decimal('175.0')
|
||||
True
|
||||
|
|
@ -0,0 +1,222 @@
|
|||
===================
|
||||
Production Scenario
|
||||
===================
|
||||
|
||||
=============
|
||||
General Setup
|
||||
=============
|
||||
|
||||
Imports::
|
||||
|
||||
>>> import datetime
|
||||
>>> from dateutil.relativedelta import relativedelta
|
||||
>>> from decimal import Decimal
|
||||
>>> from proteus import config, Model, Wizard
|
||||
>>> today = datetime.date.today()
|
||||
|
||||
Create database::
|
||||
|
||||
>>> config = config.set_trytond()
|
||||
>>> config.pool.test = True
|
||||
|
||||
Install production Module::
|
||||
|
||||
>>> Module = Model.get('ir.module.module')
|
||||
>>> modules = Module.find([('name', '=', 'product_cost_plan_operation')])
|
||||
>>> Module.install([x.id for x in modules], config.context)
|
||||
>>> Wizard('ir.module.module.install_upgrade').execute('upgrade')
|
||||
|
||||
Create company::
|
||||
|
||||
>>> Currency = Model.get('currency.currency')
|
||||
>>> CurrencyRate = Model.get('currency.currency.rate')
|
||||
>>> Company = Model.get('company.company')
|
||||
>>> Party = Model.get('party.party')
|
||||
>>> company_config = Wizard('company.company.config')
|
||||
>>> company_config.execute('company')
|
||||
>>> company = company_config.form
|
||||
>>> party = Party(name='Dunder Mifflin')
|
||||
>>> party.save()
|
||||
>>> company.party = party
|
||||
>>> currencies = Currency.find([('code', '=', 'USD')])
|
||||
>>> if not currencies:
|
||||
... currency = Currency(name='Euro', symbol=u'$', code='USD',
|
||||
... rounding=Decimal('0.01'), mon_grouping='[3, 3, 0]',
|
||||
... mon_decimal_point=',')
|
||||
... currency.save()
|
||||
... CurrencyRate(date=today + relativedelta(month=1, day=1),
|
||||
... rate=Decimal('1.0'), currency=currency).save()
|
||||
... else:
|
||||
... currency, = currencies
|
||||
>>> company.currency = currency
|
||||
>>> company_config.execute('add')
|
||||
>>> company, = Company.find()
|
||||
|
||||
Reload the context::
|
||||
|
||||
>>> User = Model.get('res.user')
|
||||
>>> config._context = User.get_preferences(True, config.context)
|
||||
|
||||
Configuration production location::
|
||||
|
||||
>>> Location = Model.get('stock.location')
|
||||
>>> warehouse, = Location.find([('code', '=', 'WH')])
|
||||
>>> production_location, = Location.find([('code', '=', 'PROD')])
|
||||
>>> warehouse.production_location = production_location
|
||||
>>> warehouse.save()
|
||||
|
||||
Create a route with two operations on diferent work center::
|
||||
|
||||
>>> ProductUom = Model.get('product.uom')
|
||||
>>> Route = Model.get('production.route')
|
||||
>>> OperationType = Model.get('production.operation.type')
|
||||
>>> RouteOperation = Model.get('production.route.operation')
|
||||
>>> assembly = OperationType(name='Assembly')
|
||||
>>> assembly.save()
|
||||
>>> clean = OperationType(name='clean')
|
||||
>>> clean.save()
|
||||
>>> WorkCenter = Model.get('production.work_center')
|
||||
>>> hour, = ProductUom.find([('name', '=', 'Hour')])
|
||||
>>> workcenter1 = WorkCenter()
|
||||
>>> workcenter1.name = 'Assembler Machine'
|
||||
>>> workcenter1.type = 'machine'
|
||||
>>> workcenter1.uom = hour
|
||||
>>> workcenter1.cost_price = Decimal('25.0')
|
||||
>>> workcenter1.save()
|
||||
>>> workcenter2 = WorkCenter()
|
||||
>>> workcenter2.name = 'Cleaner Machine'
|
||||
>>> workcenter2.type = 'machine'
|
||||
>>> workcenter2.uom = hour
|
||||
>>> workcenter2.cost_price = Decimal('50.0')
|
||||
>>> workcenter2.save()
|
||||
>>> route = Route(name='default route')
|
||||
>>> route_operation = RouteOperation()
|
||||
>>> route.operations.append(route_operation)
|
||||
>>> route_operation.sequence = 1
|
||||
>>> route_operation.operation_type = assembly
|
||||
>>> route_operation.work_center = workcenter1
|
||||
>>> route_operation = RouteOperation()
|
||||
>>> route.operations.append(route_operation)
|
||||
>>> route_operation.sequence = 2
|
||||
>>> route_operation.operation_type = clean
|
||||
>>> route_operation.work_center = workcenter2
|
||||
>>> route.save()
|
||||
>>> route.reload()
|
||||
>>> len(route.operations) == 2
|
||||
True
|
||||
|
||||
|
||||
Create product::
|
||||
|
||||
>>> unit, = ProductUom.find([('name', '=', 'Unit')])
|
||||
>>> ProductTemplate = Model.get('product.template')
|
||||
>>> Product = Model.get('product.product')
|
||||
>>> product = Product()
|
||||
>>> template = ProductTemplate()
|
||||
>>> template.name = 'product'
|
||||
>>> template.default_uom = unit
|
||||
>>> template.type = 'goods'
|
||||
>>> template.list_price = Decimal(30)
|
||||
>>> template.cost_price = Decimal(20)
|
||||
>>> template.save()
|
||||
>>> product.template = template
|
||||
>>> product.save()
|
||||
|
||||
Create Components::
|
||||
|
||||
>>> component1 = Product()
|
||||
>>> template1 = ProductTemplate()
|
||||
>>> template1.name = 'component 1'
|
||||
>>> template1.default_uom = unit
|
||||
>>> template1.type = 'goods'
|
||||
>>> template1.list_price = Decimal(5)
|
||||
>>> template1.cost_price = Decimal(1)
|
||||
>>> template1.save()
|
||||
>>> component1.template = template1
|
||||
>>> component1.save()
|
||||
|
||||
>>> meter, = ProductUom.find([('name', '=', 'Meter')])
|
||||
>>> centimeter, = ProductUom.find([('name', '=', 'centimeter')])
|
||||
>>> component2 = Product()
|
||||
>>> template2 = ProductTemplate()
|
||||
>>> template2.name = 'component 2'
|
||||
>>> template2.default_uom = meter
|
||||
>>> template2.type = 'goods'
|
||||
>>> template2.list_price = Decimal(7)
|
||||
>>> template2.cost_price = Decimal(5)
|
||||
>>> template2.save()
|
||||
>>> component2.template = template2
|
||||
>>> component2.save()
|
||||
|
||||
Create Bill of Material::
|
||||
|
||||
>>> BOM = Model.get('production.bom')
|
||||
>>> BOMInput = Model.get('production.bom.input')
|
||||
>>> BOMOutput = Model.get('production.bom.output')
|
||||
>>> bom = BOM(name='product')
|
||||
>>> input1 = BOMInput()
|
||||
>>> bom.inputs.append(input1)
|
||||
>>> input1.product = component1
|
||||
>>> input1.quantity = 5
|
||||
>>> input2 = BOMInput()
|
||||
>>> bom.inputs.append(input2)
|
||||
>>> input2.product = component2
|
||||
>>> input2.quantity = 150
|
||||
>>> input2.uom = centimeter
|
||||
>>> output = BOMOutput()
|
||||
>>> bom.outputs.append(output)
|
||||
>>> output.product = product
|
||||
>>> output.quantity = 1
|
||||
>>> bom.save()
|
||||
|
||||
>>> ProductBom = Model.get('product.product-production.bom')
|
||||
>>> product.boms.append(ProductBom(bom=bom))
|
||||
>>> product.save()
|
||||
|
||||
Create an Inventory::
|
||||
|
||||
>>> Inventory = Model.get('stock.inventory')
|
||||
>>> InventoryLine = Model.get('stock.inventory.line')
|
||||
>>> storage, = Location.find([
|
||||
... ('code', '=', 'STO'),
|
||||
... ])
|
||||
>>> inventory = Inventory()
|
||||
>>> inventory.location = storage
|
||||
>>> inventory_line1 = InventoryLine()
|
||||
>>> inventory.lines.append(inventory_line1)
|
||||
>>> inventory_line1.product = component1
|
||||
>>> inventory_line1.quantity = 10
|
||||
>>> inventory_line2 = InventoryLine()
|
||||
>>> inventory.lines.append(inventory_line2)
|
||||
>>> inventory_line2.product = component2
|
||||
>>> inventory_line2.quantity = 5
|
||||
>>> inventory.save()
|
||||
>>> Inventory.confirm([inventory.id], config.context)
|
||||
>>> inventory.state
|
||||
u'done'
|
||||
|
||||
Create a cost plan for product::
|
||||
|
||||
>>> CostPlan = Model.get('product.cost.plan')
|
||||
>>> plan = CostPlan()
|
||||
>>> plan.product = product
|
||||
>>> plan.route = route
|
||||
>>> len(plan.operations) == 2
|
||||
True
|
||||
>>> plan.quantity = 10
|
||||
>>> plan.save()
|
||||
>>> plan.state
|
||||
u'draft'
|
||||
>>> for operation in plan.operations:
|
||||
... operation.quantity = 1.0
|
||||
... operation.save()
|
||||
>>> CostPlan.compute([plan.id], config.context)
|
||||
>>> plan.reload()
|
||||
>>> plan.state
|
||||
u'computed'
|
||||
>>> len(plan.products) == 2
|
||||
True
|
||||
>>> plan.operation_cost == Decimal('75.0')
|
||||
True
|
||||
>>> plan.total_cost == plan.product_cost + plan.operation_cost
|
||||
True
|
|
@ -10,7 +10,7 @@ if os.path.isdir(DIR):
|
|||
sys.path.insert(0, os.path.dirname(DIR))
|
||||
|
||||
import unittest
|
||||
#import doctest TODO: Remove if no sceneario needed.
|
||||
import doctest
|
||||
import trytond.tests.test_tryton
|
||||
from trytond.tests.test_tryton import test_view, test_depends
|
||||
from trytond.backend.sqlite.database import Database as SQLiteDatabase
|
||||
|
@ -24,12 +24,12 @@ class TestCase(unittest.TestCase):
|
|||
def setUp(self):
|
||||
trytond.tests.test_tryton.install_module('product_cost_plan_operation')
|
||||
|
||||
# def test0005views(self):
|
||||
# '''
|
||||
# Test views.
|
||||
# '''
|
||||
# test_view('product_cost_plan_operation')
|
||||
#
|
||||
def test0005views(self):
|
||||
'''
|
||||
Test views.
|
||||
'''
|
||||
test_view('product_cost_plan_operation')
|
||||
|
||||
def test0006depends(self):
|
||||
'''
|
||||
Test depends.
|
||||
|
@ -50,10 +50,10 @@ def doctest_dropdb(test):
|
|||
def suite():
|
||||
suite = trytond.tests.test_tryton.suite()
|
||||
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestCase))
|
||||
# TODO: remove if no scenario needed.
|
||||
#suite.addTests(doctest.DocFileSuite('scenario_invoice.rst',
|
||||
# setUp=doctest_dropdb, tearDown=doctest_dropdb, encoding='utf-8',
|
||||
# optionflags=doctest.REPORT_ONLY_FIRST_FAILURE))
|
||||
suite.addTests(doctest.DocFileSuite(
|
||||
'scenario_product_cost_plan_operation.rst',
|
||||
setUp=doctest_dropdb, tearDown=doctest_dropdb, encoding='utf-8',
|
||||
optionflags=doctest.REPORT_ONLY_FIRST_FAILURE))
|
||||
return suite
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
[tryton]
|
||||
version=3.0.0
|
||||
depends:
|
||||
production
|
||||
product_cost_plan
|
||||
production_operation
|
||||
xml:
|
||||
plan.xml
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- The COPYRIGHT file at the top level of this repository contains the full
|
||||
copyright notices and license terms. -->
|
||||
<data>
|
||||
<xpath expr="/form/field[@name='quantity']" position="after">
|
||||
<label name="route"/>
|
||||
<field name="route"/>
|
||||
</xpath>
|
||||
<xpath expr="/form/notebook/page[@id='general']" position="after">
|
||||
<page string="Operations" id="operations">
|
||||
<field name="operations" colspan="4"/>
|
||||
<label name="operation_cost"/>
|
||||
<field name="operation_cost"/>
|
||||
</page>
|
||||
</xpath>
|
||||
</data>
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- The COPYRIGHT file at the top level of this repository contains the full
|
||||
copyright notices and license terms. -->
|
||||
<data>
|
||||
<xpath expr="/tree/field[@name='bom']" position="after">
|
||||
<field name="route"/>
|
||||
</xpath>
|
||||
</data>
|
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- The COPYRIGHT file at the top level of this repository contains the full
|
||||
copyright notices and license terms. -->
|
||||
<form string="Product Cost Plan BOM">
|
||||
<label name="plan"/>
|
||||
<field name="plan"/>
|
||||
<newline/>
|
||||
<label name="route_operation"/>
|
||||
<field name="route_operation"/>
|
||||
<label name="work_center"/>
|
||||
<field name="work_center"/>
|
||||
<label name="quantity"/>
|
||||
<field name="quantity"/>
|
||||
<label name="uom"/>
|
||||
<field name="uom"/>
|
||||
</form>
|
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- The COPYRIGHT file at the top level of this repository contains the full
|
||||
copyright notices and license terms. -->
|
||||
<tree string="Product Cost Plan Operation" editable="bottom">
|
||||
<field name="plan"/>
|
||||
<field name="route_operation"/>
|
||||
<field name="work_center"/>
|
||||
<field name="quantity"/>
|
||||
<field name="uom"/>
|
||||
<field name="cost"/>
|
||||
</tree>
|
Loading…
Reference in New Issue