mirror of
https://github.com/OCA/stock-logistics-warehouse.git
synced 2025-01-21 14:27:28 +02:00
41
.travis.yml
41
.travis.yml
@@ -3,42 +3,25 @@
|
||||
language: python
|
||||
|
||||
python:
|
||||
# - "pypy" # not supported by odoo 8
|
||||
# - "3.4" # not supported by odoo 8
|
||||
# - "3.3" # not supported by odoo 8
|
||||
- "2.7"
|
||||
# - "2.6" # not supported by odoo 8
|
||||
|
||||
env:
|
||||
- ODOO="https://github.com/savoirfairelinux/odoo/archive/setuptools-addons.tar.gz" # Temp until https://github.com/odoo/odoo/issues/185 or https://github.com/odoo/odoo/issues/441 is fixed
|
||||
# - ODOO="https://github.com/odoo/odoo/archive/master.tar.gz"
|
||||
# - ODOO="https://github.com/OCA/OCB/archive/master.zip"
|
||||
- VERSION="8.0" ODOO_REPO="odoo/odoo"
|
||||
- VERSION="8.0" ODOO_REPO="OCA/OCB"
|
||||
|
||||
# Need coveralls for coverage reports
|
||||
# Need flake8 for pep8 testing
|
||||
# Manually get PyChart
|
||||
# Install tested version of odoo (official or ocb)
|
||||
# Get modules from other repos which have dependencies (in this case travel requires modules from lp:partner-contact-management and lp:openerp-hr
|
||||
install:
|
||||
- pip install coveralls flake8
|
||||
- pip install http://download.gna.org/pychart/PyChart-1.39.tar.gz
|
||||
- pip install ${ODOO}
|
||||
virtualenv:
|
||||
system_site_packages: true
|
||||
|
||||
# Create databae
|
||||
# Pre-install modules and dependencies
|
||||
before_script:
|
||||
- createdb test
|
||||
install:
|
||||
- git clone https://github.com/OCA/maintainer-quality-tools.git ${HOME}/maintainer-quality-tools
|
||||
- export PATH=${HOME}/maintainer-quality-tools/travis:${PATH}
|
||||
- travis_install_nightly
|
||||
- git clone https://github.com/OCA/stock-logistics-tracking -b ${VERSION} $HOME/stock-logistics-tracking
|
||||
- git clone https://github.com/OCA/stock-logistics-barcode -b ${VERSION} $HOME/stock-logistics-barcode
|
||||
|
||||
# Test with flake, ignore F401 for __init__.py files, use a max length of 120
|
||||
# Run tests with coverage
|
||||
# Only test modules in repo (list populated by directories in repo)
|
||||
# Preload modules before testing to only run tests of repo's modules
|
||||
# Include current directory and dependent repos in addons-path as well as official addons
|
||||
script:
|
||||
- flake8 . --max-line-length=120 --filename=__init__.py --ignore=F401
|
||||
- flake8 . --max-line-length=120 --exclude=__init__.py
|
||||
- odoo.py -d test --stop-after-init --init=$(python -c 'import os; print(",".join(x for x in os.listdir(".") if os.path.isdir(x) and not x.startswith(".") and not x.endswith(".unported")))') --addons-path=$(pwd),`python -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())"`/addons
|
||||
- coverage run $(which odoo.py) -d test --test-enable --log-level=test --stop-after-init --init=$(python -c 'import os; print(",".join(x for x in os.listdir(".") if os.path.isdir(x) and not x.startswith(".") and not x.endswith("_unported")))') --addons-path=$(pwd),`python -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())"`/addons
|
||||
- travis_run_flake8
|
||||
- travis_run_tests
|
||||
|
||||
after_success:
|
||||
coveralls
|
||||
|
||||
@@ -33,7 +33,7 @@ Immediately usable is computed : Quantity on Hand - Outgoing Stock.
|
||||
""",
|
||||
"website": "http://tinyerp.com/module_account.html",
|
||||
"category": "Generic Modules/Stock",
|
||||
"data": ["product_view.xml",
|
||||
"data": ["product_view.xml",
|
||||
],
|
||||
"active": False,
|
||||
'installable': False
|
||||
|
||||
@@ -137,6 +137,6 @@ class product_immediately_usable(orm.Model):
|
||||
type='float',
|
||||
string='Immediately Usable',
|
||||
multi='qty_available',
|
||||
help="Quantity of products really available for sale." \
|
||||
help="Quantity of products really available for sale."
|
||||
"Computed as: Quantity On Hand - Outgoing."),
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
##############################################################################
|
||||
|
||||
from openerp.osv import fields, orm
|
||||
from openerp.tools.translate import _
|
||||
|
||||
|
||||
class product_product(orm.Model):
|
||||
|
||||
@@ -23,7 +23,8 @@
|
||||
'version': '0.1',
|
||||
'category': 'Warehouse Management',
|
||||
'description': """
|
||||
You can choose which stock moves have to generate inventory valuation accounting entries, by specifying it in the location form.
|
||||
You can choose which stock moves have to generate inventory valuation
|
||||
accounting entries, by specifying it in the location form.
|
||||
""",
|
||||
'author': 'Agile Business Group',
|
||||
'website': 'http://www.agilebg.com',
|
||||
|
||||
@@ -19,13 +19,16 @@
|
||||
##############################################################################
|
||||
|
||||
from openerp.osv import fields, orm
|
||||
from openerp.tools.translate import _
|
||||
|
||||
|
||||
class stock_location(orm.Model):
|
||||
_inherit = "stock.location"
|
||||
_columns = {
|
||||
'consider_internal': fields.boolean('Consider internal', help="Consider as internal location for inventory valuation: stock moves from internal to internal will not generate accounting entries"),
|
||||
'consider_internal': fields.boolean(
|
||||
'Consider internal',
|
||||
help="Consider as internal location for inventory valuation: "
|
||||
"stock moves from internal to internal will not generate "
|
||||
"accounting entries"),
|
||||
}
|
||||
|
||||
|
||||
@@ -33,14 +36,18 @@ class stock_move(orm.Model):
|
||||
_inherit = "stock.move"
|
||||
|
||||
def _create_product_valuation_moves(self, cr, uid, move, context=None):
|
||||
if (move.location_id.company_id and move.location_dest_id.company_id
|
||||
and move.location_id.company_id != move.location_dest_id.company_id):
|
||||
return super(stock_move, self)._create_product_valuation_moves(
|
||||
_super = super(stock_move, self)
|
||||
location = move.location_id
|
||||
location_dest = move.location_dest_id
|
||||
if (location.company_id
|
||||
and location_dest.company_id
|
||||
and location.company_id != location_dest.company_id):
|
||||
return _super._create_product_valuation_moves(
|
||||
cr, uid, move, context=context)
|
||||
if (move.location_id.usage == 'internal' or
|
||||
move.location_id.consider_internal) and (
|
||||
move.location_dest_id.usage == 'internal' or
|
||||
move.location_dest_id.consider_internal):
|
||||
return
|
||||
return super(stock_move, self)._create_product_valuation_moves(
|
||||
return _super._create_product_valuation_moves(
|
||||
cr, uid, move, context=context)
|
||||
|
||||
@@ -24,27 +24,37 @@
|
||||
'version': '0.2',
|
||||
'category': 'Tools',
|
||||
'description': """
|
||||
This module allows to improve reordering rules of stock module.
|
||||
|
||||
It works forecasting the stock needed per product for n days of sales, with the next formula:
|
||||
(( Qty sold in days_stats * (1+forecast_gap)) / days_stats * days_warehouse)
|
||||
where:
|
||||
- days_stats = days on wich calculate sales stats;
|
||||
- forecast_gap = forecast of increase/decrease on sales (%);
|
||||
- days_warehouse = days of stock to keep in the warehouse.
|
||||
This module allows to improve reordering rules of stock module.
|
||||
|
||||
Usage:
|
||||
insert days_stats, forecast_gap and days_warehouse vars in product form
|
||||
and create a reordering rule for the same product, without inserting nothing (neither maximum or
|
||||
minimum quantity are required). The cron job will be executed daily and will update the maximum
|
||||
quantity in the reordering rule (you can force it to start changing the date and hour of
|
||||
execution).
|
||||
This module doesn't need purchase module to work, but it's useful with that module.'""",
|
||||
It works forecasting the stock needed per product for n days of sales, with
|
||||
the following formula:
|
||||
|
||||
(( Qty sold in days_stats * (1+forecast_gap)) / days_stats * days_warehouse)
|
||||
|
||||
where:
|
||||
- days_stats = days on wich calculate sales stats;
|
||||
- forecast_gap = forecast of increase/decrease on sales (%);
|
||||
- days_warehouse = days of stock to keep in the warehouse.
|
||||
|
||||
Usage:
|
||||
|
||||
insert days_stats, forecast_gap and days_warehouse vars in product form and
|
||||
create a reordering rule for the same product, without inserting nothing
|
||||
(neither maximum or minimum quantity are required). The cron job will be
|
||||
executed daily and will update the maximum quantity in the reordering rule
|
||||
(you can force it to start changing the date and hour of execution).
|
||||
|
||||
This module doesn't need purchase module to work, but it's useful with that
|
||||
module.""",
|
||||
'author': 'Sergio Corato',
|
||||
'website': 'http://www.icstools.it',
|
||||
'depends': ['procurement','sale',],
|
||||
'demo_xml' : [],
|
||||
'data': ['stock_reord_rule_view.xml','cron_data.xml',],
|
||||
'depends': ['procurement',
|
||||
'sale',
|
||||
],
|
||||
'demo_xml': [],
|
||||
'data': ['stock_reord_rule_view.xml',
|
||||
'cron_data.xml',
|
||||
],
|
||||
'images': [],
|
||||
'active': False,
|
||||
'installable': False,
|
||||
|
||||
@@ -21,39 +21,58 @@
|
||||
|
||||
from openerp.osv import orm, fields
|
||||
|
||||
|
||||
class stock_warehouse_orderpoint(orm.Model):
|
||||
_inherit = "stock.warehouse.orderpoint"
|
||||
|
||||
def _qty_orderpoint_days(self, cr, uid, ids, context=None):
|
||||
"""Calculate quantity to create warehouse stock for n days of sales.
|
||||
(( Qty sold in days_stats * (1+forecast_gap)) / days_stats * days_warehouse)"""
|
||||
Qty sold in days_stats * (1+forecast_gap)
|
||||
/ days_stats * days_warehouse)
|
||||
"""
|
||||
|
||||
obj_product = self.pool.get('product.product')
|
||||
product_ids = tuple(obj_product.search(cr, uid, [], context=context))
|
||||
sql= """SELECT sol.product_id AS product_id,
|
||||
(sum( product_uos_qty )/pp.days_stats*(1+pp.forecast_gap/100) * pp.days_warehouse)
|
||||
AS quantity FROM sale_order_line sol JOIN sale_order so ON so.id = sol.order_id
|
||||
JOIN product_product pp ON pp.id = sol.product_id
|
||||
sql = """
|
||||
SELECT sol.product_id AS product_id,
|
||||
(sum(product_uos_qty) / pp.days_stats *
|
||||
(1 + pp.forecast_gap / 100) * pp.days_warehouse)
|
||||
AS quantity
|
||||
FROM sale_order_line sol
|
||||
JOIN sale_order so ON so.id = sol.order_id
|
||||
JOIN product_product pp ON pp.id = sol.product_id
|
||||
JOIN product_template pt ON pt.id = pp.product_tmpl_id
|
||||
WHERE sol.state in ('done','confirmed') AND pt.type = 'product'
|
||||
AND sol.product_id IN %s AND date_order > (date(now()) - pp.days_stats)
|
||||
GROUP BY sol.product_uom, sol.product_id, pp.days_stats, pp.forecast_gap,
|
||||
pp.days_warehouse;"""
|
||||
AND sol.product_id IN %s AND date_order > (date(now()) - pp.days_stats)
|
||||
GROUP BY sol.product_uom,
|
||||
sol.product_id,
|
||||
pp.days_stats,
|
||||
pp.forecast_gap,
|
||||
pp.days_warehouse;
|
||||
"""
|
||||
cr.execute(sql, (product_ids,))
|
||||
sql_res = cr.fetchall()
|
||||
if sql_res:
|
||||
for val in sql_res:
|
||||
if val:
|
||||
reord_rules_ids = self.search(cr, uid, [('product_id', '=', val[0])], context=context)
|
||||
domain = [('product_id', '=', val[0])]
|
||||
reord_rules_ids = self.search(cr, uid,
|
||||
domain,
|
||||
context=context)
|
||||
if reord_rules_ids:
|
||||
self.write(cr, uid, reord_rules_ids, {'product_max_qty': val[1]}, context=context)
|
||||
self.write(cr, uid,
|
||||
reord_rules_ids,
|
||||
{'product_max_qty': val[1]},
|
||||
context=context)
|
||||
return True
|
||||
|
||||
|
||||
class product_product(orm.Model):
|
||||
_inherit = "product.product"
|
||||
|
||||
_columns = {
|
||||
'days_warehouse': fields.integer('Days of needed warehouse stock'),
|
||||
'days_stats': fields.integer('Days of sale statistics'),
|
||||
'forecast_gap': fields.float('Expected sales variation (percent +/-)', digits=(6,3)),
|
||||
'forecast_gap': fields.float('Expected sales variation (percent +/-)',
|
||||
digits=(6, 3)),
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
from openerp.osv import orm, fields
|
||||
from openerp.osv import orm
|
||||
|
||||
|
||||
class product_product(orm.Model):
|
||||
|
||||
@@ -134,7 +134,9 @@ class stock_reservation(orm.Model):
|
||||
return super(stock_reservation, self).unlink(cr, uid, ids,
|
||||
context=context)
|
||||
|
||||
def onchange_product_id(self, cr, uid, ids, product_id=False, context=None):
|
||||
def onchange_product_id(self, cr, uid, ids,
|
||||
product_id=False,
|
||||
context=None):
|
||||
move_obj = self.pool.get('stock.move')
|
||||
if ids:
|
||||
reserv = self.read(cr, uid, ids, ['move_id'], context=context,
|
||||
@@ -146,7 +148,6 @@ class stock_reservation(orm.Model):
|
||||
cr, uid, move_ids, prod_id=product_id, loc_id=False,
|
||||
loc_dest_id=False, partner_id=False)
|
||||
if result.get('value'):
|
||||
vals = result['value']
|
||||
# only keep the existing fields on the view
|
||||
keep = ('product_uom', 'name')
|
||||
result['value'] = dict((key, value) for key, value in
|
||||
@@ -154,7 +155,10 @@ class stock_reservation(orm.Model):
|
||||
key in keep)
|
||||
return result
|
||||
|
||||
def onchange_quantity(self, cr, uid, ids, product_id, product_qty, context=None):
|
||||
def onchange_quantity(self, cr, uid, ids,
|
||||
product_id,
|
||||
product_qty,
|
||||
context=None):
|
||||
""" On change of product quantity avoid negative quantities """
|
||||
if not product_id or product_qty <= 0.0:
|
||||
return {'value': {'product_qty': 0.0}}
|
||||
|
||||
@@ -117,9 +117,22 @@ class sale_order_line(orm.Model):
|
||||
reserv_obj.release(cr, uid, reserv_ids, context=context)
|
||||
return True
|
||||
|
||||
def product_id_change(self, cr, uid, ids, pricelist, product, qty=0,
|
||||
uom=False, qty_uos=0, uos=False, name='', partner_id=False,
|
||||
lang=False, update_tax=True, date_order=False, packaging=False, fiscal_position=False, flag=False, context=None):
|
||||
def product_id_change(self, cr, uid, ids,
|
||||
pricelist,
|
||||
product,
|
||||
qty=0,
|
||||
uom=False,
|
||||
qty_uos=0,
|
||||
uos=False,
|
||||
name='',
|
||||
partner_id=False,
|
||||
lang=False,
|
||||
update_tax=True,
|
||||
date_order=False,
|
||||
packaging=False,
|
||||
fiscal_position=False,
|
||||
flag=False,
|
||||
context=None):
|
||||
result = super(sale_order_line, self).product_id_change(
|
||||
cr, uid, ids, pricelist, product, qty=qty, uom=uom,
|
||||
qty_uos=qty_uos, uos=uos, name=name, partner_id=partner_id,
|
||||
@@ -146,9 +159,13 @@ class sale_order_line(orm.Model):
|
||||
return result
|
||||
|
||||
def write(self, cr, uid, ids, vals, context=None):
|
||||
block_on_reserve = ('product_id', 'product_uom', 'product_uos',
|
||||
block_on_reserve = ('product_id',
|
||||
'product_uom',
|
||||
'product_uos',
|
||||
'type')
|
||||
update_on_reserve = ('price_unit', 'product_uom_qty', 'product_uos_qty')
|
||||
update_on_reserve = ('price_unit',
|
||||
'product_uom_qty',
|
||||
'product_uos_qty')
|
||||
keys = set(vals.keys())
|
||||
test_block = keys.intersection(block_on_reserve)
|
||||
test_update = keys.intersection(update_on_reserve)
|
||||
@@ -162,7 +179,9 @@ class sale_order_line(orm.Model):
|
||||
'of lines with a stock reservation. '
|
||||
'Release the reservation '
|
||||
'before changing the product.'))
|
||||
res = super(sale_order_line, self).write(cr, uid, ids, vals, context=context)
|
||||
res = super(sale_order_line, self).write(cr, uid, ids,
|
||||
vals,
|
||||
context=context)
|
||||
if test_update:
|
||||
for line in self.browse(cr, uid, ids, context=context):
|
||||
if not line.reservation_ids:
|
||||
|
||||
@@ -98,7 +98,6 @@ class sale_stock_reserve(orm.TransientModel):
|
||||
if not (active_model and active_ids):
|
||||
return close
|
||||
|
||||
line_obj = self.pool.get('sale.order.line')
|
||||
if active_model == 'sale.order':
|
||||
sale_obj = self.pool.get('sale.order')
|
||||
sales = sale_obj.browse(cr, uid, active_ids, context=context)
|
||||
|
||||
Reference in New Issue
Block a user