update to 4.7

This commit is contained in:
?ngel ?lvarez 2018-04-25 15:44:48 +02:00
parent ca643dcc70
commit 8197b17013
25 changed files with 294 additions and 185 deletions

57
.drone.yml Normal file
View File

@ -0,0 +1,57 @@
clone:
hg:
image: plugins/hg
pipeline:
tox:
image: ${IMAGE}
environment:
- CFLAGS=-O0
- DB_CACHE=/cache
- TOX_TESTENV_PASSENV=CFLAGS DB_CACHE
- POSTGRESQL_URI=postgresql://postgres@postgresql:5432/
commands:
- pip install tox
- tox -e "${TOXENV}-${DATABASE}"
notify:
image: drillster/drone-email
from: drone@localhost
host: smtp
port: 25
skip_verify: true
when:
status: [ changed, failure ]
services:
postgresql:
image: postgres
when:
matrix:
DATABASE: postgresql
matrix:
include:
- IMAGE: python:2.7
TOXENV: py27
DATABASE: sqlite
- IMAGE: python:2.7
TOXENV: py27
DATABASE: postgresql
- IMAGE: python:3.4
TOXENV: py34
DATABASE: sqlite
- IMAGE: python:3.4
TOXENV: py34
DATABASE: postgresql
- IMAGE: python:3.5
TOXENV: py35
DATABASE: sqlite
- IMAGE: python:3.5
TOXENV: py35
DATABASE: postgresql
- IMAGE: python:3.6
TOXENV: py36
DATABASE: sqlite
- IMAGE: python:3.6
TOXENV: py36
DATABASE: postgresql

View File

@ -1,20 +1,22 @@
#The COPYRIGHT file at the top level of this repository contains the full
#copyright notices and license terms.
# The COPYRIGHT file at the top level of this repository contains the full
# copyright notices and license terms.
from trytond.pool import Pool
from .plan import *
from .configuration import *
from . import plan
from . import configuration
def register():
Pool.register(
Plan,
PlanBOM,
PlanProductLine,
PlanCostType,
PlanCost,
Configuration,
CreateBomStart,
plan.Plan,
plan.PlanBOM,
plan.PlanProductLine,
plan.PlanCostType,
plan.PlanCost,
configuration.Configuration,
configuration.ConfigurationProductcostPlan,
plan.CreateBomStart,
module='product_cost_plan', type_='model')
Pool.register(
CreateBom,
plan.CreateBom,
module='product_cost_plan', type_='wizard')

View File

