initial commit
This commit is contained in:
commit
46bb29b659
|
@ -0,0 +1,14 @@
|
||||||
|
Copyright (C) 2014 NaN·tic
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
@ -0,0 +1,27 @@
|
||||||
|
Installing
|
||||||
|
==========
|
||||||
|
|
||||||
|
Prerequisites
|
||||||
|
-------------
|
||||||
|
|
||||||
|
* see setup.py file
|
||||||
|
|
||||||
|
Installation
|
||||||
|
------------
|
||||||
|
|
||||||
|
Once you've downloaded and unpacked the source release, enter the
|
||||||
|
directory where the archive was unpacked, and run:
|
||||||
|
|
||||||
|
python setup.py install
|
||||||
|
|
||||||
|
Note that you may need administrator/root privileges for this step, as
|
||||||
|
this command will by default attempt to install module to the Python
|
||||||
|
site-packages directory on your system.
|
||||||
|
|
||||||
|
For advanced options, please refer to the easy_install and/or the distutils
|
||||||
|
documentation:
|
||||||
|
|
||||||
|
http://peak.telecommunity.com/DevCenter/EasyInstall
|
||||||
|
http://docs.python.org/inst/inst.html
|
||||||
|
|
||||||
|
To use without installation, extract the archive into ``trytond/modules``.
|
|
@ -0,0 +1,13 @@
|
||||||
|
include INSTALL
|
||||||
|
include README
|
||||||
|
include COPYRIGHT
|
||||||
|
include CHANGELOG
|
||||||
|
include LICENSE
|
||||||
|
include tryton.cfg
|
||||||
|
include *.xml
|
||||||
|
include view/*.xml
|
||||||
|
include *.odt
|
||||||
|
include locale/*.po
|
||||||
|
include doc/*
|
||||||
|
include icons/*
|
||||||
|
include tests/*.rst
|
|
@ -0,0 +1,14 @@
|
||||||
|
# The COPYRIGHT file at the top level of this repository contains the full
|
||||||
|
# copyright notices and license terms.
|
||||||
|
from trytond.pool import Pool
|
||||||
|
from .production import *
|
||||||
|
|
||||||
|
|
||||||
|
def register():
|
||||||
|
Pool.register(
|
||||||
|
Production,
|
||||||
|
SplitProductionStart,
|
||||||
|
module='production_split_serial_number', type_='model')
|
||||||
|
Pool.register(
|
||||||
|
SplitProduction,
|
||||||
|
module='production_split_serial_number', type_='wizard')
|
|
@ -0,0 +1,7 @@
|
||||||
|
#:inside:production/production:section:split_production#
|
||||||
|
|
||||||
|
Si el producto esta marcado como |serial_number| se creará automáticamente
|
||||||
|
un lote para las salidas de cada una de las producciones resultantes.
|
||||||
|
|
||||||
|
|
||||||
|
.. |serial_number| field:: product.template/serial_number
|
|
@ -0,0 +1,7 @@
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr "Content-Type: text/plain; charset=utf-8\n"
|
||||||
|
|
||||||
|
msgctxt "field:production.split.start,quantity_readonly:"
|
||||||
|
msgid "Quantity Readonly"
|
||||||
|
msgstr "Quantitat només lectura"
|
|
@ -0,0 +1,7 @@
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr "Content-Type: text/plain; charset=utf-8\n"
|
||||||
|
|
||||||
|
msgctxt "field:production.split.start,quantity_readonly:"
|
||||||
|
msgid "Quantity Readonly"
|
||||||
|
msgstr "Cantitat solo lectura"
|
|
@ -0,0 +1,63 @@
|
||||||
|
# The COPYRIGHT file at the top level of this repository contains the full
|
||||||
|
# copyright notices and license terms.
|
||||||
|
from trytond.model import fields
|
||||||
|
from trytond.pool import Pool, PoolMeta
|
||||||
|
from trytond.pyson import Eval
|
||||||
|
from trytond.transaction import Transaction
|
||||||
|
|
||||||
|
__all__ = ['Production', 'SplitProductionStart', 'SplitProduction']
|
||||||
|
__metaclass__ = PoolMeta
|
||||||
|
|
||||||
|
|
||||||
|
class Production:
|
||||||
|
__name__ = 'production'
|
||||||
|
|
||||||
|
def _split_inputs_outputs(self, factor):
|
||||||
|
pool = Pool()
|
||||||
|
Lot = pool.get('stock.lot')
|
||||||
|
super(Production, self)._split_inputs_outputs(factor)
|
||||||
|
for output in self.outputs:
|
||||||
|
if not output.product.serial_number:
|
||||||
|
continue
|
||||||
|
if output.quantity != 1.0 or output.lot:
|
||||||
|
continue
|
||||||
|
if hasattr(output, 'get_production_output_lot'):
|
||||||
|
lot = output.get_production_output_lot()
|
||||||
|
lot.save()
|
||||||
|
else:
|
||||||
|
lot = Lot(product=output.product)
|
||||||
|
lot.save()
|
||||||
|
if lot:
|
||||||
|
output.lot = lot
|
||||||
|
output.save()
|
||||||
|
|
||||||
|
|
||||||
|
class SplitProductionStart:
|
||||||
|
__name__ = 'production.split.start'
|
||||||
|
|
||||||
|
quantity_readonly = fields.Boolean('Quantity Readonly')
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def __setup__(cls):
|
||||||
|
super(SplitProductionStart, cls).__setup__()
|
||||||
|
if 'quantity_readonly' not in cls.quantity.depends:
|
||||||
|
readonly = cls.quantity.states.get('readonly', False)
|
||||||
|
cls.quantity.states.update({
|
||||||
|
'readonly': Eval('quantity_readonly') | readonly,
|
||||||
|
})
|
||||||
|
cls.quantity.depends.append('quantity_readonly')
|
||||||
|
|
||||||
|
|
||||||
|
class SplitProduction:
|
||||||
|
'Split Production'
|
||||||
|
__name__ = 'production.split'
|
||||||
|
|
||||||
|
def default_start(self, fields):
|
||||||
|
pool = Pool()
|
||||||
|
Production = pool.get('production')
|
||||||
|
default = super(SplitProduction, self).default_start(fields)
|
||||||
|
production = Production(Transaction().context['active_id'])
|
||||||
|
if production.product and production.product.serial_number:
|
||||||
|
default['quantity'] = 1.0
|
||||||
|
default['quantity_readonly'] = True
|
||||||
|
return default
|
|
@ -0,0 +1,99 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# encoding: utf-8
|
||||||
|
|
||||||
|
from setuptools import setup
|
||||||
|
import re
|
||||||
|
import os
|
||||||
|
import ConfigParser
|
||||||
|
|
||||||
|
MODULE = 'production_split_serial_number'
|
||||||
|
PREFIX = 'nantic'
|
||||||
|
MODULE2PREFIX = {}
|
||||||
|
|
||||||
|
|
||||||
|
def read(fname):
|
||||||
|
return open(os.path.join(os.path.dirname(__file__), fname)).read()
|
||||||
|
|
||||||
|
|
||||||
|
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.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()
|
||||||
|
|
||||||
|
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):
|
||||||
|
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(get_require_version('trytond'))
|
||||||
|
|
||||||
|
tests_require = [get_require_version('proteus')]
|
||||||
|
|
||||||
|
setup(name='%s_%s' % (PREFIX, MODULE),
|
||||||
|
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: '.'},
|
||||||
|
packages=[
|
||||||
|
'trytond.modules.%s' % MODULE,
|
||||||
|
'trytond.modules.%s.tests' % MODULE,
|
||||||
|
],
|
||||||
|
package_data={
|
||||||
|
'trytond.modules.%s' % MODULE: (info.get('xml', [])
|
||||||
|
+ ['tryton.cfg', 'locale/*.po', 'tests/*.rst']),
|
||||||
|
},
|
||||||
|
classifiers=[
|
||||||
|
'Development Status :: 5 - Production/Stable',
|
||||||
|
'Environment :: Plugins',
|
||||||
|
'Framework :: Tryton',
|
||||||
|
'Intended Audience :: Developers',
|
||||||
|
'Intended Audience :: Financial and Insurance Industry',
|
||||||
|
'Intended Audience :: Legal Industry',
|
||||||
|
'License :: OSI Approved :: GNU General Public License (GPL)',
|
||||||
|
'Natural Language :: Bulgarian',
|
||||||
|
'Natural Language :: Catalan',
|
||||||
|
'Natural Language :: Czech',
|
||||||
|
'Natural Language :: Dutch',
|
||||||
|
'Natural Language :: English',
|
||||||
|
'Natural Language :: French',
|
||||||
|
'Natural Language :: German',
|
||||||
|
'Natural Language :: Russian',
|
||||||
|
'Natural Language :: Spanish',
|
||||||
|
'Operating System :: OS Independent',
|
||||||
|
'Programming Language :: Python :: 2.6',
|
||||||
|
'Programming Language :: Python :: 2.7',
|
||||||
|
'Topic :: Office/Business',
|
||||||
|
],
|
||||||
|
license='GPL-3',
|
||||||
|
install_requires=requires,
|
||||||
|
zip_safe=False,
|
||||||
|
entry_points="""
|
||||||
|
[trytond.modules]
|
||||||
|
%s = trytond.modules.%s
|
||||||
|
""" % (MODULE, MODULE),
|
||||||
|
test_suite='tests',
|
||||||
|
test_loader='trytond.test_loader:Loader',
|
||||||
|
tests_require=tests_require,
|
||||||
|
)
|
|
@ -0,0 +1,3 @@
|
||||||
|
# The COPYRIGHT file at the top level of this repository contains the full
|
||||||
|
# copyright notices and license terms.
|
||||||
|
from .test_production_split_serial_number import suite
|
|
@ -0,0 +1,178 @@
|
||||||
|
=======================================
|
||||||
|
Production Split Serial Number Scenario
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
Imports::
|
||||||
|
|
||||||
|
>>> import datetime
|
||||||
|
>>> from dateutil.relativedelta import relativedelta
|
||||||
|
>>> from decimal import Decimal
|
||||||
|
>>> from proteus import config, Model, Wizard
|
||||||
|
>>> today = datetime.date.today()
|
||||||
|
>>> yesterday = today - relativedelta(days=1)
|
||||||
|
|
||||||
|
Create database::
|
||||||
|
|
||||||
|
>>> config = config.set_trytond()
|
||||||
|
>>> config.pool.test = True
|
||||||
|
|
||||||
|
Install production Module::
|
||||||
|
|
||||||
|
>>> Module = Model.get('ir.module.module')
|
||||||
|
>>> module, = Module.find([
|
||||||
|
... ('name', '=', 'production_split_serial_number')])
|
||||||
|
>>> module.click('install')
|
||||||
|
>>> 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)
|
||||||
|
|
||||||
|
Create product::
|
||||||
|
|
||||||
|
>>> Sequence = Model.get('ir.sequence')
|
||||||
|
>>> 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.serial_number = True
|
||||||
|
>>> product_sequence = Sequence(code='stock.lot', name='Product Sequence')
|
||||||
|
>>> product_sequence.save()
|
||||||
|
>>> template.lot_sequence = product_sequence
|
||||||
|
>>> 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')
|
||||||
|
>>> Location = Model.get('stock.location')
|
||||||
|
>>> 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 = 200
|
||||||
|
>>> inventory_line2 = InventoryLine()
|
||||||
|
>>> inventory.lines.append(inventory_line2)
|
||||||
|
>>> inventory_line2.product = component2
|
||||||
|
>>> inventory_line2.quantity = 60
|
||||||
|
>>> inventory.save()
|
||||||
|
>>> Inventory.confirm([inventory.id], config.context)
|
||||||
|
>>> inventory.state
|
||||||
|
u'done'
|
||||||
|
|
||||||
|
Make a production::
|
||||||
|
|
||||||
|
>>> Production = Model.get('production')
|
||||||
|
>>> production = Production()
|
||||||
|
>>> production.product = product
|
||||||
|
>>> production.bom = bom
|
||||||
|
>>> production.quantity = 4
|
||||||
|
>>> production.save()
|
||||||
|
>>> split_production = Wizard('production.split', [production])
|
||||||
|
>>> split_production.form.quantity
|
||||||
|
1.0
|
||||||
|
>>> split_production.form.count = 2
|
||||||
|
>>> split_production.execute('split')
|
||||||
|
>>> productions = Production.find([])
|
||||||
|
>>> len(productions)
|
||||||
|
3
|
||||||
|
>>> lots = [o.lot for p in productions for o in p.outputs if o.lot]
|
||||||
|
>>> lot1, lot2 = sorted(lots, key=lambda a: int(a.number))
|
||||||
|
>>> lot1.number
|
||||||
|
u'1'
|
||||||
|
>>> lot2.number
|
||||||
|
u'2'
|
||||||
|
>>> product_sequence.reload()
|
||||||
|
>>> product_sequence.number_next
|
||||||
|
3
|
|
@ -0,0 +1,187 @@
|
||||||
|
=======================================================
|
||||||
|
Production Split Serial With Output Lot Number Scenario
|
||||||
|
=======================================================
|
||||||
|
|
||||||
|
Imports::
|
||||||
|
|
||||||
|
>>> import datetime
|
||||||
|
>>> from dateutil.relativedelta import relativedelta
|
||||||
|
>>> from decimal import Decimal
|
||||||
|
>>> from proteus import config, Model, Wizard
|
||||||
|
>>> today = datetime.date.today()
|
||||||
|
>>> yesterday = today - relativedelta(days=1)
|
||||||
|
|
||||||
|
Create database::
|
||||||
|
|
||||||
|
>>> config = config.set_trytond()
|
||||||
|
>>> config.pool.test = True
|
||||||
|
|
||||||
|
Install production Module::
|
||||||
|
|
||||||
|
>>> Module = Model.get('ir.module.module')
|
||||||
|
>>> modules = Module.find([
|
||||||
|
... ('name', 'in', ['production_split_serial_number',
|
||||||
|
... 'production_output_lot'])])
|
||||||
|
>>> Module.install([m.id for m 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)
|
||||||
|
|
||||||
|
Create product::
|
||||||
|
|
||||||
|
>>> ProductUom = Model.get('product.uom')
|
||||||
|
>>> tType = Model.get('stock.lot.type')
|
||||||
|
>>> 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.serial_number = True
|
||||||
|
>>> 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')
|
||||||
|
>>> Location = Model.get('stock.location')
|
||||||
|
>>> 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 = 200
|
||||||
|
>>> inventory_line2 = InventoryLine()
|
||||||
|
>>> inventory.lines.append(inventory_line2)
|
||||||
|
>>> inventory_line2.product = component2
|
||||||
|
>>> inventory_line2.quantity = 60
|
||||||
|
>>> inventory.save()
|
||||||
|
>>> Inventory.confirm([inventory.id], config.context)
|
||||||
|
>>> inventory.state
|
||||||
|
u'done'
|
||||||
|
|
||||||
|
Configure production sequence::
|
||||||
|
|
||||||
|
>>> Sequence = Model.get('ir.sequence')
|
||||||
|
>>> Config = Model.get('production.configuration')
|
||||||
|
>>> config = Config()
|
||||||
|
>>> config.output_lot_creation = 'done'
|
||||||
|
>>> output_sequence = Sequence(code='stock.lot', name='Output Sequence')
|
||||||
|
>>> output_sequence.save()
|
||||||
|
>>> config.output_lot_sequence = output_sequence
|
||||||
|
>>> config.save()
|
||||||
|
|
||||||
|
Make a production::
|
||||||
|
|
||||||
|
>>> Production = Model.get('production')
|
||||||
|
>>> production = Production()
|
||||||
|
>>> production.product = product
|
||||||
|
>>> production.bom = bom
|
||||||
|
>>> production.quantity = 4
|
||||||
|
>>> production.save()
|
||||||
|
>>> split_production = Wizard('production.split', [production])
|
||||||
|
>>> split_production.form.quantity
|
||||||
|
1.0
|
||||||
|
>>> split_production.form.count = 2
|
||||||
|
>>> split_production.execute('split')
|
||||||
|
>>> productions = Production.find([])
|
||||||
|
>>> len(productions)
|
||||||
|
3
|
||||||
|
>>> lots = [o.lot for p in productions for o in p.outputs if o.lot]
|
||||||
|
>>> lot1, lot2 = sorted(lots, key=lambda a: int(a.number))
|
||||||
|
>>> lot1.number
|
||||||
|
u'1'
|
||||||
|
>>> lot2.number
|
||||||
|
u'2'
|
||||||
|
>>> output_sequence.reload()
|
||||||
|
>>> output_sequence.number_next
|
||||||
|
3
|
|
@ -0,0 +1,33 @@
|
||||||
|
# The COPYRIGHT file at the top level of this repository contains the full
|
||||||
|
# copyright notices and license terms.
|
||||||
|
import unittest
|
||||||
|
import doctest
|
||||||
|
import trytond.tests.test_tryton
|
||||||
|
from trytond.tests.test_tryton import test_depends
|
||||||
|
from trytond.tests.test_tryton import doctest_setup, doctest_teardown
|
||||||
|
|
||||||
|
|
||||||
|
class TestCase(unittest.TestCase):
|
||||||
|
'Test module'
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
trytond.tests.test_tryton.install_module(
|
||||||
|
'production_split_serial_number')
|
||||||
|
|
||||||
|
def test0006depends(self):
|
||||||
|
'Test depends'
|
||||||
|
test_depends()
|
||||||
|
|
||||||
|
|
||||||
|
def suite():
|
||||||
|
suite = trytond.tests.test_tryton.suite()
|
||||||
|
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestCase))
|
||||||
|
suite.addTests(doctest.DocFileSuite(
|
||||||
|
'scenario_production_split_serial_number.rst',
|
||||||
|
setUp=doctest_setup, tearDown=doctest_teardown, encoding='utf-8',
|
||||||
|
optionflags=doctest.REPORT_ONLY_FIRST_FAILURE))
|
||||||
|
suite.addTests(doctest.DocFileSuite(
|
||||||
|
'scenario_production_split_serial_number_output_lot.rst',
|
||||||
|
setUp=doctest_setup, tearDown=doctest_teardown, encoding='utf-8',
|
||||||
|
optionflags=doctest.REPORT_ONLY_FIRST_FAILURE))
|
||||||
|
return suite
|
|
@ -0,0 +1,9 @@
|
||||||
|
[tryton]
|
||||||
|
version=3.4.0
|
||||||
|
depends:
|
||||||
|
stock_serial_number
|
||||||
|
stock_lot_sequence
|
||||||
|
production_split
|
||||||
|
extras_depend:
|
||||||
|
production_output_lot
|
||||||
|
xml:
|
Loading…
Reference in New Issue