diff --git a/.drone.yml b/.drone.yml
new file mode 100644
index 0000000..897fc2e
--- /dev/null
+++ b/.drone.yml
@@ -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
diff --git a/__init__.py b/__init__.py
index d76e019..266c447 100644
--- a/__init__.py
+++ b/__init__.py
@@ -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')
diff --git a/configuration.py b/configuration.py
index 5a157e5..95a9314 100644
--- a/configuration.py
+++ b/configuration.py
@@ -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)
diff --git a/configuration.xml b/configuration.xml
index a0c67d9..a4748ca 100644
--- a/configuration.xml
+++ b/configuration.xml
@@ -5,15 +5,8 @@ this repository contains the full copyright notices and license terms. -->
production.configuration
-
-
configuration_form
-
-
-
-
-
+
diff --git a/locale/ca_ES.po b/locale/ca.po
similarity index 100%
rename from locale/ca_ES.po
rename to locale/ca.po
diff --git a/locale/es_ES.po b/locale/es.po
similarity index 100%
rename from locale/es_ES.po
rename to locale/es.po
diff --git a/plan.py b/plan.py
index 38834dd..56d17af 100644
--- a/plan.py
+++ b/plan.py
@@ -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)
diff --git a/setup.py b/setup.py
index fd8ee51..6db88a2 100644
--- a/setup.py
+++ b/setup.py
@@ -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',
+ ],
)
diff --git a/tests/__init__.py b/tests/__init__.py
index 063097f..2826ac6 100644
--- a/tests/__init__.py
+++ b/tests/__init__.py
@@ -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']
diff --git a/tests/scenario_product_cost_plan.rst b/tests/scenario_product_cost_plan.rst
index bcc48bd..6f00fc8 100644
--- a/tests/scenario_product_cost_plan.rst
+++ b/tests/scenario_product_cost_plan.rst
@@ -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
-
diff --git a/tests/scenario_product_cost_plan_extras_depend.rst b/tests/scenario_product_cost_plan_extras_depend.rst
index 68d5922..666b016 100644
--- a/tests/scenario_product_cost_plan_extras_depend.rst
+++ b/tests/scenario_product_cost_plan_extras_depend.rst
@@ -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
-
diff --git a/tests/test_product_cost_plan.py b/tests/test_product_cost_plan.py
index d898639..61640f5 100644
--- a/tests/test_product_cost_plan.py
+++ b/tests/test_product_cost_plan.py
@@ -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
diff --git a/tox.ini b/tox.ini
new file mode 100644
index 0000000..2082ab4
--- /dev/null
+++ b/tox.ini
@@ -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}
diff --git a/tryton.cfg b/tryton.cfg
index cb38d02..1a5335b 100644
--- a/tryton.cfg
+++ b/tryton.cfg
@@ -1,5 +1,6 @@
[tryton]
-version=4.1.0
+version=4.8.0
+
depends:
production
extras_depend:
diff --git a/view/cost_plan_bom_line_form.xml b/view/cost_plan_bom_line_form.xml
index c5971a8..32ebeac 100644
--- a/view/cost_plan_bom_line_form.xml
+++ b/view/cost_plan_bom_line_form.xml
@@ -1,7 +1,7 @@
-