@ -1,19 +1,41 @@
#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 trytond.model import fields
# 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 trytond.model import fields, ModelSQL
from trytond.pyson import Eval
from trytond.pool import PoolMeta
from trytond.pool import PoolMeta, Pool
from trytond.modules.company.model import (
CompanyMultiValueMixin, CompanyValueMixin)
__all__ = ['Configuration']
__metaclass__ = PoolMeta
__all__ = ['Configuration', 'ConfigurationProductcostPlan']
class Configuration:
class Configuration(CompanyMultiValueMixin):
__name__ = 'production.configuration'
__metaclass__ = PoolMeta
product_cost_plan_sequence = fields.Property(fields.Many2One('ir.sequence',
product_cost_plan_sequence = fields.MultiValue(
fields.Many2One('ir.sequence',
'Product Cost Plan Sequence', domain=[
('company', 'in',
[Eval('context', {}).get('company', -1), None]),
('code', '=', 'product_cost_plan'),
], required=True))
@classmethod
def multivalue_model(cls, field):
pool = Pool()
if field in {'product_cost_plan_sequence'}:
return pool.get('production.configuration.cost_plan')
return super(Configuration, cls).multivalue_model(field)
class ConfigurationProductcostPlan(ModelSQL, CompanyValueMixin):
"Production Configuration Cost Plan"
__name__ = 'production.configuration.cost_plan'
product_cost_plan_sequence = fields.Many2One('ir.sequence',
'Product Cost Plan Sequence', domain=[
('company', 'in',
[Eval('context', {}).get('company', -1), None]),
('code', '=', 'product_cost_plan'),
], required=True)

View File

@ -5,15 +5,8 @@ this repository contains the full copyright notices and license terms. -->
<data>
<record model="ir.ui.view" id="production_configuration_view_form">
<field name="model">production.configuration</field>
<field name="type" eval="None"/>
<field name="inherit" ref="production.production_configuration_view_form"/>
<field name="name">configuration_form</field>
</record>
<record model="ir.property" id="property_product_cost_plan_sequence">
<field name="field"
search="[('model.model', '=', 'production.configuration'), ('name', '=', 'product_cost_plan_sequence')]"/>
<field name="value" eval="'ir.sequence,' + str(ref('sequence_product_cost_plan'))"/>
<field name="inherit" ref="production.production_configuration_view_form"/>
</record>
</data>
</tryton>

62
plan.py
View File

@ -4,15 +4,15 @@ from decimal import Decimal
from trytond.config import config
from trytond.model import ModelSQL, ModelView, fields
from trytond.modules.product import TemplateFunction
from trytond.pool import Pool
from trytond.pyson import Eval, Bool, If
from trytond.transaction import Transaction
from trytond.wizard import Wizard, StateView, StateAction, Button
price_digits = (16, config.getint('product', 'price_decimal', default=4))
__all__ = ['PlanCostType', 'Plan', 'PlanBOM', 'PlanProductLine', 'PlanCost',
'CreateBomStart', 'CreateBom']
DIGITS = (16, config.getint('product', 'price_decimal', default=4))
class PlanCostType(ModelSQL, ModelView):
@ -64,14 +64,14 @@ class Plan(ModelSQL, ModelView):
depends=['costs']),
'get_products_tree', setter='set_products_tree')
products_cost = fields.Function(fields.Numeric('Products Cost',
digits=DIGITS),
digits=price_digits),
'get_products_cost')
costs = fields.One2Many('product.cost.plan.cost', 'plan', 'Costs')
product_cost_price = fields.Function(fields.Numeric('Product Cost Price',
digits=DIGITS),
digits=price_digits),
'on_change_with_product_cost_price')
cost_price = fields.Function(fields.Numeric('Unit Cost Price',
digits=DIGITS),
digits=price_digits),
'get_cost_price')
notes = fields.Text('Notes')
@ -121,7 +121,9 @@ class Plan(ModelSQL, ModelView):
self.bom = None
if self.product:
self.name = self.product.rec_name
self.bom = self.on_change_with_bom()
bom = self.on_change_with_bom()
self.bom = bom
self.boms = [x[1] for x in self.find_boms()]
if self.product:
self.uom = self.product.default_uom
@ -145,6 +147,19 @@ class Plan(ModelSQL, ModelView):
if boms:
return boms[0].id
def find_boms(self, inputs=None):
res = []
if not self.bom:
return res
if not inputs:
inputs = self.bom.inputs
for input_ in inputs:
if input_.product.boms:
product_bom = input_.product.boms[0].bom
res.append((input_.product.id, product_bom.id))
res += self.find_boms(product_bom.inputs)
return res
@fields.depends('bom', 'boms', 'product')
def on_change_with_boms(self):
boms = {
@ -154,16 +169,7 @@ class Plan(ModelSQL, ModelView):
if not self.bom:
return boms
def find_boms(inputs):
res = []
for input_ in inputs:
if input_.product.boms:
product_bom = input_.product.boms[0].bom
res.append((input_.product.id, product_bom.id))
res += find_boms(product_bom.inputs)
return res
products = set(find_boms(self.bom.inputs))
products = set(self.find_boms())
for index, (product_id, _) in enumerate(products):
boms['add'].append((index, {
'product': product_id,
@ -339,10 +345,7 @@ class Plan(ModelSQL, ModelView):
assert self.product
cost_price = Uom.compute_price(self.uom, self.cost_price,
self.product.default_uom)
if (hasattr(self.product.__class__, 'cost_price') and not
isinstance(self.product.__class__.cost_price, TemplateFunction)
):
if hasattr(self.product.__class__, 'cost_price'):
digits = self.product.__class__.cost_price.digits[1]
cost_price = cost_price.quantize(Decimal(str(10 ** -digits)))
self.product.cost_price = cost_price
@ -519,18 +522,18 @@ class PlanProductLine(ModelSQL, ModelView):
party_stock = fields.Boolean('Party Stock',
help='Use stock owned by party instead of company stock.')
product_cost_price = fields.Numeric('Product Cost Price',
digits=DIGITS,
digits=price_digits,
states={
'readonly': True,
}, depends=['product'])
cost_price = fields.Numeric('Cost Price', required=True,
digits=DIGITS)
digits=price_digits)
unit_cost = fields.Function(fields.Numeric('Unit Cost',
digits=DIGITS,
digits=price_digits,
help="The cost of this product for each unit of plan's product."),
'get_unit_cost')
total_cost = fields.Function(fields.Numeric('Total Cost',
digits=DIGITS,
digits=price_digits,
help="The cost of this product for total plan's quantity."),
'get_total_cost')
@ -561,7 +564,6 @@ class PlanProductLine(ModelSQL, ModelView):
if self.product.may_belong_to_party:
zero_cost_price = True
self.uom = self.product.default_uom.id
self.uom.rec_name = self.product.default_uom.rec_name
self.product_cost_price = self.product.cost_price
if zero_cost_price:
self.cost_price = Decimal('0.0')
@ -571,7 +573,6 @@ class PlanProductLine(ModelSQL, ModelView):
self.name = None
self.party_stock = False
self.uom = None
self.uom.rec_name = ''
self.product_cost_price = None
@fields.depends('children', '_parent_plan.uom', 'product', 'uom', 'plan')
@ -584,7 +585,7 @@ class PlanProductLine(ModelSQL, ModelView):
if self.product:
return self.product.default_uom.category.id
@fields.depends('uom', 'product')
@fields.depends('uom')
def on_change_with_uom_digits(self, name=None):
if self.uom:
return self.uom.digits
@ -596,7 +597,8 @@ class PlanProductLine(ModelSQL, ModelView):
if self.party_stock:
self.cost_price = Decimal('0.0')
if self.cost_price is None and self.product and self.uom:
return
if not self.cost_price and self.product and self.uom:
digits = self.__class__.cost_price.digits[1]
cost = UoM.compute_price(self.product.default_uom,
self.product.cost_price, self.uom)
@ -693,9 +695,9 @@ class PlanCost(ModelSQL, ModelView):
('system', '=', Eval('system')),
],
required=True, states=STATES, depends=DEPENDS)
internal_cost = fields.Numeric('Cost (Internal Use)', digits=DIGITS,
internal_cost = fields.Numeric('Cost (Internal Use)', digits=price_digits,
readonly=True)
cost = fields.Function(fields.Numeric('Cost', digits=DIGITS,
cost = fields.Function(fields.Numeric('Cost', digits=price_digits,
required=True, states=STATES, depends=DEPENDS),
'get_cost', setter='set_cost')
system = fields.Boolean('System Managed', readonly=True)

View File

@ -4,44 +4,83 @@
from setuptools import setup
import re
import os
import ConfigParser
import io
try:
from configparser import ConfigParser
except ImportError:
from ConfigParser import ConfigParser
MODULE = 'product_cost_plan'
PREFIX = 'nantic'
MODULE2PREFIX = {}
MODULE2PREFIX = {
'production_external_party': 'nantic',
}
def read(fname):
return open(os.path.join(os.path.dirname(__file__), fname)).read()
return io.open(
os.path.join(os.path.dirname(__file__), fname),
'r', encoding='utf-8').read()
config = ConfigParser.ConfigParser()
def get_require_version(name):
if minor_version % 2:
require = '%s >= %s.%s.dev0, < %s.%s'
else:
require = '%s >= %s.%s, < %s.%s'
require %= (name, major_version, minor_version,
major_version, minor_version + 1)
return require
config = ConfigParser()
config.readfp(open('tryton.cfg'))
info = dict(config.items('tryton'))
for key in ('depends', 'extras_depend', 'xml'):
if key in info:
info[key] = info[key].strip().splitlines()
major_version, minor_version, _ = info.get('version', '0.0.1').split('.', 2)
version = info.get('version', '0.0.1')
major_version, minor_version, _ = version.split('.', 2)
major_version = int(major_version)
minor_version = int(minor_version)
requires = []
for dep in info.get('depends', []):
if not re.match(r'(ir|res|webdav)(\W|$)', dep):
for dep in info.get('depends', []) + ['production_external_party']:
if not re.match(r'(ir|res)(\W|$)', dep):
prefix = MODULE2PREFIX.get(dep, 'trytond')
requires.append('%s_%s >= %s.%s, < %s.%s' %
(prefix, dep, major_version, minor_version,
major_version, minor_version + 1))
requires.append('trytond >= %s.%s, < %s.%s' %
(major_version, minor_version, major_version, minor_version + 1))
requires.append(get_require_version('%s_%s' % (prefix, dep)))
requires.append(get_require_version('trytond'))
tests_require = ['proteus >= %s.%s, < %s.%s' %
(major_version, minor_version, major_version, minor_version + 1)]
tests_require = [
get_require_version('proteus'),
get_require_version('nantic-production_external_party')
]
series = '%s.%s' % (major_version, minor_version)
if minor_version % 2:
branch = 'default'
else:
branch = series
dependency_links = [
('hg+https://bitbucket.org/nantic/'
'trytond-production_external_party@%(branch)s'
'#egg=nantic-production_external_party-%(series)s' % {
'branch': branch,
'series': series,
}),
]
if minor_version % 2:
# Add development index for testing with proteus
dependency_links.append('https://trydevpi.tryton.org/')
setup(name='%s_%s' % (PREFIX, MODULE),
version=info.get('version', '0.0.1'),
description='Product Cost Plan',
version=version,
description='',
long_description=read('README'),
author='NaN·tic',
author_email='info@nan-tic.com',
url='http://www.nan-tic.com/',
download_url="https://bitbucket.org/nantic/trytond-%s" % MODULE,
package_dir={'trytond.modules.%s' % MODULE: '.'},
@ -51,7 +90,7 @@ setup(name='%s_%s' % (PREFIX, MODULE),
],
package_data={
'trytond.modules.%s' % MODULE: (info.get('xml', [])
+ ['tryton.cfg', 'locale/*.po', 'view/*.xml', 'tests/*.rst']),
+ ['tryton.cfg', 'locale/*.po', 'tests/*.rst']),
},
classifiers=[
'Development Status :: 5 - Production/Stable',
@ -71,12 +110,17 @@ setup(name='%s_%s' % (PREFIX, MODULE),
'Natural Language :: Russian',
'Natural Language :: Spanish',
'Operating System :: OS Independent',
'Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: Implementation :: CPython',
'Programming Language :: Python :: Implementation :: PyPy',
'Topic :: Office/Business',
],
license='GPL-3',
install_requires=requires,
dependency_links=dependency_links,
zip_safe=False,
entry_points="""
[trytond.modules]
@ -85,4 +129,9 @@ setup(name='%s_%s' % (PREFIX, MODULE),
test_suite='tests',
test_loader='trytond.test_loader:Loader',
tests_require=tests_require,
use_2to3=True,
convert_2to3_doctests=[
'tests/scenario_account_invoice_discount.rst',
'tests/scenario_account_invoice_information_uom.rst',
],
)

View File

@ -1,4 +1,10 @@
# The COPYRIGHT file at the top level of this repository contains the full
# copyright notices and license terms.
from .test_product_cost_plan import suite
try:
from trytond.modules.account_invoice.tests.test_product_cost_plan import (
suite)
except ImportError:
from .test_product_cost_plan import suite
__all__ = ['suite']

View File

@ -13,44 +13,22 @@ Imports::
>>> from decimal import Decimal
>>> from proteus import config, Model, Wizard
>>> today = datetime.date.today()
Create database::
>>> config = config.set_trytond()
>>> config.pool.test = True
>>> from trytond.tests.tools import activate_modules
>>> from trytond.modules.company.tests.tools import create_company, \
... get_company
Install product_cost_plan Module::
>>> Module = Model.get('ir.module')
>>> modules = Module.find([('name', '=', 'product_cost_plan')])
>>> Module.install([x.id for x in modules], config.context)
>>> Wizard('ir.module.install_upgrade').execute('upgrade')
>>> config = activate_modules('product_cost_plan')
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()
>>> _ = create_company()
>>> company = get_company()
>>> tax_identifier = company.party.identifiers.new()
>>> tax_identifier.type = 'eu_vat'
>>> tax_identifier.code = 'BE0897290877'
>>> company.party.save()
Reload the context::
@ -64,15 +42,18 @@ Create product::
>>> ProductTemplate = Model.get('product.template')
>>> template = ProductTemplate()
>>> template.name = 'product'
>>> template.producible = True
>>> template.default_uom = unit
>>> template.type = 'goods'
>>> template.list_price = Decimal(30)
>>> template.cost_price = Decimal(20)
>>> template.save()
>>> product, = template.products
>>> product.cost_price = Decimal(20)
>>> product.save()
>>> template = ProductTemplate()
>>> template.name = 'product 2'
>>> template.producible = True
>>> template.default_uom = unit
>>> template.type = 'goods'
>>> template.list_price = Decimal(30)
@ -82,6 +63,7 @@ Create product::
>>> template = ProductTemplate()
>>> template.name = 'product 3'
>>> template.producible = True
>>> template.default_uom = unit
>>> template.type = 'goods'
>>> template.list_price = Decimal(15)
@ -98,36 +80,41 @@ Create Components::
>>> templateA.default_uom = meter
>>> templateA.type = 'goods'
>>> templateA.list_price = Decimal(2)
>>> templateA.cost_price = Decimal(1)
>>> templateA.save()
>>> componentA, = templateA.products
>>> componentA.cost_price = Decimal(1)
>>> componentA.save()
>>> 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, = templateB.products
>>> componentB.cost_price = Decimal(1)
>>> componentB.save()
>>> template1 = ProductTemplate()
>>> template1.name = 'component 1'
>>> template1.default_uom = unit
>>> template1.producible = True
>>> template1.type = 'goods'
>>> template1.list_price = Decimal(5)
>>> template1.cost_price = Decimal(2)
>>> template1.save()
>>> component1, = template1.products
>>> component1.cost_price = Decimal(2)
>>> component1.save()
>>> 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, = template2.products
>>> component2.cost_price = Decimal(5)
>>> component2.save()
Create Bill of Material::
@ -145,7 +132,7 @@ Create Bill of Material::
>>> component_bom.save()
>>> ProductBom = Model.get('product.product-production.bom')
>>> component1.boms.append(ProductBom(bom=component_bom))
>>> component1.boms.append(ProductBom (bom=component_bom))
>>> component1.save()
>>> bom = BOM(name='product')
@ -169,6 +156,7 @@ Create a cost plan from BoM without child BoMs::
>>> CostPlan = Model.get('product.cost.plan')
>>> plan = CostPlan()
>>> plan.number = '1'
>>> plan.product = product
>>> plan.bom == bom
True
@ -201,10 +189,10 @@ Create a cost plan from BoM without child BoMs::
>>> cost, = plan.costs
>>> cost.rec_name == 'Raw materials'
True
>>> cost.cost == Decimal('17.5')
True
>>> plan.cost_price == Decimal('17.5')
True
>>> cost.cost == Decimal('17.5')
True
Create a manual cost and test total cost is updated::
@ -344,4 +332,3 @@ Create BoM from Cost Plan::
True
>>> plan3.bom.outputs[0].quantity
2.0

View File

@ -12,52 +12,25 @@ Imports::
>>> from dateutil.relativedelta import relativedelta
>>> from decimal import Decimal
>>> from proteus import config, Model, Wizard
>>> from trytond.tests.tools import activate_modules
>>> today = datetime.date.today()
>>> from trytond.modules.company.tests.tools import create_company, \
... get_company
>>> today = datetime.date.today()
Create database::
>>> config = config.set_trytond()
>>> config.pool.test = True
Install product_cost_plan Module::
>>> Module = Model.get('ir.module')
>>> modules = Module.find([('name', '=', 'product_cost_plan')])
>>> Module.install([x.id for x in modules], config.context)
>>> Wizard('ir.module.install_upgrade').execute('upgrade')
Install production_external_party Module::
>>> Module = Model.get('ir.module')
>>> modules = Module.find([('name', '=', 'production_external_party')])
>>> Module.install([x.id for x in modules], config.context)
>>> Wizard('ir.module.install_upgrade').execute('upgrade')
>>> config = activate_modules(['product_cost_plan', 'production_external_party'])
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()
>>> _ = create_company()
>>> company = get_company()
>>> tax_identifier = company.party.identifiers.new()
>>> tax_identifier.type = 'eu_vat'
>>> tax_identifier.code = 'BE0897290877'
>>> company.party.save()
Reload the context::
@ -74,16 +47,18 @@ Create product::
>>> template.default_uom = unit
>>> template.type = 'goods'
>>> template.list_price = Decimal(30)
>>> template.cost_price = Decimal(20)
>>> template.producible = True
>>> template.save()
>>> product, = template.products
>>> product.cost_price = Decimal(20)
>>> product.save()
>>> template = ProductTemplate()
>>> template.name = 'product 2'
>>> template.default_uom = unit
>>> template.type = 'goods'
>>> template.list_price = Decimal(30)
>>> template.cost_price = Decimal(0)
>>> template.producible = True
>>> template.save()
>>> product2, = template.products
@ -92,7 +67,7 @@ Create product::
>>> template.default_uom = unit
>>> template.type = 'goods'
>>> template.list_price = Decimal(15)
>>> template.cost_price = Decimal(0)
>>> template.producible = True
>>> template.save()
>>> product3, = template.products
@ -105,19 +80,21 @@ Create Components::
>>> template1.default_uom = unit
>>> template1.type = 'goods'
>>> template1.list_price = Decimal(5)
>>> template1.cost_price = Decimal(2)
>>> template1.may_belong_to_party = True
>>> template1.save()
>>> component1, = template1.products
>>> component1.cost_price = Decimal(2)
>>> component1.save()
>>> 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, = template2.products
>>> component2.cost_price = Decimal(5)
>>> component2.save()
Create Bill of Material with party stock for component 1::
@ -145,6 +122,7 @@ Create a cost plan from BoM::
>>> CostPlan = Model.get('product.cost.plan')
>>> plan = CostPlan()
>>> plan.number = '1'
>>> plan.product = product
>>> plan.bom == bom
True
@ -181,8 +159,6 @@ Set party stock also for second component::
>>> for product_line in plan2.products:
... if product_line.product == component2:
... product_line.party_stock = True
... product_line.save()
... product_line.reload()
>>> plan2.save()
>>> plan2.reload()
>>> sorted([(p.quantity, p.product.rec_name, bool(p.party_stock), p.cost_price)
@ -284,4 +260,3 @@ Create BoM from Cost Plan::
True
>>> plan3.bom.outputs[0].quantity
2.0

View File

@ -5,26 +5,26 @@ import unittest
import doctest
import trytond.tests.test_tryton
from trytond.tests.test_tryton import ModuleTestCase
from trytond.tests.test_tryton import doctest_setup, doctest_teardown
from trytond.tests.test_tryton import doctest_teardown
from trytond.tests.test_tryton import doctest_checker
class TestCase(ModuleTestCase):
class ProductCostPlanTestCase(ModuleTestCase):
'Test module'
module = 'product_cost_plan'
def suite():
suite = trytond.tests.test_tryton.suite()
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestCase))
suite.addTests(doctest.DocFileSuite(
'scenario_product_cost_plan.rst',
setUp=doctest_setup, tearDown=doctest_teardown, encoding='utf-8',
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(
ProductCostPlanTestCase))
suite.addTests(doctest.DocFileSuite('scenario_product_cost_plan.rst',
tearDown=doctest_teardown, encoding='utf-8',
checker=doctest_checker,
optionflags=doctest.REPORT_ONLY_FIRST_FAILURE))
suite.addTests(doctest.DocFileSuite(
'scenario_product_cost_plan_extras_depend.rst',
setUp=doctest_setup, tearDown=doctest_teardown, encoding='utf-8',
suite.addTests(
doctest.DocFileSuite('scenario_product_cost_plan_extras_depend.rst',
tearDown=doctest_teardown, encoding='utf-8',
checker=doctest_checker,
optionflags=doctest.REPORT_ONLY_FIRST_FAILURE))
return suite

15
tox.ini Normal file
View File

@ -0,0 +1,15 @@
[tox]
envlist = {py27,py34,py35,py36}-{sqlite,postgresql},pypy-{sqlite,postgresql}
[testenv]
commands = {envpython} setup.py test
deps =
{py27,py34,py35,py36}-postgresql: psycopg2 >= 2.5
pypy-postgresql: psycopg2cffi >= 2.5
sqlite: sqlitebck
setenv =
sqlite: TRYTOND_DATABASE_URI={env:SQLITE_URI:sqlite://}
postgresql: TRYTOND_DATABASE_URI={env:POSTGRESQL_URI:postgresql://}
sqlite: DB_NAME={env:SQLITE_NAME::memory:}
postgresql: DB_NAME={env:POSTGRESQL_NAME:test}
install_command = pip install --pre --process-dependency-links {opts} {packages}

View File

@ -1,5 +1,6 @@
[tryton]
version=4.1.0
version=4.8.0
depends:
production
extras_depend:

View File

@ -1,7 +1,7 @@
<?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">
<form>
<label name="plan"/>
<field name="plan"/>
<newline/>

View File

@ -1,7 +1,7 @@
<?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 BOM" editable="bottom">
<tree editable="bottom">
<field name="plan"/>
<field name="product"/>
<field name="bom"/>

View File

@ -1,7 +1,7 @@
<?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" col="6" cursor="product">
<form col="6" cursor="product">
<label name="number"/>
<field name="number"/>
<label name="name"/>

View File

@ -1,7 +1,7 @@
<?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">
<tree>
<field name="number"/>
<field name="name"/>
<field name="product" tree_invisible="1"/>

View File

@ -1,7 +1,7 @@
<?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 Product Line">
<form>
<label name="plan"/>
<field name="plan"/>
<newline/>

View File

@ -1,7 +1,7 @@
<?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 Product Line" editable="bottom" sequence="sequence">
<tree editable="bottom" sequence="sequence">
<field name="name"/>
<field name="product"/>
<field name="plan"/>

View File

@ -1,7 +1,7 @@
<?xml version="1.0"?>
<!-- The COPYRIGHT file at the top level of this repository contains the full
copyright notices and license terms. -->
<form string="Create BOM">
<form>
<label name="name"/>
<field name="name" colspan="3"/>
</form>

View File

@ -1,7 +1,7 @@
<?xml version="1.0"?>
<!-- The COPYRIGHT file at the top level of this repository contains the full
copyright notices and license terms. -->
<form string="Plan Cost">
<form>
<label name="plan"/>
<field name="plan"/>
<newline/>

View File

@ -1,7 +1,7 @@
<?xml version="1.0"?>
<!-- The COPYRIGHT file at the top level of this repository contains the full
copyright notices and license terms. -->
<tree string="Plan Cost" editable="bottom" sequence="sequence">
<tree editable="bottom" sequence="sequence">
<field name="plan"/>
<field name="type"/>
<field name="cost"/>

View File

@ -1,7 +1,7 @@
<?xml version="1.0"?>
<!-- The COPYRIGHT file at the top level of this repository contains the full
copyright notices and license terms. -->
<form string="Plan Cost Type">
<form>
<label name="name"/>
<field name="name"/>
<label name="system"/>

View File

@ -1,7 +1,7 @@
<?xml version="1.0"?>
<!-- The COPYRIGHT file at the top level of this repository contains the full
copyright notices and license terms. -->
<tree string="Plan Cost Type">
<tree>
<field name="name"/>
<field name="system"/>
</tree>