diff --git a/addons/account/account_analytic_line.py b/addons/account/account_analytic_line.py
index de4948f0bd1..a982ca51182 100644
--- a/addons/account/account_analytic_line.py
+++ b/addons/account/account_analytic_line.py
@@ -112,13 +112,29 @@ class account_analytic_line(osv.osv):
(prod.name, prod_id,))
amount_unit = prod.price_get('list_price', context)[prod_id]
- prec = self.pool.get('decimal.precision').precision_get(cr, uid, 'Account')
- amount = amount_unit * quantity or 1.0
- result = round(amount, prec)
- if is_purchase:
- result *= -1
- return {
- 'value': {
+ if not company_id:
+ company_id=company_obj._company_default_get(cr, uid, 'account.analytic.line', context)
+ flag = False
+ # Compute based on pricetype
+ product_price_type_ids = product_price_type_obj.search(cr, uid, [('field','=','standard_price')], context)
+ pricetype = product_price_type_obj.browse(cr, uid, product_price_type_ids, context)[0]
+ if journal_id:
+ journal = analytic_journal_obj.browse(cr, uid, journal_id)
+ if journal.type == 'sale':
+ product_price_type_ids = product_price_type_obj.search(cr, uid, [('field','=','list_price')], context)
+ if product_price_type_ids:
+ pricetype = product_price_type_obj.browse(cr, uid, product_price_type_ids, context)[0]
+ # Take the company currency as the reference one
+ if pricetype.field == 'list_price':
+ flag = True
+ amount_unit = prod.price_get(pricetype.field, context)[prod.id]
+ amount = amount_unit*unit_amount or 1.0
+ prec = self.pool.get('decimal.precision').precision_get(cr, uid, 'Account')
+ amount = amount_unit*unit_amount or 1.0
+ result = round(amount, prec)
+ if not flag:
+ result *= -1
+ return {'value': {
'amount': result,
'general_account_id': a,
}
diff --git a/addons/account/board_account_view.xml b/addons/account/board_account_view.xml
index 190479f63a7..89767eb1507 100644
--- a/addons/account/board_account_view.xml
+++ b/addons/account/board_account_view.xml
@@ -68,6 +68,6 @@
-
+
diff --git a/addons/account/product_view.xml b/addons/account/product_view.xml
index cc9351d55f4..67352fd1120 100644
--- a/addons/account/product_view.xml
+++ b/addons/account/product_view.xml
@@ -9,7 +9,7 @@
-
+
diff --git a/addons/crm/crm_opportunity.py b/addons/crm/crm_opportunity.py
index 2b0062eb9d5..9047007d52c 100644
--- a/addons/crm/crm_opportunity.py
+++ b/addons/crm/crm_opportunity.py
@@ -19,8 +19,7 @@
#
##############################################################################
-from datetime import datetime
-from osv import fields,osv,orm
+from osv import fields,osv
from tools.translate import _
import crm
import time
@@ -121,7 +120,7 @@ class crm_opportunity(osv.osv):
"""
res = super(crm_opportunity, self).case_reset(cr, uid, ids, *args)
self.write(cr, uid, ids, {'stage_id': False})
- return True
+ return res
def case_open(self, cr, uid, ids, *args):
diff --git a/addons/decimal_precision/decimal_precision.py b/addons/decimal_precision/decimal_precision.py
index 1986eb8c441..5dbb2006b52 100644
--- a/addons/decimal_precision/decimal_precision.py
+++ b/addons/decimal_precision/decimal_precision.py
@@ -27,7 +27,7 @@ import pooler
class decimal_precision(osv.osv):
_name = 'decimal.precision'
_columns = {
- 'name': fields.char('Usage', size=50, required=True),
+ 'name': fields.char('Usage', size=50, select=True, required=True),
'digits': fields.integer('Digits', required=True),
}
_defaults = {
@@ -35,7 +35,7 @@ class decimal_precision(osv.osv):
}
_sql_constraints = [
- ('name_uniq', 'unique (name)', """The Usage of the decimal precision must be unique!"""),
+ ('name_uniq', 'unique (name)', """Only one value can be defined for each given usage!"""),
]
@cache(skiparg=3)
diff --git a/addons/email_template/email_template.py b/addons/email_template/email_template.py
index ee82c917351..02ebb1c0bb6 100644
--- a/addons/email_template/email_template.py
+++ b/addons/email_template/email_template.py
@@ -31,10 +31,8 @@ TEMPLATE_ENGINES = []
from osv import osv, fields
from tools.translate import _
-from mako.template import Template #For backward combatibility
try:
from mako.template import Template as MakoTemplate
- from mako import exceptions
TEMPLATE_ENGINES.append(('mako', 'Mako Templates'))
except:
LOGGER.notifyChannel(
@@ -302,7 +300,6 @@ This is useful for CRM leads for example"),
def unlink_action(self, cr, uid, ids, context):
for template in self.browse(cr, uid, ids, context):
- obj = self.pool.get(template.object_name.model)
try:
if template.ref_ir_act_window:
self.pool.get('ir.actions.act_window').unlink(cr, uid, template.ref_ir_act_window.id, context)
diff --git a/addons/event/event.py b/addons/event/event.py
index e2d51474689..16bf5f8677d 100644
--- a/addons/event/event.py
+++ b/addons/event/event.py
@@ -19,11 +19,10 @@
#
##############################################################################
-import time
-
from crm import crm
from osv import fields, osv
from tools.translate import _
+import time
import tools
import decimal_precision as dp
@@ -37,7 +36,7 @@ class event_type(osv.osv):
}
event_type()
-class event_event(crm.crm_case, osv.osv):
+class event_event(osv.osv):
"""Event"""
_name = 'event.event'
_description = __doc__
@@ -55,7 +54,6 @@ class event_event(crm.crm_case, osv.osv):
'registration_ids': False,
})
return super(event_event, self).copy(cr, uid, id, default=default, context=context)
-
def onchange_product(self, cr, uid, ids, product_id):
"""This function returns value of product's unit price based on product id.
@param self: The object pointer
@@ -162,9 +160,10 @@ class event_event(crm.crm_case, osv.osv):
cr.execute('SELECT SUM(nb_register) FROM event_registration WHERE id IN %s', (tuple(reg_ids),))
number = cr.fetchone()
if 'register_current' in fields:
- res[event.id]['register_current'] = number and number[0]
+ res[event.id]['register_current'] = len(reg_ids)
if 'register_prospect' in fields:
- res[event.id]['register_prospect'] = number and number[0]
+ res[event.id]['register_prospect'] = len(reg_ids)
+
return res
def write(self, cr, uid, ids, vals, context=None):
@@ -245,7 +244,6 @@ class event_event(crm.crm_case, osv.osv):
'state': 'draft',
'company_id': lambda self,cr,uid,c: self.pool.get('res.company')._company_default_get(cr, uid, 'event.event', context=c),
'user_id': lambda obj, cr, uid, context: uid,
- 'section_id': crm.crm_case._get_section,
}
def _check_recursion(self, cr, uid, ids):
@@ -340,8 +338,8 @@ class event_registration(osv.osv):
_defaults = {
'nb_register': 1,
'tobe_invoiced': True,
- 'state': 'draft',
- 'active': True,
+ 'state': lambda *a: 'draft',
+ 'active': lambda *a: 1,
'user_id': lambda self, cr, uid, ctx: uid,
}
@@ -357,9 +355,6 @@ class event_registration(osv.osv):
val_invoice = inv_pool.onchange_partner_id(cr, uid, [], 'out_invoice', reg.partner_invoice_id.id, False, False)
val_invoice['value'].update({'partner_id': reg.partner_invoice_id.id})
-
- inv_lines_pool.product_id_change(cr, uid, [], reg.event_id.product_id.id, uom=False, partner_id=reg.partner_invoice_id.id, fposition_id=reg.partner_invoice_id.property_account_position.id)
-
val_invoice['value'].update({
'origin': reg.event_product,
'reference': False,
@@ -591,6 +586,7 @@ class event_registration(osv.osv):
data ={}
if not contact:
return data
+ contact_obj = self.pool.get('res.partner.contact')
addr_obj = self.pool.get('res.partner.address')
job_obj = self.pool.get('res.partner.job')
@@ -670,6 +666,7 @@ class event_registration(osv.osv):
data['contact_id'] = job_obj.browse(cr, uid, job_ids[0]).contact_id.id
d = self.onchange_contact_id(cr, uid, ids, data['contact_id'], part)
data.update(d['value'])
+ partner_data = res_obj.browse(cr, uid, part)
return {'value': data}
def onchange_partner_invoice_id(self, cr, uid, ids, event_id, partner_invoice_id):
@@ -716,4 +713,5 @@ class event_registration_badge(osv.osv):
}
event_registration_badge()
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
\ No newline at end of file
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+
diff --git a/addons/hr_attendance/report/timesheet.py b/addons/hr_attendance/report/timesheet.py
index b0845422655..abedddd50c5 100644
--- a/addons/hr_attendance/report/timesheet.py
+++ b/addons/hr_attendance/report/timesheet.py
@@ -83,7 +83,6 @@ class report_custom(report_rml):
dt = datetime.strptime(att['name'], '%Y-%m-%d %H:%M:%S')
if att['action'] == 'sign_out':
week_wh[ldt.date().weekday()] = week_wh.get(ldt.date().weekday(), 0) + ((dt - ldt).seconds/3600)
- ldt = dt
# Week xml representation
week_repr = ['', '%s' % monday.strftime('%Y-%m-%d'), '%s' % n_monday.strftime('%Y-%m-%d')]
diff --git a/addons/hr_expense/hr_expense.py b/addons/hr_expense/hr_expense.py
index e9e64f76c4e..0679fee37b1 100644
--- a/addons/hr_expense/hr_expense.py
+++ b/addons/hr_expense/hr_expense.py
@@ -253,10 +253,8 @@ class hr_expense_line(osv.osv):
product = self.pool.get('product.product').browse(cr, uid, product_id, context=context)
v['name'] = product.name
# Compute based on pricetype of employee company
- pricetype_id = self.pool.get('hr.employee').browse(cr, uid, employee_id, context=context).user_id.company_id.property_valuation_price_type.id
context['currency_id'] = self.pool.get('hr.employee').browse(cr, uid, employee_id, context=context).user_id.company_id.currency_id.id
- pricetype = self.pool.get('product.price.type').browse(cr, uid, pricetype_id, context=context)
- amount_unit = product.price_get(pricetype.field, context)[product.id]
+ amount_unit = product.price_get('standard_price', context)[product.id]
v['unit_amount'] = amount_unit
if not uom_id:
v['uom_id'] = product.uom_id.id
diff --git a/addons/mrp/board_manufacturing_view.xml b/addons/mrp/board_manufacturing_view.xml
index aa8bd2ab69e..c5474670d9b 100644
--- a/addons/mrp/board_manufacturing_view.xml
+++ b/addons/mrp/board_manufacturing_view.xml
@@ -15,7 +15,7 @@
-
+
diff --git a/addons/mrp/mrp.py b/addons/mrp/mrp.py
index eea2e4a5224..5f2695cf3a7 100644
--- a/addons/mrp/mrp.py
+++ b/addons/mrp/mrp.py
@@ -861,7 +861,7 @@ class mrp_production(osv.osv):
source = production.product_id.product_tmpl_id.property_stock_production.id
data = {
'name':'PROD:' + production.name,
- 'date_planned': production.date_planned,
+ 'date': production.date_planned,
'product_id': production.product_id.id,
'product_qty': production.product_qty,
'product_uom': production.product_uom.id,
@@ -883,7 +883,7 @@ class mrp_production(osv.osv):
if line.product_id.type in ('product', 'consu'):
res_dest_id = move_obj.create(cr, uid, {
'name':'PROD:' + production.name,
- 'date_planned': production.date_planned,
+ 'date': production.date_planned,
'product_id': line.product_id.id,
'product_qty': line.product_qty,
'product_uom': line.product_uom.id,
@@ -904,7 +904,7 @@ class mrp_production(osv.osv):
'product_uom': line.product_uom.id,
'product_uos_qty': line.product_uos and line.product_uos_qty or False,
'product_uos': line.product_uos and line.product_uos.id or False,
- 'date_planned': newdate,
+ 'date': newdate,
'move_dest_id': res_dest_id,
'location_id': production.location_src_id.id,
'location_dest_id': routing_loc or production.location_src_id.id,
diff --git a/addons/mrp/mrp_view.xml b/addons/mrp/mrp_view.xml
index 6fda888190c..7addfbc3194 100644
--- a/addons/mrp/mrp_view.xml
+++ b/addons/mrp/mrp_view.xml
@@ -5,10 +5,9 @@
-
-
+
+
+
+ procurement.order.tree.board
+ procurement.order
+ tree
+
+
+
+
+
+
+
+
+
+ procurement.order.form
@@ -85,11 +100,13 @@
-
-
+
+
+
+
@@ -106,29 +123,29 @@
+
Procurement Ordersir.actions.act_windowprocurement.orderform
- {'search_default_current':1}
- {'search_default_Current':1}
- The procurement order will schedule a proposal for automatic procurement for the product concerned. This procurement will start a task, a purchase order form for the supplier or a production depending on the product configuration.
+ {'search_default_current':1}
+ Procurement Orders represent the need for a certain quantity of products, at a given time, in a given location. Sale Orders are one typical source of Procurement Orders (but these are distinct documents). Depending on the procurement parameters and the products configuration, the procurement engine will attempt to satisfy the need by reserving products from stock, or ordering products from a supplier, or passing a manufacturing order, etc.
-
- Procurements
+
+ Procurement Exceptionsir.actions.act_windowprocurement.orderformtree,form
+ {'search_default_perm_exceptions':1}
- []
- {}
+ Procurement Orders represent the need for a certain quantity of products, at a given time, in a given location. Sale Orders are one typical source of Procurement Orders (but these are distinct documents). Depending on the procurement parameters and the products configuration, the procurement engine will attempt to satisfy the need by reserving products from stock, or ordering products from a supplier, or passing a manufacturing order, etc. A Procurement Exception occurs when the system cannot find a way to fulfill a procurement. Some exceptions will resolve themselves automatically, but others require manual intervention (those are identified by a specific error message)
-
+
Procurement Exceptionsir.actions.act_window
@@ -136,26 +153,6 @@
formtree,form[('state','=','exception')]
- For each customer order line or raw materials in a manufacturing order, there is a procurement form . In normal system use, procurement orders are automatically generated by OpenERP. But if there are configuration problems, the system can remain blocked by a procurement without generating a corresponding document. It is a procurement exception. Some problems are link to timing and will be correct automatically by the system. If there is no procurement rule, the exception must be corrected manually.
-
-
-
- Procurement Exceptions to Fix
- ir.actions.act_window
- procurement.order
- form
- tree,form
- [('state','=','exception'), ('message', '<>', '')]
-
-
-
-
- Temporary Procurement Exceptions
- ir.actions.act_window
- procurement.order
- form
- tree,form
- [('state','=','exception'), ('message', '=', '')]
@@ -263,8 +260,12 @@
res_model="stock.warehouse.orderpoint"
src_model="procurement.order"/>
-
-
-
+
+
+
+
+
+
+
diff --git a/addons/procurement/schedulers.py b/addons/procurement/schedulers.py
index 777a8de45cc..06cbb86957a 100644
--- a/addons/procurement/schedulers.py
+++ b/addons/procurement/schedulers.py
@@ -241,7 +241,7 @@ class procurement_order(osv.osv):
'product_id': op.product_id.id,
'product_qty': qty,
'product_uom': op.product_uom.id,
- 'location_id': op.warehouse_id.lot_input_id.id,
+ 'location_id': op.location_id.id,
'procure_method': 'make_to_order',
'origin': op.name
})
diff --git a/addons/product/__init__.py b/addons/product/__init__.py
index 3052a02d212..4247b49d41b 100644
--- a/addons/product/__init__.py
+++ b/addons/product/__init__.py
@@ -23,6 +23,5 @@ import pricelist
import report
import partner
import wizard
-import company
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/product/__openerp__.py b/addons/product/__openerp__.py
index 5d099489859..851031e0424 100644
--- a/addons/product/__openerp__.py
+++ b/addons/product/__openerp__.py
@@ -55,7 +55,6 @@
'product_view.xml',
'pricelist_view.xml',
'partner_view.xml',
- 'company_view.xml',
'process/product_process.xml'
],
'test':['test/product_report.yml'],
diff --git a/addons/product/company.py b/addons/product/company.py
deleted file mode 100644
index 0f248dba309..00000000000
--- a/addons/product/company.py
+++ /dev/null
@@ -1,43 +0,0 @@
-# -*- coding: utf-8 -*-
-##############################################################################
-#
-# OpenERP, Open Source Management Solution
-# Copyright (C) 2004-2009 Tiny SPRL ().
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero 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 Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see .
-#
-##############################################################################
-
-from osv import fields, osv
-
-
-class res_company(osv.osv):
- _inherit = 'res.company'
- _columns = {
- 'property_valuation_price_type': fields.property(
- 'product.price.type',
- type='many2one',
- relation='product.price.type',
- domain=[],
- string="Valuation Price Type",
- method=True,
- view_load=True,
- help="The price type field in the selected price type will be used, instead of the default one, \
- for valuation of product in the current company"),
- }
-
-res_company()
-
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
-
diff --git a/addons/product/company_view.xml b/addons/product/company_view.xml
deleted file mode 100644
index eb0294e2daa..00000000000
--- a/addons/product/company_view.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-
-
-
-
-
- res.company.product.property.form.inherit
- res.company
- form
-
-
-
-
-
-
-
-
-
-
diff --git a/addons/product/product.py b/addons/product/product.py
index 09732a9bff3..22f57ac981f 100644
--- a/addons/product/product.py
+++ b/addons/product/product.py
@@ -69,15 +69,15 @@ class product_uom(osv.osv):
'name': fields.char('Name', size=64, required=True, translate=True),
'category_id': fields.many2one('product.uom.categ', 'UoM Category', required=True, ondelete='cascade',
help="Quantity conversions may happen automatically between Units of Measure in the same category, according to their respective ratios."),
- 'factor': fields.float('Ratio', digits=(12, 6), required=True,
+ 'factor': fields.float('Ratio', required=True,digits=(12, 6),
help='How many times this UoM is smaller than the reference UoM in this category:\n'\
'1 * (reference unit) = ratio * (this unit)'),
- 'factor_inv': fields.function(_factor_inv, digits=(12, 6),
+ 'factor_inv': fields.function(_factor_inv, digits_compute=dp.get_precision('Product UoM'),
fnct_inv=_factor_inv_write,
method=True, string='Ratio',
help='How many times this UoM is bigger than the reference UoM in this category:\n'\
'1 * (this unit) = ratio * (reference unit)', required=True),
- 'rounding': fields.float('Rounding Precision', digits=(16, 3), required=True,
+ 'rounding': fields.float('Rounding Precision', digits_compute=dp.get_precision('Product UoM'), required=True,
help="The computed quantity will be a multiple of this value. "\
"Use 1.0 for a UoM that cannot be further split, such as a piece."),
'active': fields.boolean('Active', help="By unchecking the active field you can disable a unit of measure without deleting it."),
diff --git a/addons/product/product_data.xml b/addons/product/product_data.xml
index 2052221cf78..31e93f46d5f 100644
--- a/addons/product/product_data.xml
+++ b/addons/product/product_data.xml
@@ -144,12 +144,6 @@ parameter) will see those record just disappear.
-
- property_valuation_price_type
-
-
-
-
Sale Price2
@@ -166,6 +160,9 @@ parameter) will see those record just disappear.
Stock Weight2
-
+
+ Product UoM
+
+
diff --git a/addons/project_timesheet/test/worktask_entry_to_timesheetline_entry.yml b/addons/project_timesheet/test/worktask_entry_to_timesheetline_entry.yml
index b08300b8d33..76762ef8c28 100644
--- a/addons/project_timesheet/test/worktask_entry_to_timesheetline_entry.yml
+++ b/addons/project_timesheet/test/worktask_entry_to_timesheetline_entry.yml
@@ -7,6 +7,8 @@
login: hr
name: HR Manager
password: hr
+ groups_id:
+ - base.group_hr_manager
-
Create a product with type service used to specify employees designation
diff --git a/addons/purchase/purchase.py b/addons/purchase/purchase.py
index eb1f2a1ace0..1522c4543f3 100644
--- a/addons/purchase/purchase.py
+++ b/addons/purchase/purchase.py
@@ -451,7 +451,7 @@ class purchase_order(osv.osv):
'product_uos_qty': order_line.product_qty,
'product_uom': order_line.product_uom.id,
'product_uos': order_line.product_uom.id,
- 'date_planned': order_line.date_planned,
+ 'date': order_line.date_planned,
'date_expected': order_line.date_planned,
'location_id': loc_id,
'location_dest_id': dest,
diff --git a/addons/purchase/stock.py b/addons/purchase/stock.py
index 49a28e352b2..fb3d8f0b078 100644
--- a/addons/purchase/stock.py
+++ b/addons/purchase/stock.py
@@ -31,6 +31,21 @@ class stock_move(osv.osv):
_defaults = {
'purchase_line_id': False
}
+
+ def _get_reference_accounting_values_for_valuation(self, cr, uid, move, context=None):
+ """
+ Overrides the default stock valuation to take into account the currency that was specified
+ on the purchase order in case the valuation data was not directly specified during picking
+ confirmation.
+ """
+ reference_amount, reference_currency_id = super(stock_move, self)._get_reference_accounting_values_for_valuation(cr, uid, move, context=context)
+ if move.product_id.cost_method != 'average' or not move.price_unit:
+ # no average price costing or cost not specified during picking validation, we will
+ # plug the purchase line values if they are found.
+ if move.purchase_line_id and move.picking_id.purchase_id.pricelist_id:
+ reference_amount, reference_currency_id = move.purchase_line_id.price_unit, move.picking_id.purchase_id.pricelist_id.currency_id.id
+ return reference_amount, reference_currency_id
+
stock_move()
#
@@ -86,6 +101,9 @@ class stock_picking(osv.osv):
user, picking, move_line)
def _invoice_line_hook(self, cursor, user, move_line, invoice_line_id):
+ if move_line.purchase_line_id:
+ invoice_line_obj = self.pool.get('account.invoice.line')
+ invoice_line_obj.write(cursor, user, [invoice_line_id], {'note': move_line.purchase_line_id.notes,})
return super(stock_picking, self)._invoice_line_hook(cursor, user,
move_line, invoice_line_id)
@@ -99,5 +117,48 @@ class stock_picking(osv.osv):
stock_picking()
+class stock_partial_picking(osv.osv_memory):
+ _inherit = 'stock.partial.picking'
+
+ def default_get(self, cr, uid, fields, context=None):
+ """ To get default values for the object.
+ @param self: The object pointer.
+ @param cr: A database cursor
+ @param uid: ID of the user currently logged in
+ @param fields: List of fields for which we want default values
+ @param context: A standard dictionary
+ @return: A dictionary which of fields with values.
+ """
+ pick_obj = self.pool.get('stock.picking')
+ res = super(stock_partial_picking, self).default_get(cr, uid, fields, context=context)
+ for pick in pick_obj.browse(cr, uid, context.get('active_ids', [])):
+ for m in pick.move_lines:
+ if (m.product_id.cost_method == 'average'):
+ if pick.type == 'in' and pick.purchase_id and m.purchase_line_id:
+ res['move%s_product_price'%(m.id)] = m.purchase_line_id.price_unit
+ res['move%s_product_currency'%(m.id)] = pick.purchase_id.pricelist_id.currency_id.id
+ return res
+stock_partial_picking()
+
+class stock_partial_move(osv.osv_memory):
+ _inherit = "stock.partial.move"
+ def default_get(self, cr, uid, fields, context=None):
+ """ To get default values for the object.
+ @param self: The object pointer.
+ @param cr: A database cursor
+ @param uid: ID of the user currently logged in
+ @param fields: List of fields for which we want default values
+ @param context: A standard dictionary
+ @return: A dictionary which of fields with values.
+ """
+ res = super(stock_partial_move, self).default_get(cr, uid, fields, context=context)
+ move_obj = self.pool.get('stock.move')
+ for m in move_obj.browse(cr, uid, context.get('active_ids', [])):
+ if (m.product_id.cost_method == 'average'):
+ if m.picking_id.type == 'in' and m.purchase_line_id and m.picking_id.purchase_id:
+ res['move%s_product_price'%(m.id)] = m.purchase_line_id.price_unit
+ res['move%s_product_currency'%(m.id)] = m.picking_id.purchase_id.pricelist_id.currency_id.id
+ return res
+stock_partial_move()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/purchase/stock_view.xml b/addons/purchase/stock_view.xml
index d5a645b4b98..a65f557a3b2 100644
--- a/addons/purchase/stock_view.xml
+++ b/addons/purchase/stock_view.xml
@@ -1,7 +1,17 @@
-
+
+ stock.move.form
+ stock.move
+ form
+
+
+
+
+
+
+ Picking liststock.picking
diff --git a/addons/sale/process/sale_process.xml b/addons/sale/process/sale_process.xml
index 3351c265e3d..20f20a7ef72 100644
--- a/addons/sale/process/sale_process.xml
+++ b/addons/sale/process/sale_process.xml
@@ -63,7 +63,7 @@
-
+
diff --git a/addons/sale/sale.py b/addons/sale/sale.py
index fe5deac13a7..ac2b1a620cf 100644
--- a/addons/sale/sale.py
+++ b/addons/sale/sale.py
@@ -712,7 +712,7 @@ class sale_order(osv.osv):
'name': line.name[:64],
'picking_id': picking_id,
'product_id': line.product_id.id,
- 'date_planned': date_planned,
+ 'date': date_planned,
'date_expected': date_planned,
'product_qty': line.product_uom_qty,
'product_uom': line.product_uom.id,
diff --git a/addons/sale/stock.py b/addons/sale/stock.py
index 39260e759b4..0bb47110213 100644
--- a/addons/sale/stock.py
+++ b/addons/sale/stock.py
@@ -29,7 +29,25 @@ class stock_move(osv.osv):
_defaults = {
'sale_line_id': False
}
-
+
+ def _get_reference_accounting_values_for_valuation(self, cr, uid, move, context=None):
+ """
+ Overrides the default stock valuation to take into account the currency that was specified
+ on the sale order in case the valuation data was not directly specified during picking
+ confirmation.
+ """
+ reference_amount, reference_currency_id = super(stock_move, self)._get_reference_accounting_values_for_valuation(cr, uid, move, context=context)
+ if move.product_id.cost_method != 'average' or not move.price_unit:
+ # no average price costing or cost not specified during picking validation, we will
+ # plug the sale line values if they are found.
+ if move.sale_line_id and move.picking_id.sale_id.pricelist_id:
+ reference_amount, reference_currency_id = move.sale_line_id.price_unit, move.picking_id.sale_id.pricelist_id.currency_id.id
+ return reference_amount, reference_currency_id
+ def _create_chained_picking(self, cr, uid, pick_name,picking,ptype,move, context=None):
+ res=super(stock_move, self)._create_chained_picking(cr, uid, pick_name,picking,ptype,move, context=context)
+ if picking.sale_id:
+ picking_obj = self.pool.get('stock.picking').write(cr,uid,[res],{'sale_id':picking.sale_id.id})
+ return res
stock_move()
class stock_picking(osv.osv):
@@ -186,6 +204,7 @@ class stock_picking(osv.osv):
'quantity': sale_line.product_uos_qty,
'invoice_line_tax_id': [(6, 0, tax_ids)],
'account_analytic_id': account_analytic_id,
+ 'notes':sale_line.notes
}, context=context)
self.pool.get('sale.order.line').write(cursor, user, [sale_line.id], {'invoiced':True,
'invoice_lines': [(6, 0, [invoice_line_id])],
@@ -208,4 +227,47 @@ class stock_picking(osv.osv):
stock_picking()
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
\ No newline at end of file
+class stock_partial_picking(osv.osv_memory):
+ _inherit = 'stock.partial.picking'
+
+ def default_get(self, cr, uid, fields, context=None):
+ """ To get default values for the object.
+ @param self: The object pointer.
+ @param cr: A database cursor
+ @param uid: ID of the user currently logged in
+ @param fields: List of fields for which we want default values
+ @param context: A standard dictionary
+ @return: A dictionary which of fields with values.
+ """
+ pick_obj = self.pool.get('stock.picking')
+ res = super(stock_partial_picking, self).default_get(cr, uid, fields, context=context)
+ for pick in pick_obj.browse(cr, uid, context.get('active_ids', [])):
+ for m in pick.move_lines:
+ if (m.product_id.cost_method == 'average'):
+ if pick.type == 'out' and pick.sale_id and m.sale_line_id:
+ res['move%s_product_price'%(m.id)] = m.sale_line_id.price_unit
+ res['move%s_product_currency'%(m.id)] = pick.sale_id.pricelist_id.currency_id.id
+ return res
+stock_partial_picking()
+
+class stock_partial_move(osv.osv_memory):
+ _inherit = "stock.partial.move"
+ def default_get(self, cr, uid, fields, context=None):
+ """ To get default values for the object.
+ @param self: The object pointer.
+ @param cr: A database cursor
+ @param uid: ID of the user currently logged in
+ @param fields: List of fields for which we want default values
+ @param context: A standard dictionary
+ @return: A dictionary which of fields with values.
+ """
+ res = super(stock_partial_move, self).default_get(cr, uid, fields, context=context)
+ move_obj = self.pool.get('stock.move')
+ for m in move_obj.browse(cr, uid, context.get('active_ids', [])):
+ if (m.product_id.cost_method == 'average'):
+ if m.picking_id.type == 'out' and m.sale_line_id and m.picking_id.sale_id:
+ res['move%s_product_price'%(m.id)] = m.sale_line_id.price_unit
+ res['move%s_product_currency'%(m.id)] = m.picking_id.sale_id.pricelist_id.currency_id.id
+ return res
+stock_partial_move()
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/sale/stock_view.xml b/addons/sale/stock_view.xml
index 6b3c0e2c684..93529fd3b7d 100644
--- a/addons/sale/stock_view.xml
+++ b/addons/sale/stock_view.xml
@@ -13,6 +13,17 @@
+
+ stock.move.form
+ stock.move
+ form
+
+
+
+
+
+
+
diff --git a/addons/sale_journal/test/picking_journal.yml b/addons/sale_journal/test/picking_journal.yml
index af4ee8ec8a0..9412c3d40d1 100644
--- a/addons/sale_journal/test/picking_journal.yml
+++ b/addons/sale_journal/test/picking_journal.yml
@@ -20,7 +20,7 @@
move_lines:
- company_id: base.main_company
date_expected: '2010-08-04 14:09:38'
- date_planned: '2010-08-04 14:09:38'
+ date: '2010-08-04 14:09:38'
location_id: stock.stock_location_stock
product_id: product.product_product_24
product_qty: 50.0
@@ -84,4 +84,4 @@
-
!python {model: sale_journal.picking.journal}: |
pick_jour = self.browse(cr, uid, ref("sale_journal_picking_journal_pickjournal0"))
- assert (pick_jour.state == 'close'), "Journal is not in the open state"
\ No newline at end of file
+ assert (pick_jour.state == 'close'), "Journal is not in the open state"
diff --git a/addons/stock/__openerp__.py b/addons/stock/__openerp__.py
index 432f95242bd..79d6c860e65 100644
--- a/addons/stock/__openerp__.py
+++ b/addons/stock/__openerp__.py
@@ -45,11 +45,12 @@ Thanks to the double entry management, the inventory controlling is powerful and
"init_xml" : [],
"demo_xml" : ["stock_demo.xml"],
"update_xml" : [
+ "security/stock_security.xml",
+ "security/ir.model.access.csv",
"stock_data.xml",
"wizard/stock_move_view.xml",
"wizard/stock_partial_picking_view.xml",
"wizard/stock_partial_move_view.xml",
- "wizard/stock_inventory_set_stock_zero_view.xml",
"wizard/stock_fill_inventory_view.xml",
"wizard/stock_invoice_onshipping_view.xml",
"wizard/stock_inventory_merge_view.xml",
@@ -68,10 +69,7 @@ Thanks to the double entry management, the inventory controlling is powerful and
"product_view.xml",
"partner_view.xml",
"report/report_stock_move_view.xml",
- "report/report_stock_picking_view.xml",
"report/report_stock_view.xml",
- "security/stock_security.xml",
- "security/ir.model.access.csv",
"board_warehouse_view.xml"
],
'test': ['test/stock_test.yml',
diff --git a/addons/stock/board_warehouse_view.xml b/addons/stock/board_warehouse_view.xml
index 8467688d994..84a24f3d0b2 100644
--- a/addons/stock/board_warehouse_view.xml
+++ b/addons/stock/board_warehouse_view.xml
@@ -5,30 +5,56 @@
Warehouse
-
- Reception Picking Stock Move
+
+
+ Incoming Productstock.moveir.actions.act_windowformtree,form
- [('picking_id','!=',False),('picking_id.type','=','in'),('date','<=',time.strftime('%Y-%m-%d 23:59:59'))]
-
+ ['|','&',('picking_id','=',False),('location_id.usage', 'in', ['customer','supplier']),'&',('picking_id','!=',False),('picking_id.type','=','in')]
+
-
+
+
+ Outgoing Product
+ stock.move
+ ir.actions.act_window
+ form
+ tree,form
+ ['|','&',('picking_id','=',False),('location_dest_id.usage', 'in', ['customer','supplier']),'&',('picking_id','!=',False),('picking_id.type','=','out')]
+
+
+
+
+ report.stock.move
+ form
+ graph,tree
+
+ {'search_default_month-1':1,'search_default_in':1,'group_by':['day'], 'group_by_no_leaf':1}
+
+
+ report.stock.move
+ form
+ graph,tree
+
+ {'search_default_month-1':1,'search_default_out':1,'group_by':['day'], 'group_by_no_leaf':1}
+ board.warehouse.formboard.boardform
@@ -43,9 +69,9 @@
-
-
-
+
+
+
diff --git a/addons/stock/product.py b/addons/stock/product.py
index 19b6570a31a..e1d4c71b46c 100644
--- a/addons/stock/product.py
+++ b/addons/stock/product.py
@@ -21,6 +21,7 @@
from osv import fields, osv
from tools.translate import _
+import decimal_precision as dp
class product_product(osv.osv):
_inherit = "product.product"
@@ -228,16 +229,25 @@ class product_product(osv.osv):
results = []
results2 = []
- from_date=context.get('from_date',False)
- to_date=context.get('to_date',False)
- date_str=False
+ from_date = context.get('from_date',False)
+ to_date = context.get('to_date',False)
+ date_str = False
+ date_values = False
+ where = [tuple(location_ids),tuple(location_ids),tuple(ids),tuple(states)]
if from_date and to_date:
- date_str="date_planned>='%s' and date_planned<='%s'"%(from_date,to_date)
+ date_str = "date>=%s and date<=%s"
+ where.append(tuple([from_date]))
+ where.append(tuple([to_date]))
elif from_date:
- date_str="date_planned>='%s'"%(from_date)
+ date_str = "date>=%s"
+ date_values = [from_date]
elif to_date:
- date_str="date_planned<='%s'"%(to_date)
+ date_str = "date<=%s"
+ date_values = [to_date]
+
+ if date_values:
+ where.append(tuple(date_values))
if 'in' in what:
# all moves from a location out of the set to a location in the set
cr.execute(
@@ -247,8 +257,7 @@ class product_product(osv.osv):
'and location_dest_id IN %s'\
'and product_id IN %s'\
'and state IN %s' + (date_str and 'and '+date_str+' ' or '') +''\
- 'group by product_id,product_uom',(tuple(location_ids),tuple(location_ids),tuple(ids),tuple(states),)
- )
+ 'group by product_id,product_uom',tuple(where))
results = cr.fetchall()
if 'out' in what:
# all moves from a location in the set to a location out of the set
@@ -259,8 +268,7 @@ class product_product(osv.osv):
'and location_dest_id NOT IN %s '\
'and product_id IN %s'\
'and state in %s' + (date_str and 'and '+date_str+' ' or '') + ''\
- 'group by product_id,product_uom',(tuple(location_ids),tuple(location_ids),tuple(ids),tuple(states),)
- )
+ 'group by product_id,product_uom',tuple(where))
results2 = cr.fetchall()
uom_obj = self.pool.get('product.uom')
uoms = map(lambda x: x[2], results) + map(lambda x: x[2], results2)
@@ -309,16 +317,19 @@ class product_product(osv.osv):
return res
_columns = {
- 'qty_available': fields.function(_product_available, method=True, type='float', string='Real Stock', help="Current quantities of products in selected locations or all internal if none have been selected.", multi='qty_available'),
- 'virtual_available': fields.function(_product_available, method=True, type='float', string='Virtual Stock', help="Future stock for this product according to the selected locations or all internal if none have been selected. Computed as: Real Stock - Outgoing + Incoming.", multi='qty_available'),
- 'incoming_qty': fields.function(_product_available, method=True, type='float', string='Incoming', help="Quantities of products that are planned to arrive in selected locations or all internal if none have been selected.", multi='qty_available'),
- 'outgoing_qty': fields.function(_product_available, method=True, type='float', string='Outgoing', help="Quantities of products that are planned to leave in selected locations or all internal if none have been selected.", multi='qty_available'),
+ 'qty_available': fields.function(_product_available, method=True, type='float', string='Real Stock', help="Current quantities of products in selected locations or all internal if none have been selected.", multi='qty_available', digits_compute=dp.get_precision('Product UoM')),
+ 'virtual_available': fields.function(_product_available, method=True, type='float', string='Virtual Stock', help="Future stock for this product according to the selected locations or all internal if none have been selected. Computed as: Real Stock - Outgoing + Incoming.", multi='qty_available', digits_compute=dp.get_precision('Product UoM')),
+ 'incoming_qty': fields.function(_product_available, method=True, type='float', string='Incoming', help="Quantities of products that are planned to arrive in selected locations or all internal if none have been selected.", multi='qty_available', digits_compute=dp.get_precision('Product UoM')),
+ 'outgoing_qty': fields.function(_product_available, method=True, type='float', string='Outgoing', help="Quantities of products that are planned to leave in selected locations or all internal if none have been selected.", multi='qty_available', digits_compute=dp.get_precision('Product UoM')),
'track_production': fields.boolean('Track Manufacturing Lots' , help="Forces to specify a Production Lot for all moves containing this product and generated by a Manufacturing Order"),
'track_incoming': fields.boolean('Track Incoming Lots', help="Forces to specify a Production Lot for all moves containing this product and coming from a Supplier Location"),
'track_outgoing': fields.boolean('Track Outgoing Lots', help="Forces to specify a Production Lot for all moves containing this product and going to a Customer Location"),
'location_id': fields.dummy(string='Stock Location', relation='stock.location', type='many2one'),
- 'valuation':fields.selection([('manual_periodic', 'Periodic (manual)'),
- ('real_time','Real Time (automatized)'),], 'Stock Valuation', help="Decide if the system must automatically create account moves based on stock moves", required=True),
+ 'valuation':fields.selection([('manual_periodic', 'Periodical (manual)'),
+ ('real_time','Real Time (automated)'),], 'Inventory Valuation',
+ help="If real-time valuation is enabled for a product, the system will automatically write journal entries corresponding to stock moves." \
+ "The inventory variation account set on the product category will represent the current inventory value, and the stock input and stock output account will hold the counterpart moves for incoming and outgoing products."
+ , required=True),
}
_defaults = {
@@ -404,11 +415,11 @@ class product_template(osv.osv):
'property_stock_account_input': fields.property('account.account',
type='many2one', relation='account.account',
string='Stock Input Account', method=True, view_load=True,
- help='This account will be used, instead of the default one, to value input stock'),
+ help='When doing real-time inventory valuation, counterpart Journal Items for all incoming stock moves will be posted in this account. If not set on the product, the one from the product category is used.'),
'property_stock_account_output': fields.property('account.account',
type='many2one', relation='account.account',
string='Stock Output Account', method=True, view_load=True,
- help='This account will be used, instead of the default one, to value output stock'),
+ help='When doing real-time inventory valuation, counterpart Journal Items for all outgoing stock moves will be posted in this account. If not set on the product, the one from the product category is used.'),
}
product_template()
@@ -420,23 +431,23 @@ class product_category(osv.osv):
'property_stock_journal': fields.property('account.journal',
relation='account.journal', type='many2one',
string='Stock journal', method=True, view_load=True,
- help="This journal will be used for the accounting move generated by stock move"),
+ help="When doing real-time inventory valuation, this is the Accounting Journal in which entries will be automatically posted when stock moves are processed."),
'property_stock_account_input_categ': fields.property('account.account',
type='many2one', relation='account.account',
string='Stock Input Account', method=True, view_load=True,
- help='This account will be used to value the input stock'),
+ help='When doing real-time inventory valuation, counterpart Journal Items for all incoming stock moves will be posted in this account. This is the default value for all products in this category, it can also directly be set on each product.'),
'property_stock_account_output_categ': fields.property('account.account',
type='many2one', relation='account.account',
string='Stock Output Account', method=True, view_load=True,
- help='This account will be used to value the output stock'),
+ help='When doing real-time inventory valuation, counterpart Journal Items for all outgoing stock moves will be posted in this account. This is the default value for all products in this category, it can also directly be set on each product.'),
'property_stock_variation': fields.property('account.account',
type='many2one',
relation='account.account',
string="Stock Variation Account",
method=True, view_load=True,
- help="This account will be used in product when valuation type is real-time valuation ",),
+ help="When real-time inventory valuation is enabled on a product, this account will hold the current value of the products.",),
}
product_category()
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
\ No newline at end of file
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/stock/product_view.xml b/addons/stock/product_view.xml
index fea019a12ab..561bb9f2206 100644
--- a/addons/stock/product_view.xml
+++ b/addons/stock/product_view.xml
@@ -60,10 +60,9 @@
-
-
-
-
+
+
+
@@ -76,10 +75,21 @@
26
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -122,17 +132,17 @@
-
+
diff --git a/addons/stock/report/__init__.py b/addons/stock/report/__init__.py
index 5a53a5b79dd..18bfe2609c2 100644
--- a/addons/stock/report/__init__.py
+++ b/addons/stock/report/__init__.py
@@ -24,7 +24,6 @@ import ups
import picking
import lot_overview_all
import report_stock
-import report_stock_picking
import report_stock_move
import stock_inventory_move_report
import lot_overview
diff --git a/addons/stock/report/lot_overview.py b/addons/stock/report/lot_overview.py
index a96bdf38099..66230e2822c 100644
--- a/addons/stock/report/lot_overview.py
+++ b/addons/stock/report/lot_overview.py
@@ -37,7 +37,8 @@ class lot_overview(report_sxw.rml_parse):
def process(self,location_id):
location_obj = pooler.get_pool(self.cr.dbname).get('stock.location')
data = location_obj._product_get_report(self.cr,self.uid, [location_id])
- data['location_name'] = location_obj.read(self.cr, self.uid, [location_id],['name'])[0]['name']
+
+ data['location_name'] = location_obj.read(self.cr, self.uid, [location_id],['complete_name'])[0]['complete_name']
self.price_total = 0.0
self.price_total += data['total_price']
self.grand_total += data['total_price']
diff --git a/addons/stock/report/lot_overview_all.py b/addons/stock/report/lot_overview_all.py
index c523b9775d1..0b4edcaecf4 100644
--- a/addons/stock/report/lot_overview_all.py
+++ b/addons/stock/report/lot_overview_all.py
@@ -37,7 +37,7 @@ class lot_overview_all(report_sxw.rml_parse):
def process(self,location_id):
location_obj = pooler.get_pool(self.cr.dbname).get('stock.location')
data = location_obj._product_get_all_report(self.cr,self.uid, [location_id])
- data['location_name'] = location_obj.read(self.cr, self.uid, [location_id],['name'])[0]['name']
+ data['location_name'] = location_obj.read(self.cr, self.uid, [location_id],['complete_name'])[0]['complete_name']
self.price_total = 0.0
self.price_total += data['total_price']
self.grand_total += data['total_price']
diff --git a/addons/stock/report/product_stock.py b/addons/stock/report/product_stock.py
index e55405381b4..b7dd254234c 100644
--- a/addons/stock/report/product_stock.py
+++ b/addons/stock/report/product_stock.py
@@ -69,12 +69,12 @@ class report_stock(report_int):
if not loc_ids or not product_ids:
return (False, 'pdf')
- cr.execute("select sum(r.product_qty * u.factor), r.date_planned, r.product_id "
+ cr.execute("select sum(r.product_qty * u.factor), r.date, r.product_id "
"from stock_move r left join product_uom u on (r.product_uom=u.id) "
"where state IN %s"
"and location_id IN %s"
"and product_id IN %s"
- "group by date_planned,product_id",(('confirmed','assigned','waiting'),tuple(loc_ids) ,tuple(product_ids),))
+ "group by date,product_id",(('confirmed','assigned','waiting'),tuple(loc_ids) ,tuple(product_ids),))
for (qty, dt, prod_id) in cr.fetchall():
if dt<=dt_from:
dt= (datetime.now() + relativedelta(days=1)).strftime('%Y-%m-%d')
@@ -83,12 +83,12 @@ class report_stock(report_int):
products.setdefault(prod_id, [])
products[prod_id].append((dt,-qty))
- cr.execute("select sum(r.product_qty * u.factor), r.date_planned, r.product_id "
+ cr.execute("select sum(r.product_qty * u.factor), r.date, r.product_id "
"from stock_move r left join product_uom u on (r.product_uom=u.id) "
"where state IN %s"
"and location_dest_id IN %s"
"and product_id IN %s"
- "group by date_planned,product_id",(('confirmed','assigned','waiting'),tuple(loc_ids) ,tuple(product_ids),))
+ "group by date,product_id",(('confirmed','assigned','waiting'),tuple(loc_ids) ,tuple(product_ids),))
for (qty, dt, prod_id) in cr.fetchall():
if dt<=dt_from:
diff --git a/addons/stock/report/report_stock_move.py b/addons/stock/report/report_stock_move.py
index 92fb4559345..618c086cd4f 100644
--- a/addons/stock/report/report_stock_move.py
+++ b/addons/stock/report/report_stock_move.py
@@ -29,7 +29,7 @@ class report_stock_move(osv.osv):
_description = "Moves Statistics"
_auto = False
_columns = {
- 'date_planned': fields.date('Date Planned', readonly=True ),
+ 'date': fields.date('Date', readonly=True),
'year': fields.char('Year', size=4, readonly=True),
'day': fields.char('Day', size=128, readonly=True),
'month':fields.selection([('01','January'), ('02','February'), ('03','March'), ('04','April'),
@@ -39,26 +39,30 @@ class report_stock_move(osv.osv):
'product_id':fields.many2one('product.product', 'Product', readonly=True),
'company_id':fields.many2one('res.company', 'Company', readonly=True),
'picking_id':fields.many2one('stock.picking', 'Packing', readonly=True),
- 'type': fields.selection([('out', 'Sending Goods'), ('in', 'Getting Goods'), ('internal', 'Internal'), ('delivery', 'Delivery')], 'Shipping Type', required=True, select=True, help="Shipping type specify, goods coming in or going out."),
+ 'type': fields.selection([('out', 'Sending Goods'), ('in', 'Getting Goods'), ('internal', 'Internal'), ('delivery', 'Delivery'), ('other', 'Others')], 'Shipping Type', required=True, select=True, help="Shipping type specify, goods coming in or going out."),
'location_id': fields.many2one('stock.location', 'Source Location', readonly=True, select=True, help="Sets a location if you produce at a fixed location. This can be a partner location if you subcontract the manufacturing operations."),
'location_dest_id': fields.many2one('stock.location', 'Dest. Location', readonly=True, select=True, help="Location where the system will stock the finished products."),
'state': fields.selection([('draft', 'Draft'), ('waiting', 'Waiting'), ('confirmed', 'Confirmed'), ('assigned', 'Available'), ('done', 'Done'), ('cancel', 'Cancelled')], 'State', readonly=True, select=True),
- 'product_qty_in':fields.float('In Qty',readonly=True),
- 'product_qty_out':fields.float('Out Qty',readonly=True),
- 'value' : fields.float('Total Value', required=True, digits_compute=dp.get_precision('Sale Price')),
- 'day_diff2':fields.float('Delay (Days)',readonly=True, digits=(16,2), group_operator="avg"),
- 'day_diff1':fields.float('Planned (Days)',readonly=True, digits=(16,2), group_operator="avg"),
- 'day_diff':fields.float('Real (Days)',readonly=True, digits=(16,2), group_operator="avg"),
+ 'product_qty':fields.integer('Quantity',readonly=True),
+ 'categ_id': fields.many2one('product.category', 'Product Category', ),
+ 'product_qty_in':fields.integer('In Qty',readonly=True),
+ 'product_qty_out':fields.integer('Out Qty',readonly=True),
+ 'value' : fields.float('Total Value', required=True),
+ 'day_diff2':fields.float('Lag (Days)',readonly=True, digits_compute=dp.get_precision('Shipping Delay'), group_operator="avg"),
+ 'day_diff1':fields.float('Planned Lead Time (Days)',readonly=True, digits_compute=dp.get_precision('Shipping Delay'), group_operator="avg"),
+ 'day_diff':fields.float('Execution Lead Time (Days)',readonly=True, digits_compute=dp.get_precision('Shipping Delay'), group_operator="avg"),
+ 'stock_journal': fields.many2one('stock.journal','Stock Journal', select=True),
+
+
}
def init(self, cr):
tools.drop_view_if_exists(cr, 'report_stock_move')
cr.execute("""
- create or replace view report_stock_move as (
- select
+ CREATE OR REPLACE view report_stock_move AS (
+ SELECT
min(sm_id) as id,
- sum(value) as value,
- al.dp as date_planned,
+ al.dp as date,
al.curr_year as year,
al.curr_month as month,
al.curr_day as day,
@@ -69,56 +73,74 @@ class report_stock_move(osv.osv):
al.picking_id as picking_id,
al.company_id as company_id,
al.location_dest_id as location_dest_id,
+ al.product_qty,
al.out_qty as product_qty_out,
al.in_qty as product_qty_in,
al.address_id as partner_id,
al.product_id as product_id,
al.state as state ,
al.product_uom as product_uom,
- al.type as type
+ al.categ_id as categ_id,
+ coalesce(al.type, 'other') as type,
+ al.stock_journal as stock_journal,
+ sum(al.in_value - al.out_value) as value
FROM (SELECT
-
CASE WHEN sp.type in ('out','delivery') THEN
- sum(sm.product_qty) END AS out_qty,
+ sum(sm.product_qty * pu.factor)
+ ELSE 0.0
+ END AS out_qty,
CASE WHEN sp.type in ('in') THEN
- sum(sm.product_qty) END AS in_qty,
+ sum(sm.product_qty * pu.factor)
+ ELSE 0.0
+ END AS in_qty,
+ CASE WHEN sp.type in ('out','delivery') THEN
+ sum(sm.product_qty * pu.factor) * pt.standard_price
+ ELSE 0.0
+ END AS out_value,
+ CASE WHEN sp.type in ('in') THEN
+ sum(sm.product_qty * pu.factor) * pt.standard_price
+ ELSE 0.0
+ END AS in_value,
min(sm.id) as sm_id,
- sm.date_planned as dp,
- to_char(date_trunc('day',sm.date_planned), 'YYYY') as curr_year,
- to_char(date_trunc('day',sm.date_planned), 'MM') as curr_month,
- to_char(date_trunc('day',sm.date_planned), 'YYYY-MM-DD') as curr_day,
- avg(date(sm.date_planned)-date(sm.create_date)) as curr_day_diff,
+ sm.date as dp,
+ to_char(date_trunc('day',sm.date), 'YYYY') as curr_year,
+ to_char(date_trunc('day',sm.date), 'MM') as curr_month,
+ to_char(date_trunc('day',sm.date), 'YYYY-MM-DD') as curr_day,
+ avg(date(sm.date)-date(sm.create_date)) as curr_day_diff,
avg(date(sm.date_expected)-date(sm.create_date)) as curr_day_diff1,
- avg(date(sm.date_planned)-date(sm.date_expected)) as curr_day_diff2,
+ avg(date(sm.date)-date(sm.date_expected)) as curr_day_diff2,
sm.location_id as location_id,
sm.location_dest_id as location_dest_id,
- pt.standard_price * sum(sm.product_qty) as value,
+ sum(sm.product_qty) as product_qty,
+ pt.categ_id as categ_id ,
sm.address_id as address_id,
sm.product_id as product_id,
sm.picking_id as picking_id,
sm.company_id as company_id,
sm.state as state,
sm.product_uom as product_uom,
- sp.type as type
- from
+ sp.type as type,
+ sp.stock_journal_id AS stock_journal
+ FROM
stock_move sm
- left join stock_picking sp on (sm.picking_id=sp.id)
- left join product_product pp on (sm.product_id=pp.id)
- left join product_template pt on (pp.product_tmpl_id=pt.id)
- left join stock_location sl on (sm.location_id = sl.id)
+ LEFT JOIN stock_picking sp ON (sm.picking_id=sp.id)
+ LEFT JOIN product_product pp ON (sm.product_id=pp.id)
+ LEFT JOIN product_uom pu ON (sm.product_uom=pu.id)
+ LEFT JOIN product_template pt ON (pp.product_tmpl_id=pt.id)
+ LEFT JOIN stock_location sl ON (sm.location_id = sl.id)
- group by
- sm.id,sp.type, sm.date_planned,sm.address_id,
+ GROUP BY
+ sm.id,sp.type, sm.date,sm.address_id,
sm.product_id,sm.state,sm.product_uom,sm.date_expected,
- sm.product_id,pt.standard_price, sm.picking_id,
- sm.company_id,sm.product_qty, sm.location_id,sm.location_dest_id)
- as al
+ sm.product_id,pt.standard_price, sm.picking_id, sm.product_qty,
+ sm.company_id,sm.product_qty, sm.location_id,sm.location_dest_id,pu.factor,pt.categ_id, sp.stock_journal_id)
+ AS al
- group by
+ GROUP BY
al.out_qty,al.in_qty,al.curr_year,al.curr_month,
al.curr_day,al.curr_day_diff,al.curr_day_diff1,al.curr_day_diff2,al.dp,al.location_id,al.location_dest_id,
al.address_id,al.product_id,al.state,al.product_uom,
- al.picking_id,al.company_id,al.type
+ al.picking_id,al.company_id,al.type,al.product_qty, al.categ_id, al.stock_journal
)
""")
@@ -130,14 +152,15 @@ class report_stock_inventory(osv.osv):
_description = "Stock Statistics"
_auto = False
_columns = {
- 'date_planned': fields.datetime('Date', readonly=True),
+ 'date': fields.datetime('Date', readonly=True),
'partner_id':fields.many2one('res.partner.address', 'Partner', readonly=True),
'product_id':fields.many2one('product.product', 'Product', readonly=True),
+ 'product_categ_id':fields.many2one('product.category', 'Product Category', readonly=True),
'location_id': fields.many2one('stock.location', 'Location', readonly=True),
'prodlot_id': fields.many2one('stock.production.lot', 'Lot', readonly=True),
'company_id': fields.many2one('res.company', 'Company', readonly=True),
- 'product_qty':fields.float('Qty', digits=(16,2), readonly=True),
- 'value' : fields.float('Total Value', digits=(16,2), required=True, digits_compute=dp.get_precision('Sale Price')),
+ 'product_qty':fields.float('Quantity', digits_compute=dp.get_precision('Product UoM'), readonly=True),
+ 'value' : fields.float('Total Value', digits_compute=dp.get_precision('Account'), required=True),
'state': fields.selection([('draft', 'Draft'), ('waiting', 'Waiting'), ('confirmed', 'Confirmed'), ('assigned', 'Available'), ('done', 'Done'), ('cancel', 'Cancelled')], 'State', readonly=True, select=True,
help='When the stock move is created it is in the \'Draft\' state.\n After that it is set to \'Confirmed\' state.\n If stock is available state is set to \'Avaiable\'.\n When the picking it done the state is \'Done\'.\
\nThe state is \'Waiting\' if the move is waiting for another one.'),
@@ -146,44 +169,44 @@ class report_stock_inventory(osv.osv):
def init(self, cr):
tools.drop_view_if_exists(cr, 'report_stock_inventory')
cr.execute("""
-create or replace view report_stock_inventory as (
- (select
- min(m.id) as id, m.date_planned as date_planned,
+CREATE OR REPLACE view report_stock_inventory AS (
+ (SELECT
+ min(m.id) as id, m.date as date,
m.address_id as partner_id, m.location_id as location_id,
- m.product_id as product_id, l.usage as location_type,
+ m.product_id as product_id, pt.categ_id as product_categ_id, l.usage as location_type,
m.company_id,
m.state as state, m.prodlot_id as prodlot_id,
- sum(-m.product_qty*u.factor)::decimal(16,2) as product_qty,
- sum(-pt.standard_price * m.product_qty * u.factor)::decimal(16,2) as value
- from
+ coalesce(sum(-m.product_qty * u.factor)::decimal, 0.0) as product_qty,
+ coalesce(sum(-pt.standard_price * m.product_qty * u.factor)::decimal, 0.0) as value
+ FROM
stock_move m
- left join stock_picking p on (m.picking_id=p.id)
- left join product_product pp on (m.product_id=pp.id)
- left join product_template pt on (pp.product_tmpl_id=pt.id)
- left join product_uom u on (m.product_uom=u.id)
- left join stock_location l on (m.location_id=l.id)
- group by
- m.id, m.product_id, m.address_id, m.location_id, m.prodlot_id,
- m.date_planned, m.state, l.usage, m.company_id
-) union all (
- select
- -m.id as id, m.date_planned as date_planned,
+ LEFT JOIN stock_picking p ON (m.picking_id=p.id)
+ LEFT JOIN product_product pp ON (m.product_id=pp.id)
+ LEFT JOIN product_template pt ON (pp.product_tmpl_id=pt.id)
+ LEFT JOIN product_uom u ON (m.product_uom=u.id)
+ LEFT JOIN stock_location l ON (m.location_id=l.id)
+ GROUP BY
+ m.id, m.product_id, m.product_uom, pt.categ_id, m.address_id, m.location_id, m.location_dest_id,
+ m.prodlot_id, m.date, m.state, l.usage, m.company_id
+) UNION ALL (
+ SELECT
+ -m.id as id, m.date as date,
m.address_id as partner_id, m.location_dest_id as location_id,
- m.product_id as product_id, l.usage as location_type,
+ m.product_id as product_id, pt.categ_id as product_categ_id, l.usage as location_type,
m.company_id,
m.state as state, m.prodlot_id as prodlot_id,
- sum(m.product_qty*u.factor)::decimal(16,2) as product_qty,
- sum(pt.standard_price * m.product_qty * u.factor)::decimal(16,2) as value
- from
+ coalesce(sum(m.product_qty*u.factor)::decimal, 0.0) as product_qty,
+ coalesce(sum(pt.standard_price * m.product_qty * u.factor)::decimal, 0.0) as value
+ FROM
stock_move m
- left join stock_picking p on (m.picking_id=p.id)
- left join product_product pp on (m.product_id=pp.id)
- left join product_template pt on (pp.product_tmpl_id=pt.id)
- left join product_uom u on (m.product_uom=u.id)
- left join stock_location l on (m.location_dest_id=l.id)
- group by
- m.id, m.product_id, m.address_id, m.location_id, m.location_dest_id,
- m.prodlot_id, m.date_planned, m.state, l.usage, m.company_id
+ LEFT JOIN stock_picking p ON (m.picking_id=p.id)
+ LEFT JOIN product_product pp ON (m.product_id=pp.id)
+ LEFT JOIN product_template pt ON (pp.product_tmpl_id=pt.id)
+ LEFT JOIN product_uom u ON (m.product_uom=u.id)
+ LEFT JOIN stock_location l ON (m.location_dest_id=l.id)
+ GROUP BY
+ m.id, m.product_id, m.product_uom, pt.categ_id, m.address_id, m.location_id, m.location_dest_id,
+ m.prodlot_id, m.date, m.state, l.usage, m.company_id
)
);
""")
diff --git a/addons/stock/report/report_stock_move_view.xml b/addons/stock/report/report_stock_move_view.xml
index 6377bd38e9a..353b8b6bc9c 100644
--- a/addons/stock/report/report_stock_move_view.xml
+++ b/addons/stock/report/report_stock_move_view.xml
@@ -13,7 +13,7 @@
tree
-
+
@@ -24,12 +24,15 @@
+
+
-
-
-
-
+
+
+
+
+
@@ -42,7 +45,18 @@
-
+
+
+
+
+
+ report.stock.move.graph
+ report.stock.move
+ graph
+
+
+
+
@@ -54,52 +68,44 @@
-
-
+ domain="[('date','<=', (datetime.date (int(time.strftime('%%Y')), datetime.date.today().month, 1) - datetime.timedelta (days = 1)).strftime('%%Y-%%m-%%d')),('date','>',(datetime.date (int(time.strftime('%%Y')), datetime.date.today().month-1, 1)).strftime('%%Y-%%m-%%d'))]"
+ help="Last month"/>
+
-
-
-
-
+
+
+
-
+
-
-
-
-
-
-
-
-
-
+
@@ -109,13 +115,25 @@
+
-
+
+
+
+
+
+
+
+
+
+
+
+
@@ -126,28 +144,28 @@
formtree,graph
- {'contact_display': 'partner', 'search_default_month':1, 'search_default_group_product':1,'search_default_done':1,'group_by':[], 'group_by_no_leaf':1}
+ {'full':'1','contact_display': 'partner','search_default_done':1, 'search_default_month':1, 'search_default_group_type':1, 'group_by': [], 'group_by_no_leaf':1,}
-
report.stock.inventory.treereport.stock.inventorytree
-
+
+
-
-
+
+
@@ -173,43 +191,46 @@
+ help="Analysis of current inventory (only moves that have already been processed)"/>
+ help = "Analysis including future moves (similar to virtual stock)"/>
-
-
-
-
+
+
-
-
-
-
-
-
-
+
+
+
-
+
-
-
+
+
+
+
+
+
+
+
+
+
+
@@ -220,7 +241,7 @@
formtree,graph
- {'contact_display': 'partner', 'search_default_real':1, 'search_default_location_type_internal':1,'search_default_group_product':1,'search_default_group_location':1,'group_by':[], 'group_by_no_leaf':1}
+ {'contact_display': 'partner', 'search_default_real':1, 'search_default_location_type_internal':1,'search_default_group_product':1,'group_by':[], 'group_by_no_leaf':1}
diff --git a/addons/stock/report/report_stock_picking.py b/addons/stock/report/report_stock_picking.py
deleted file mode 100644
index 81f20ed8906..00000000000
--- a/addons/stock/report/report_stock_picking.py
+++ /dev/null
@@ -1,113 +0,0 @@
-# -*- coding: utf-8 -*-
-##############################################################################
-#
-# OpenERP, Open Source Management Solution
-# Copyright (C) 2004-2010 Tiny SPRL ().
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero 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 Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see .
-#
-##############################################################################
-from osv import fields,osv
-import tools
-
-class report_stock_picking(osv.osv):
- _name = "report.stock.picking"
- _description = "Stock Picking Report"
- _auto = False
- _columns = {
- 'year': fields.char('Year',size=64,required=False, readonly=True),
- 'month':fields.selection([('01','January'), ('02','February'), ('03','March'), ('04','April'), ('05','May'), ('06','June'),
- ('07','July'), ('08','August'), ('09','September'), ('10','October'), ('11','November'), ('12','December')],'Month',readonly=True),
- 'day': fields.char('Day', size=128, readonly=True),
- 'nbr': fields.integer('# of Lines', readonly=True),
- 'nbp': fields.integer('# of Picking', readonly=True),
- 'partner_id':fields.many2one('res.partner', 'Partner', readonly=True),
- 'product_qty': fields.float('# of Products', readonly=True),
- 'product_uos_qty': fields.float('# of Products', readonly=True),
- 'product_id': fields.many2one('product.product', 'Product', readonly=True),
- 'date': fields.date('Date', readonly=True),
- 'avg_days_late': fields.float('Avg Due Days', digits=(16,2), readonly=True, group_operator="avg"),
- 'avg_days_to_deliver': fields.float('Avg Days to Deliver', digits=(16,2), readonly=True, group_operator="avg",
- help="Number of Avg Days to deliver"),
- 'state': fields.selection([('draft', 'Draft'),('auto', 'Waiting'),('confirmed', 'Confirmed'),('assigned', 'Available'),('done', 'Done'),('cancel', 'Cancelled')], 'State'),
- 'type': fields.selection([('out', 'Sending Goods'), ('in', 'Getting Goods'), ('internal', 'Internal'), ('delivery', 'Delivery')], 'Shipping Type', required=True),
- 'company_id': fields.many2one('res.company', 'Company', readonly=True),
- 'invoice_state': fields.selection([
- ("invoiced", "Invoiced"),
- ("2binvoiced", "To Be Invoiced"),
- ("none", "Not from Picking")], "Invoice Status",readonly=True),
- 'min_date': fields.date('Expected Date',help="Expected date for Picking. Default it takes current date"),
- 'order_date': fields.date('Order Date', help="Date of Order"),
- 'date_done': fields.date('Date Done', help="Date of completion"),
- 'location_id': fields.many2one('stock.location', 'Source Location', help="Sets a location if you produce at a fixed location. This can be a partner location if you subcontract the manufacturing operations."),
- 'location_dest_id': fields.many2one('stock.location', 'Dest. Location', help="Location where the system will stock the finished products."),
- 'max_date': fields.date('Max.Expected Date'),
- 'product_uos': fields.many2one('product.uom', 'Product UOS'),
- 'product_uom': fields.many2one('product.uom', 'Product UOM'),
- }
- def init(self, cr):
- tools.drop_view_if_exists(cr, 'report_stock_picking')
- cr.execute("""
- create or replace view report_stock_picking as (
- select
- min(sm.id) as id,
- date_trunc('day',sp.min_date) as min_date,
- date_trunc('day',sp.date) as order_date,
- date_trunc('day',sp.date_done) as date_done,
- date_trunc('day',sp.max_date) as max_date,
- to_char(sp.create_date, 'YYYY') as year,
- to_char(sp.create_date, 'MM') as month,
- to_char(sp.create_date, 'YYYY-MM-DD') as day,
- sp.address_id as partner_id,
- to_date(to_char(sp.create_date, 'MM-dd-YYYY'),'MM-dd-YYYY') as date,
- count(sm.id) as nbr,
- count(sp.id) as nbp,
- sum(sm.product_qty*u.factor) as product_qty,
- sum(sm.product_uos_qty) as product_uos_qty,
- sm.product_id as product_id,
- sm.location_dest_id as location_dest_id,
- sm.location_id as location_id,
- sp.type,
- sm.product_uos,
- sm.product_uom,
- sp.invoice_state,
- sp.company_id as company_id,
- avg(extract('epoch' from (sp.date_done-sp.create_date)))/(3600*24) as avg_days_to_deliver,
- avg(extract('epoch' from (sp.date_done-sp.min_date)))/(3600*24) as avg_days_late,
- sp.state
- from stock_picking as sp
- Inner join stock_move as sm ON (sm.picking_id=sp.id)
- Inner join product_uom u on (u.id=sm.product_uom)
- group by sp.type,
- sp.create_date,
- sp.address_id,
- sm.product_id,
- to_char(sp.create_date, 'YYYY'),
- sm.location_dest_id,
- sm.location_id,
- to_char(sp.create_date, 'MM'),
- to_char(sp.create_date, 'YYYY-MM-DD'),
- sm.product_uos,
- sm.product_uom,
- date_trunc('day',sp.min_date),
- date_trunc('day',sp.date),
- date_trunc('day',sp.date_done),
- date_trunc('day',sp.max_date),
- sp.invoice_state,
- sp.company_id,
- sp.state
-
- )""")
-report_stock_picking()
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/stock/report/report_stock_picking_view.xml b/addons/stock/report/report_stock_picking_view.xml
deleted file mode 100644
index edbd9e66b7d..00000000000
--- a/addons/stock/report/report_stock_picking_view.xml
+++ /dev/null
@@ -1,138 +0,0 @@
-
-
-
-
-
-
- report.stock.picking.tree
- report.stock.picking
- tree
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- report.stock.picking.graph
- report.stock.picking
- graph
-
-
-
-
-
-
-
-
-
-
-
- report.stock.picking.select
- report.stock.picking
- search
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Picking
- report.stock.picking
- form
- tree,graph
-
-
- {'search_default_Partner':1,'search_default_month':1,'group_by_no_leaf':1,'group_by':[]}
-
-
-
-
- tree
-
-
-
-
-
-
- graph
-
-
-
-
-
-
-
-
-
diff --git a/addons/stock/security/ir.model.access.csv b/addons/stock/security/ir.model.access.csv
index a120ce0c571..e879a03e9e1 100644
--- a/addons/stock/security/ir.model.access.csv
+++ b/addons/stock/security/ir.model.access.csv
@@ -32,17 +32,12 @@
"access_stock_lines_date_manager","report.stock.lines.date manager","model_report_stock_lines_date","stock.group_stock_manager",1,1,1,1
"access_stock_lines_date_system","report.stock.lines.date system","model_report_stock_lines_date","base.group_system",1,0,0,0
"access_stock_report_tracklots","stock.report.tracklots","model_stock_report_tracklots","stock.group_stock_user",1,1,1,1
-"access_report_products_to_received_planned","report.products.to.received.planned","model_report_products_to_received_planned","stock.group_stock_user",1,1,1,1
-"access_report_delivery_products_planned","report.delivery.products.planned","model_report_delivery_products_planned","stock.group_stock_user",1,1,1,1
"access_report_stock_move_user","report.stock.move user","model_report_stock_move","stock.group_stock_user",1,0,0,0
"access_report_stock_move_system","report.stock.move system","model_report_stock_move","base.group_system",1,0,0,0
"access_report_stock_move_manager","report.stock.move manager","model_report_stock_move","stock.group_stock_manager",1,1,1,1
"access_report_stock_inventory_user","report.stock.inventory user","model_report_stock_inventory","stock.group_stock_user",1,1,1,1
"access_report_stock_inventory_manager","report.stock.inventory manager","model_report_stock_inventory","stock.group_stock_manager",1,1,1,1
"access_report_stock_inventory_system","report.stock.inventory system","model_report_stock_inventory","base.group_system",1,0,0,0
-"access_report_stock_picking_user","report.stock.picking user","model_report_stock_picking","stock.group_stock_user",1,1,1,1
-"access_report_stock_picking_manager","report.stock.picking manager","model_report_stock_picking","stock.group_stock_manager",1,1,1,1
-"access_report_stock_picking_system","report.stock.picking system","model_report_stock_picking","base.group_system",1,0,0,0
"access_stock_warehouse_system","stock.warehouse.system","model_stock_warehouse","base.group_system",1,1,1,1
"access_stock_location_system","stock.location.system","model_stock_location","base.group_system",1,1,1,1
"access_stock_journal_system","stock.journal.system","model_stock_journal","base.group_system",1,1,1,1
diff --git a/addons/stock/stock.py b/addons/stock/stock.py
index 966564afb78..989fc50cc31 100644
--- a/addons/stock/stock.py
+++ b/addons/stock/stock.py
@@ -25,6 +25,7 @@ from operator import itemgetter
from itertools import groupby
from osv import fields, osv
+from tools import config
from tools.translate import _
import netsvc
import tools
@@ -61,7 +62,6 @@ class stock_journal(osv.osv):
}
stock_journal()
-
#----------------------------------------------------------
# Stock Location
#----------------------------------------------------------
@@ -105,6 +105,7 @@ class stock_location(osv.osv):
res[m.id] = _get_one_full_name(m)
return res
+
def _product_value(self, cr, uid, ids, field_names, arg, context=None):
"""Computes stock value (real and virtual) for a product, as well as stock qty (real and virtual).
@param field_names: Name of field
@@ -124,7 +125,6 @@ class stock_location(osv.osv):
currency_obj = self.pool.get('res.currency')
currency = self.pool.get('res.currency').browse(cr, uid, currency_id)
currency_obj.round(cr, uid, currency, 300)
-
for loc_id, product_ids in products_by_location.items():
c = (context or {}).copy()
c['location'] = loc_id
@@ -144,7 +144,6 @@ class stock_location(osv.osv):
amount = prod.virtual_available * prod.standard_price
amount = currency_obj.round(cr, uid, currency, amount)
result[loc_id][f] += amount
-
return result
_columns = {
@@ -200,8 +199,8 @@ class stock_location(osv.osv):
'parent_left': fields.integer('Left Parent', select=1),
'parent_right': fields.integer('Right Parent', select=1),
- 'stock_real_value': fields.function(_product_value, method=True, type='float', string='Real Stock Value', multi="stock"),
- 'stock_virtual_value': fields.function(_product_value, method=True, type='float', string='Virtual Stock Value', multi="stock"),
+ 'stock_real_value': fields.function(_product_value, method=True, type='float', string='Real Stock Value', multi="stock", digits_compute=dp.get_precision('Account')),
+ 'stock_virtual_value': fields.function(_product_value, method=True, type='float', string='Virtual Stock Value', multi="stock", digits_compute=dp.get_precision('Account')),
'company_id': fields.many2one('res.company', 'Company', select=1, help='Let this field empty if this location is shared between all companies'),
'scrap_location': fields.boolean('Scrap Location', help='Check this box to allow using this location to put scrapped/damaged goods.'),
}
@@ -262,8 +261,6 @@ class stock_location(osv.osv):
context = {}
product_obj = self.pool.get('product.product')
# Take the user company and pricetype
- price_type_id = self.pool.get('res.users').browse(cr, uid, uid).company_id.property_valuation_price_type.id
- pricetype = self.pool.get('product.price.type').browse(cr, uid, price_type_id)
context['currency_id'] = self.pool.get('res.users').browse(cr, uid, uid).company_id.currency_id.id
# To be able to offer recursive or non-recursive reports we need to prevent recursive quantities by default
@@ -302,7 +299,7 @@ class stock_location(osv.osv):
# Compute based on pricetype
# Choose the right filed standard_price to read
- amount_unit = product.price_get(pricetype.field, context)[product.id]
+ amount_unit = product.price_get('standard_price', context)[product.id]
price = qty[product_id] * amount_unit
total_price += price
@@ -540,12 +537,12 @@ class stock_picking(osv.osv):
ids = [ids]
for pick in self.browse(cr, uid, ids, context):
sql_str = """update stock_move set
- date_planned='%s'
+ date='%s'
where
picking_id=%d """ % (value, pick.id)
if pick.max_date:
- sql_str += " and (date_planned='" + pick.max_date + "' or date_planned>'" + value + "')"
+ sql_str += " and (date='" + pick.max_date + "' or date>'" + value + "')"
cr.execute(sql_str)
return True
@@ -562,11 +559,11 @@ class stock_picking(osv.osv):
ids = [ids]
for pick in self.browse(cr, uid, ids, context=context):
sql_str = """update stock_move set
- date_planned='%s'
+ date='%s'
where
picking_id=%s """ % (value, pick.id)
if pick.min_date:
- sql_str += " and (date_planned='" + pick.min_date + "' or date_planned<'" + value + "')"
+ sql_str += " and (date='" + pick.min_date + "' or date<'" + value + "')"
cr.execute(sql_str)
return True
@@ -581,8 +578,8 @@ class stock_picking(osv.osv):
return res
cr.execute("""select
picking_id,
- min(date_planned),
- max(date_planned)
+ min(date),
+ max(date)
from
stock_move
where
@@ -628,14 +625,15 @@ class stock_picking(osv.osv):
"* Done: has been processed, can't be modified or cancelled anymore\n"\
"* Cancelled: has been cancelled, can't be confirmed anymore"),
'min_date': fields.function(get_min_max_date, fnct_inv=_set_minimum_date, multi="min_max_date",
- method=True, store=True, type='datetime', string='Expected Date', select=1, help="Expected date for the picking to be processed. Will be set to date of actual processing if not specified."),
+ method=True, store=True, type='datetime', string='Expected Date', select=1, help="Expected date for the picking to be processed"),
'date': fields.datetime('Order Date', help="Date of Order", select=True),
'date_done': fields.datetime('Date Done', help="Date of Completion"),
'max_date': fields.function(get_min_max_date, fnct_inv=_set_maximum_date, multi="min_max_date",
method=True, store=True, type='datetime', string='Max. Expected Date', select=2),
'move_lines': fields.one2many('stock.move', 'picking_id', 'Internal Moves', states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}),
'auto_picking': fields.boolean('Auto-Picking'),
- 'address_id': fields.many2one('res.partner.address', 'Partner', help="Address of partner"),
+ 'address_id': fields.many2one('res.partner.address', 'Address', help="Address of partner"),
+ 'partner_id': fields.related('address_id','partner_id',type='many2one',relation='res.partner',string='Partner',store=True),
'invoice_state': fields.selection([
("invoiced", "Invoiced"),
("2binvoiced", "To Be Invoiced"),
@@ -837,6 +835,7 @@ class stock_picking(osv.osv):
""" Gets payment term from partner.
@return: Payment term
"""
+ partner_obj = self.pool.get('res.partner')
partner = picking.address_id.partner_id
return partner.property_payment_term and partner.property_payment_term.id or False
@@ -867,11 +866,8 @@ class stock_picking(osv.osv):
if type in ('in_invoice', 'in_refund'):
# Take the user company and pricetype
- price_type_id = self.pool.get('res.users').browse(cr, uid, uid).company_id.property_valuation_price_type.id
- pricetype = self.pool.get('product.price.type').browse(cr, uid, price_type_id)
context['currency_id'] = move_line.company_id.currency_id.id
-
- amount_unit = move_line.product_id.price_get(pricetype.field, context)[move_line.product_id.id]
+ amount_unit = move_line.product_id.price_get('standard_price', context)[move_line.product_id.id]
return amount_unit
else:
return move_line.product_id.list_price
@@ -1017,12 +1013,6 @@ class stock_picking(osv.osv):
uos_id = move_line.product_uom.id
account_id = self.pool.get('account.fiscal.position').map_account(cr, uid, partner.property_account_position, account_id)
- notes = False
- if ('sale_line_id' in move_line._columns.keys()) and move_line.sale_line_id:
- notes = move_line.sale_line_id.notes
- elif ('purchase_line_id' in move_line._columns.keys()) and move_line.purchase_line_id:
- notes = move_line.purchase_line_id.notes
-
invoice_line_id = invoice_line_obj.create(cr, uid, {
'name': name,
'origin': origin,
@@ -1035,7 +1025,6 @@ class stock_picking(osv.osv):
'quantity': move_line.product_uos_qty or move_line.product_qty,
'invoice_line_tax_id': [(6, 0, tax_ids)],
'account_analytic_id': account_analytic_id,
- 'note': notes,
}, context=context)
self._invoice_line_hook(cr, uid, move_line, invoice_line_id)
@@ -1074,7 +1063,15 @@ class stock_picking(osv.osv):
if move.state not in ('cancel',):
return False
return True
-
+
+ def allow_cancel(self, cr, uid, ids, context={}):
+ for pick in self.browse(cr, uid, ids, context=context):
+ if not pick.move_lines:
+ return True
+ for move in pick.move_lines:
+ if move.state == 'done':
+ raise osv.except_osv(_('Error'), _('You cannot cancel picking because stock move is in done state !'))
+ return True
def unlink(self, cr, uid, ids, context=None):
move_obj = self.pool.get('stock.move')
if context is None:
@@ -1094,6 +1091,7 @@ class stock_picking(osv.osv):
return super(stock_picking, self).unlink(cr, uid, ids, context=context)
+ # FIXME: needs refactoring, this code is partially duplicated in stock_move.do_partial()!
def do_partial(self, cr, uid, ids, partial_datas, context=None):
""" Makes partial picking and moves done.
@param partial_datas : Dictionary containing details of partial picking
@@ -1101,19 +1099,29 @@ class stock_picking(osv.osv):
delivery moves with product_id, product_qty, uom
@return: Dictionary of values
"""
+ import pdb
+ #pdb.set_trace()
+ if context is None:
+ context = {}
+ else:
+ context = dict(context)
res = {}
move_obj = self.pool.get('stock.move')
product_obj = self.pool.get('product.product')
currency_obj = self.pool.get('res.currency')
- users_obj = self.pool.get('res.users')
uom_obj = self.pool.get('product.uom')
price_type_obj = self.pool.get('product.price.type')
sequence_obj = self.pool.get('ir.sequence')
wf_service = netsvc.LocalService("workflow")
+ partner_id = partial_datas.get('partner_id', False)
+ address_id = partial_datas.get('address_id', False)
+ delivery_date = partial_datas.get('delivery_date', False)
for pick in self.browse(cr, uid, ids, context=context):
new_picking = None
+ new_moves = []
complete, too_many, too_few = [], [], []
move_product_qty = {}
+ prodlot_ids = {}
for move in pick.move_lines:
if move.state in ('done', 'cancel'):
continue
@@ -1124,6 +1132,8 @@ class stock_picking(osv.osv):
product_uom = partial_data.get('product_uom',False)
product_price = partial_data.get('product_price',0.0)
product_currency = partial_data.get('product_currency',False)
+ prodlot_id = partial_data.get('prodlot_id')
+ prodlot_ids[move.id] = prodlot_id
if move.product_qty == product_qty:
complete.append(move)
elif move.product_qty > product_qty:
@@ -1134,35 +1144,36 @@ class stock_picking(osv.osv):
# Average price computation
if (pick.type == 'in') and (move.product_id.cost_method == 'average'):
product = product_obj.browse(cr, uid, move.product_id.id)
- user = users_obj.browse(cr, uid, uid)
- context['currency_id'] = move.company_id.currency_id.id
+ move_currency_id = move.company_id.currency_id.id
+ context['currency_id'] = move_currency_id
qty = uom_obj._compute_qty(cr, uid, product_uom, product_qty, product.uom_id.id)
- pricetype = False
- if user.company_id.property_valuation_price_type:
- pricetype = price_type_obj.browse(cr, uid, user.company_id.property_valuation_price_type.id)
- if pricetype and qty > 0:
+ if qty > 0:
new_price = currency_obj.compute(cr, uid, product_currency,
- user.company_id.currency_id.id, product_price)
+ move_currency_id, product_price)
new_price = uom_obj._compute_price(cr, uid, product_uom, new_price,
product.uom_id.id)
if product.qty_available <= 0:
new_std_price = new_price
else:
# Get the standard price
- amount_unit = product.price_get(pricetype.field, context)[product.id]
+ amount_unit = product.price_get('standard_price', context)[product.id]
new_std_price = ((amount_unit * product.qty_available)\
+ (new_price * qty))/(product.qty_available + qty)
# Write the field according to price type field
- product_obj.write(cr, uid, [product.id],
- {pricetype.field: new_std_price})
- move_obj.write(cr, uid, [move.id], {'price_unit': new_price})
+ product_obj.write(cr, uid, [product.id], {'standard_price': new_std_price})
+
+ # Record the values that were chosen in the wizard, so they can be
+ # used for inventory valuation if real-time valuation is enabled.
+ move_obj.write(cr, uid, [move.id],
+ {'price_unit': product_price,
+ 'price_currency_id': product_currency})
for move in too_few:
product_qty = move_product_qty[move.id]
- if not new_picking:
+ if not new_picking:
new_picking = self.copy(cr, uid, pick.id,
{
'name': sequence_obj.get(cr, uid, 'stock.picking.%s'%(pick.type)),
@@ -1170,48 +1181,48 @@ class stock_picking(osv.osv):
'state':'draft',
})
if product_qty != 0:
-
- move_obj.copy(cr, uid, move.id,
- {
+ defaults = {
'product_qty' : product_qty,
'product_uos_qty': product_qty, #TODO: put correct uos_qty
'picking_id' : new_picking,
'state': 'assigned',
'move_dest_id': False,
'price_unit': move.price_unit,
- })
+ }
+ prodlot_id = prodlot_ids[move.id]
+ if prodlot_id:
+ defaults.update(prodlot_id=prodlot_id)
+ new_obj = move_obj.copy(cr, uid, move.id, defaults)
move_obj.write(cr, uid, [move.id],
{
'product_qty' : move.product_qty - product_qty,
'product_uos_qty':move.product_qty - product_qty, #TODO: put correct uos_qty
-
})
if new_picking:
move_obj.write(cr, uid, [c.id for c in complete], {'picking_id': new_picking})
- for move in too_many:
- product_qty = move_product_qty[move.id]
- move_obj.write(cr, uid, [move.id],
- {
- 'product_qty' : product_qty,
- 'product_uos_qty': product_qty, #TODO: put correct uos_qty
- 'picking_id': new_picking,
- })
- else:
- for move in too_many:
- product_qty = move_product_qty[move.id]
- move_obj.write(cr, uid, [move.id],
- {
- 'product_qty': product_qty,
- 'product_uos_qty': product_qty #TODO: put correct uos_qty
- })
+ for move in complete:
+ if prodlot_ids.get(move.id):
+ move_obj.write(cr, uid, move.id, {'prodlot_id': prodlot_ids[move.id]})
+ for move in too_many:
+ product_qty = move_product_qty[move.id]
+ defaults = {
+ 'product_qty' : product_qty,
+ 'product_uos_qty': product_qty, #TODO: put correct uos_qty
+ }
+ prodlot_id = prodlot_ids.get(move.id)
+ if prodlot_ids.get(move.id):
+ defaults.update(prodlot_id=prodlot_id)
+ if new_picking:
+ defaults.update(picking_id=new_picking)
+ move_obj.write(cr, uid, [move.id], defaults)
+
# At first we confirm the new picking (if necessary)
if new_picking:
wf_service.trg_validate(uid, 'stock.picking', new_picking, 'button_confirm', cr)
- # Then we finish the good picking
- if new_picking:
+ # Then we finish the good picking
self.write(cr, uid, [pick.id], {'backorder_id': new_picking})
self.action_move(cr, uid, [new_picking])
wf_service.trg_validate(uid, 'stock.picking', new_picking, 'button_done', cr)
@@ -1326,7 +1337,9 @@ class stock_production_lot(osv.osv):
'prefix': fields.char('Prefix', size=64, help="Optional prefix to prepend when displaying this serial number: PREFIX/SERIAL [INT_REF]"),
'product_id': fields.many2one('product.product', 'Product', required=True),
'date': fields.datetime('Creation Date', required=True),
- 'stock_available': fields.function(_get_stock, fnct_search=_stock_search, method=True, type="float", string="Available", select="2", help="Current quantity of products with this Production Lot Number available in company warehouses"),
+ 'stock_available': fields.function(_get_stock, fnct_search=_stock_search, method=True, type="float", string="Available", select=True,
+ help="Current quantity of products with this Production Lot Number available in company warehouses",
+ digits_compute=dp.get_precision('Product UoM')),
'revisions': fields.one2many('stock.production.lot.revision', 'lot_id', 'Revisions'),
'company_id': fields.many2one('res.company', 'Company', select=True),
'move_ids': fields.one2many('stock.move', 'prodlot_id', 'Moves for this production lot', readonly=True),
@@ -1426,20 +1439,19 @@ class stock_move(osv.osv):
_columns = {
'name': fields.char('Name', size=64, required=True, select=True),
'priority': fields.selection([('0', 'Not urgent'), ('1', 'Urgent')], 'Priority'),
+ 'create_date': fields.datetime('Creation Date', readonly=True),
+ 'date': fields.datetime('Date', required=True, help="Move date: scheduled date until move is done, then date of actual move processing", readonly=True),
+ 'date_expected': fields.datetime('Scheduled Date', required=True, help="Scheduled date for the processing of this move"),
+ 'product_id': fields.many2one('product.product', 'Product', required=True, select=True, domain=[('type','<>','service')],states={'done': [('readonly', True)]}),
- 'date': fields.datetime('Creation Date', select=True),
- 'date_planned': fields.datetime('Date', required=True, help="Scheduled date for the movement of the products or real date if the move is done."),
- 'date_expected': fields.datetime('Date Expected', readonly=True,required=True, help="Scheduled date for the movement of the products"),
- 'product_id': fields.many2one('product.product', 'Product', required=True, select=True, domain=[('type','<>','service')]),
-
- 'product_qty': fields.float('Quantity', required=True),
- 'product_uom': fields.many2one('product.uom', 'Unit of Measure', required=True),
- 'product_uos_qty': fields.float('Quantity (UOS)'),
+ 'product_qty': fields.float('Quantity', digits_compute=dp.get_precision('Product UoM'), required=True,states={'done': [('readonly', True)]}),
+ 'product_uom': fields.many2one('product.uom', 'Unit of Measure', required=True,states={'done': [('readonly', True)]}),
+ 'product_uos_qty': fields.float('Quantity (UOS)', digits_compute=dp.get_precision('Product UoM')),
'product_uos': fields.many2one('product.uom', 'Product UOS'),
'product_packaging': fields.many2one('product.packaging', 'Packaging', help="It specifies attributes of packaging like type, quantity of packaging,etc."),
- 'location_id': fields.many2one('stock.location', 'Source Location', required=True, select=True, help="Sets a location if you produce at a fixed location. This can be a partner location if you subcontract the manufacturing operations."),
- 'location_dest_id': fields.many2one('stock.location', 'Destination Location', required=True, select=True, help="Location where the system will stock the finished products."),
+ 'location_id': fields.many2one('stock.location', 'Source Location', required=True, select=True,states={'done': [('readonly', True)]}, help="Sets a location if you produce at a fixed location. This can be a partner location if you subcontract the manufacturing operations."),
+ 'location_dest_id': fields.many2one('stock.location', 'Destination Location', required=True,states={'done': [('readonly', True)]}, select=True, help="Location where the system will stock the finished products."),
'address_id': fields.many2one('res.partner.address', 'Destination Address', help="Optional address where goods are to be delivered, specifically used for allotment"),
'prodlot_id': fields.many2one('stock.production.lot', 'Production Lot', help="Production lot is used to put a serial number on the production", select=True),
@@ -1450,13 +1462,13 @@ class stock_move(osv.osv):
'move_dest_id': fields.many2one('stock.move', 'Destination Move', help="Optional: next stock move when chaining them", select=True),
'move_history_ids': fields.many2many('stock.move', 'stock_move_history_ids', 'parent_id', 'child_id', 'Move History (child moves)'),
'move_history_ids2': fields.many2many('stock.move', 'stock_move_history_ids', 'child_id', 'parent_id', 'Move History (parent moves)'),
- 'picking_id': fields.many2one('stock.picking', 'Picking List', select=True),
+ 'picking_id': fields.many2one('stock.picking', 'Picking List', select=True,states={'done': [('readonly', True)]}),
'note': fields.text('Notes'),
'state': fields.selection([('draft', 'Draft'), ('waiting', 'Waiting'), ('confirmed', 'Confirmed'), ('assigned', 'Available'), ('done', 'Done'), ('cancel', 'Cancelled')], 'State', readonly=True, select=True,
help='When the stock move is created it is in the \'Draft\' state.\n After that it is set to \'Confirmed\' state.\n If stock is available state is set to \'Available\'.\n When the picking is done the state is \'Done\'.\
\nThe state is \'Waiting\' if the move is waiting for another one.'),
- 'price_unit': fields.float('Unit Price',
- digits_compute= dp.get_precision('Account')),
+ 'price_unit': fields.float('Unit Price', digits_compute= dp.get_precision('Account'), help="Technical field used to record the product cost set by the user during a picking confirmation (when average price costing method is used)"),
+ 'price_currency_id': fields.many2one('res.currency', 'Currency for average price', help="Technical field used to record the currency chosen by the user during a picking confirmation (when average price costing method is used)"),
'company_id': fields.many2one('res.company', 'Company', required=True, select=True),
'partner_id': fields.related('picking_id','address_id','partner_id',type='many2one', relation="res.partner", string="Partner", store=True, select=True),
'backorder_id': fields.related('picking_id','backorder_id',type='many2one', relation="stock.picking", string="Back Order", select=True),
@@ -1509,7 +1521,6 @@ class stock_move(osv.osv):
'priority': '1',
'product_qty': 1.0,
'scrapped' : False,
- 'date_planned': time.strftime('%Y-%m-%d %H:%M:%S'),
'date': time.strftime('%Y-%m-%d %H:%M:%S'),
'company_id': lambda self,cr,uid,c: self.pool.get('res.company')._company_default_get(cr, uid, 'stock.move', context=c),
'date_expected': time.strftime('%Y-%m-%d %H:%M:%S'),
@@ -1517,9 +1528,9 @@ class stock_move(osv.osv):
def write(self, cr, uid, ids, vals, context=None):
if uid != 1:
+ frozen_fields = set(['product_qty', 'product_uom', 'product_uos_qty', 'product_uos', 'location_id', 'location_dest_id', 'product_id'])
for move in self.browse(cr, uid, ids):
if move.state == 'done':
- frozen_fields = set(['product_qty', 'product_uom', 'product_uos_qty', 'product_uos', 'location_id', 'location_dest_id', 'product_id'])
if frozen_fields.intersection(vals):
raise osv.except_osv(_('Operation forbidden'),
_('Quantities, UoMs, Products and Locations cannot be modified on stock moves that have already been processed (except by the Administrator)'))
@@ -1642,20 +1653,37 @@ class stock_move(osv.osv):
if dest:
if dest[1] == 'transparent':
self.write(cr, uid, [m.id], {
- 'date_planned': (datetime.strptime(m.date_planned, '%Y-%m-%d %H:%M:%S') + \
+ 'date': (datetime.strptime(m.date, '%Y-%m-%d %H:%M:%S') + \
relativedelta(days=dest[2] or 0)).strftime('%Y-%m-%d'),
'location_dest_id': dest[0].id})
else:
result.setdefault(m.picking_id, [])
result[m.picking_id].append( (m, dest) )
return result
-
+ def _create_chained_picking(self, cr, uid, pick_name,picking,ptype,move, context=None):
+ res_obj = self.pool.get('res.company')
+ picking_obj = self.pool.get('stock.picking')
+ pick_id= picking_obj.create(cr, uid, {
+ 'name': pick_name,
+ 'origin': str(picking.origin or ''),
+ 'type': ptype,
+ 'note': picking.note,
+ 'move_type': picking.move_type,
+ 'auto_picking': move[0][1][1] == 'auto',
+ 'stock_journal_id': move[0][1][3],
+ 'company_id': move[0][1][4] or res_obj._company_default_get(cr, uid, 'stock.company', context=context),
+ 'address_id': picking.address_id.id,
+ 'invoice_state': 'none',
+ 'date': picking.date,
+ })
+ return pick_id
def action_confirm(self, cr, uid, ids, context=None):
""" Confirms stock move.
@return: List of ids.
"""
moves = self.browse(cr, uid, ids)
self.write(cr, uid, ids, {'state': 'confirmed'})
+ i = 0
def create_chained_picking(self, cr, uid, moves, context=None):
new_moves = []
@@ -1674,20 +1702,7 @@ class stock_move(osv.osv):
pickid = check_picking_ids[0]
else:
if picking:
- pickid = picking_obj.create(cr, uid, {
- 'name': pick_name,
- 'origin': str(picking.origin or ''),
- 'type': ptype,
- 'note': picking.note,
- 'move_type': picking.move_type,
- 'auto_picking': todo[0][1][1] == 'auto',
- 'stock_journal_id': todo[0][1][3],
- 'company_id': todo[0][1][4] or res_obj._company_default_get(cr, uid, 'stock.company', context=context),
- 'address_id': picking.address_id.id,
- 'invoice_state': 'none',
- 'date': picking.date,
- 'sale_id':' sale_id' in picking._columns.keys() and picking.sale_id.id or False
- })
+ pickid = self._create_chained_picking(cr, uid, pick_name,picking,ptype,todo,context)
else:
pickid = False
for move, (loc, auto, delay, journal, company_id, ptype) in todo:
@@ -1699,7 +1714,7 @@ class stock_move(osv.osv):
'state': 'waiting',
'company_id': company_id or res_obj._company_default_get(cr, uid, 'stock.company', context=context) ,
'move_history_ids': [],
- 'date_planned': (datetime.strptime(move.date_planned, '%Y-%m-%d %H:%M:%S') + relativedelta(days=delay or 0)).strftime('%Y-%m-%d'),
+ 'date': (datetime.strptime(move.date, '%Y-%m-%d %H:%M:%S') + relativedelta(days=delay or 0)).strftime('%Y-%m-%d'),
'move_history_ids2': []}
)
move_obj.write(cr, uid, [move.id], {
@@ -1788,6 +1803,7 @@ class stock_move(osv.osv):
def setlast_tracking(self, cr, uid, ids, context=None):
tracking_obj = self.pool.get('stock.tracking')
+ tracking = context.get('tracking', False)
picking = self.browse(cr, uid, ids)[0].picking_id
if picking:
last_track = [line.tracking_id.id for line in picking.move_lines if line.tracking_id]
@@ -1831,54 +1847,121 @@ class stock_move(osv.osv):
wf_service.trg_trigger(uid, 'stock.move', id, cr)
return True
- def _get_accounting_values(self, cr, uid, move, context=None):
+ def _get_accounting_data_for_valuation(self, cr, uid, move, context=None):
+ """
+ Return the accounts and journal to use to post Journal Entries for the real-time
+ valuation of the move.
+
+ :param context: context dictionary that can explicitly mention the company to consider via the 'force_company' key
+ :raise: osv.except_osv() is any mandatory account or journal is not defined.
+ """
product_obj=self.pool.get('product.product')
- product_uom_obj = self.pool.get('product.uom')
- price_type_obj = self.pool.get('product.price.type')
- accounts = product_obj.get_product_accounts(cr,uid,move.product_id.id,context)
+ accounts = product_obj.get_product_accounts(cr, uid, move.product_id.id, context)
acc_src = accounts['stock_account_input']
acc_dest = accounts['stock_account_output']
acc_variation = accounts.get('property_stock_variation', False)
journal_id = accounts['stock_journal']
- if context is None:
- context = {}
-
if not acc_src:
- raise osv.except_osv(_('Error!'), _('There is no stock input account defined ' \
- 'for this product: "%s" (id: %d)') % \
+ raise osv.except_osv(_('Error!'), _('There is no stock input account defined for this product or its category: "%s" (id: %d)') % \
(move.product_id.name, move.product_id.id,))
if not acc_dest:
- raise osv.except_osv(_('Error!'), _('There is no stock output account defined ' \
- 'for this product: "%s" (id: %d)') % \
+ raise osv.except_osv(_('Error!'), _('There is no stock output account defined for this product or its category: "%s" (id: %d)') % \
(move.product_id.name, move.product_id.id,))
if not journal_id:
- raise osv.except_osv(_('Error!'), _('There is no journal defined '\
- 'on the product category: "%s" (id: %d)') % \
+ raise osv.except_osv(_('Error!'), _('There is no journal defined on the product category: "%s" (id: %d)') % \
(move.product_id.categ_id.name, move.product_id.categ_id.id,))
if not acc_variation:
- raise osv.except_osv(_('Error!'), _('There is no variation account defined '\
- 'on the product category: "%s" (id: %d)') % \
+ raise osv.except_osv(_('Error!'), _('There is no inventory variation account defined on the product category: "%s" (id: %d)') % \
(move.product_id.categ_id.name, move.product_id.categ_id.id,))
- if acc_src != acc_dest:
- default_uom = move.product_id.uom_id.id
- q = product_uom_obj._compute_qty(cr, uid, move.product_uom.id, move.product_qty, default_uom)
- if move.product_id.cost_method == 'average' and move.price_unit:
- amount = q * move.price_unit
- # Base computation on valuation price type
- else:
- context['currency_id'] = move.company_id.currency_id.id
- pricetype = price_type_obj.browse(cr,uid,move.company_id.property_valuation_price_type.id)
- amount_unit = move.product_id.price_get(pricetype.field, context)[move.product_id.id]
- amount = amount_unit * q or 1.0
- return journal_id, acc_src, acc_dest, acc_variation, amount
+
+ return journal_id, acc_src, acc_dest, acc_variation
+
+ def _get_reference_accounting_values_for_valuation(self, cr, uid, move, context=None):
+ """
+ Return the reference amount and reference currency representing the inventory valuation for this move.
+ These reference values should possibly be converted before being posted in Journals to adapt to the primary
+ and secondary currencies of the relevant accounts.
+ """
+ product_obj=self.pool.get('product.product')
+ product_uom_obj = self.pool.get('product.uom')
+ price_type_obj = self.pool.get('product.price.type')
+
+ # by default the reference currency is that of the move's company
+ reference_currency_id = move.company_id.currency_id.id
+
+ default_uom = move.product_id.uom_id.id
+ qty = product_uom_obj._compute_qty(cr, uid, move.product_uom.id, move.product_qty, default_uom)
+
+ # if product is set to average price and a specific value was entered in the picking wizard,
+ # we use it
+ if move.product_id.cost_method == 'average' and move.price_unit:
+ reference_amount = qty * move.price_unit
+ reference_currency_id = move.price_currency_id.id or reference_currency_id
+
+ # Otherwise we default to the company's valuation price type, considering that the values of the
+ # valuation field are expressed in the default currency of the move's company.
+ else:
+ if context is None:
+ context = {}
+ currency_ctx = dict(context, currency_id = move.company_id.currency_id.id)
+ amount_unit = move.product_id.price_get('standard_price', currency_ctx)[move.product_id.id]
+ reference_amount = amount_unit * qty or 1.0
+
+ return reference_amount, reference_currency_id
+
+
+ def _create_product_valuation_moves(self, cr, uid, move, context=None):
+ """
+ Generate the appropriate accounting moves if the product being moves is subject
+ to real_time valuation tracking, and the source or destination location is
+ a transit location or is outside of the company.
+ """
+ if move.product_id.valuation == 'real_time': # FIXME: product valuation should perhaps be a property?
+ if context is None:
+ context = {}
+ src_company_ctx = dict(context,force_company=move.location_id.company_id.id)
+ dest_company_ctx = dict(context,force_company=move.location_dest_id.company_id.id)
+ account_moves = []
+ # Outgoing moves (or cross-company output part)
+ if move.location_id.company_id \
+ and (move.location_id.usage == 'internal' and move.location_dest_id.usage != 'internal'\
+ or move.location_id.company_id != move.location_dest_id.company_id):
+ journal_id, acc_src, acc_dest, acc_variation = self._get_accounting_data_for_valuation(cr, uid, move, src_company_ctx)
+ reference_amount, reference_currency_id = self._get_reference_accounting_values_for_valuation(cr, uid, move, src_company_ctx)
+ account_moves += [(journal_id, self._create_account_move_line(cr, uid, move, acc_variation, acc_dest, reference_amount, reference_currency_id, context))]
+
+ # Incoming moves (or cross-company input part)
+ if move.location_dest_id.company_id \
+ and (move.location_id.usage != 'internal' and move.location_dest_id.usage == 'internal'\
+ or move.location_id.company_id != move.location_dest_id.company_id):
+ journal_id, acc_src, acc_dest, acc_variation = self._get_accounting_data_for_valuation(cr, uid, move, dest_company_ctx)
+ reference_amount, reference_currency_id = self._get_reference_accounting_values_for_valuation(cr, uid, move, src_company_ctx)
+ account_moves += [(journal_id, self._create_account_move_line(cr, uid, move, acc_src, acc_variation, reference_amount, reference_currency_id, context))]
+
+ move_obj = self.pool.get('account.move')
+ for j_id, move_lines in account_moves:
+ move_obj.create(cr, uid,
+ {'name': move.name,
+ 'journal_id': j_id,
+ 'line_id': move_lines,
+ 'ref': move.picking_id and move.picking_id.name})
+
def action_done(self, cr, uid, ids, context=None):
""" Makes the move done and if all moves are done, it will finish the picking.
@return:
"""
+ track_flag = False
+ partial_datas=''
picking_ids = []
- move_obj = self.pool.get('account.move')
+ product_uom_obj = self.pool.get('product.uom')
+ price_type_obj = self.pool.get('product.price.type')
+ product_obj = self.pool.get('product.product')
+ partial_obj=self.pool.get('stock.partial.picking')
+ partial_id=partial_obj.search(cr,uid,[])
+ if partial_id:
+ partial_datas=partial_obj.read(cr,uid,partial_id)[0]
if context is None:
context = {}
@@ -1904,44 +1987,11 @@ class stock_move(osv.osv):
if move.move_dest_id.auto_validate:
self.action_done(cr, uid, [move.move_dest_id.id], context=context)
- #
- # Accounting Entries
- #
- acc_src = None
- acc_dest = None
- if move.product_id.valuation == 'real_time':
- lines = []
- if ((move.location_id.usage == 'internal' and move.location_dest_id.usage == 'customer') or (move.location_id.usage == 'internal' and move.location_dest_id.usage == 'transit')):
- if move.location_id.company_id:
- context.update({'force_company': move.location_id.company_id.id})
- journal_id, acc_src, acc_dest, acc_variation, amount = self._get_accounting_values(cr, uid, move, context)
- lines = [(journal_id, self.create_account_move(cr, uid, move, acc_dest, acc_variation, amount, context))]
-
- elif ((move.location_id.usage == 'supplier' and move.location_dest_id.usage == 'internal') or (move.location_id.usage == 'transit' and move.location_dest_id.usage == 'internal')):
- if move.location_dest_id.company_id:
- context.update({'force_company': move.location_dest_id.company_id.id})
- journal_id, acc_src, acc_dest, acc_variation, amount = self._get_accounting_values(cr, uid, move, context)
- lines = [(journal_id, self.create_account_move(cr, uid, move, acc_variation, acc_src, amount, context))]
- elif (move.location_id.usage == 'internal' and move.location_dest_id.usage == 'internal' and move.location_id.company_id != move.location_dest_id.company_id):
- if move.location_id.company_id:
- context.update({'force_company': move.location_id.company_id.id})
- journal_id, acc_src, acc_dest, acc_variation, amount = self._get_accounting_values(cr, uid, move, context)
- line1 = [(journal_id, self.create_account_move(cr, uid, move, acc_dest, acc_variation, amount, context))]
- if move.location_dest_id.company_id:
- context.update({'force_company': move.location_dest_id.company_id.id})
- journal_id, acc_src, acc_dest, acc_variation, amount = self._get_accounting_values(cr, uid, move, context)
- line2 = [(journal_id, self.create_account_move(cr, uid, move, acc_variation, acc_src, amount, context))]
- lines = line1 + line2
- for j_id, line in lines:
- move_obj.create(cr, uid, {
- 'name': move.name,
- 'journal_id': j_id,
- 'type':'cont_voucher',
- 'line_id': line,
- 'ref': move.picking_id and move.picking_id.name,
- })
-
- self.write(cr, uid, ids, {'state': 'done', 'date_planned': time.strftime('%Y-%m-%d %H:%M:%S')})
+ self._create_product_valuation_moves(cr, uid, move, context=context)
+ prodlot_id =partial_datas and partial_datas.get('move%s_prodlot_id'%(move.id), False)
+ if prodlot_id:
+ self.write(cr, uid, [move.id], {'prodlot_id': prodlot_id})
+ self.write(cr, uid, ids, {'state': 'done', 'date': time.strftime('%Y-%m-%d %H:%M:%S')})
wf_service = netsvc.LocalService("workflow")
for id in ids:
wf_service.trg_trigger(uid, 'stock.move', id, cr)
@@ -1954,31 +2004,55 @@ class stock_move(osv.osv):
picking_obj.log_picking(cr, uid, picking_ids, context=context)
return True
- def create_account_move(self, cr, uid, move,account_id, account_variation, amount, context=None):
- if context is None:
- context = {}
- partner_id = move.picking_id.address_id and (move.picking_id.address_id.partner_id and move.picking_id.address_id.partner_id.id or False) or False
- lines=[(0, 0, {
- 'name': move.name,
- 'quantity': move.product_qty,
- 'product_id': move.product_id and move.product_id.id or False,
- 'credit': amount,
- 'account_id': account_id,
- 'ref': move.picking_id and move.picking_id.name or False,
- 'date': time.strftime('%Y-%m-%d') ,
- 'partner_id': partner_id,
- }),
- (0, 0, {
- 'name': move.name,
- 'product_id': move.product_id and move.product_id.id or False,
- 'quantity': move.product_qty,
- 'debit': amount,
- 'account_id': account_variation,
- 'ref': move.picking_id and move.picking_id.name or False,
- 'date': time.strftime('%Y-%m-%d') ,
- 'partner_id': partner_id,
- })]
- return lines
+ def _create_account_move_line(self, cr, uid, move, src_account_id, dest_account_id, reference_amount, reference_currency_id, context=None):
+ """
+ Generate the account.move.line values to post to track the stock valuation difference due to the
+ processing of the given stock move.
+ """
+ # prepare default values considering that the destination accounts have the reference_currency_id as their main currency
+ partner_id = (move.picking_id.address_id and move.picking_id.address_id.partner_id and move.picking_id.address_id.partner_id.id) or False
+ debit_line_vals = {
+ 'name': move.name,
+ 'product_id': move.product_id and move.product_id.id or False,
+ 'quantity': move.product_qty,
+ 'ref': move.picking_id and move.picking_id.name or False,
+ 'date': time.strftime('%Y-%m-%d'),
+ 'partner_id': partner_id,
+ 'debit': reference_amount,
+ 'account_id': dest_account_id,
+ }
+ credit_line_vals = {
+ 'name': move.name,
+ 'product_id': move.product_id and move.product_id.id or False,
+ 'quantity': move.product_qty,
+ 'ref': move.picking_id and move.picking_id.name or False,
+ 'date': time.strftime('%Y-%m-%d'),
+ 'partner_id': partner_id,
+ 'credit': reference_amount,
+ 'account_id': src_account_id,
+ }
+
+ # if we are posting to accounts in a different currency, provide correct values in both currencies correctly
+ # when compatible with the optional secondary currency on the account.
+ # Financial Accounts only accept amounts in secondary currencies if there's no secondary currency on the account
+ # or if it's the same as that of the secondary amount being posted.
+ account_obj = self.pool.get('account.account')
+ src_acct, dest_acct = account_obj.browse(cr, uid, [src_account_id, dest_account_id], context=context)
+ src_main_currency_id = src_acct.company_id.currency_id.id
+ dest_main_currency_id = dest_acct.company_id.currency_id.id
+ cur_obj = self.pool.get('res.currency')
+ if reference_currency_id != src_main_currency_id:
+ # fix credit line:
+ credit_line_vals['credit'] = cur_obj.compute(cr, uid, reference_currency_id, src_main_currency_id, reference_amount, context=context)
+ if (not src_acct.currency_id) or src_acct.currency_id.id == reference_currency_id:
+ credit_line_vals.update(currency_id=reference_currency_id, amount_currency=reference_amount)
+ if reference_currency_id != dest_main_currency_id:
+ # fix debit line:
+ debit_line_vals['debit'] = cur_obj.compute(cr, uid, reference_currency_id, dest_main_currency_id, reference_amount, context=context)
+ if (not dest_acct.currency_id) or dest_acct.currency_id.id == reference_currency_id:
+ debit_line_vals.update(currency_id=reference_currency_id, amount_currency=reference_amount)
+
+ return [(0, 0, debit_line_vals), (0, 0, credit_line_vals)]
def unlink(self, cr, uid, ids, context=None):
if context is None:
@@ -2167,26 +2241,34 @@ class stock_move(osv.osv):
return res
+ # FIXME: needs refactoring, this code is partially duplicated in stock_picking.do_partial()!
def do_partial(self, cr, uid, ids, partial_datas, context=None):
""" Makes partial pickings and moves done.
@param partial_datas: Dictionary containing details of partial picking
like partner_id, address_id, delivery_date, delivery
moves with product_id, product_qty, uom
"""
+ import pdb
+ #pdb.set_trace()
res = {}
picking_obj = self.pool.get('stock.picking')
product_obj = self.pool.get('product.product')
currency_obj = self.pool.get('res.currency')
- users_obj = self.pool.get('res.users')
uom_obj = self.pool.get('product.uom')
price_type_obj = self.pool.get('product.price.type')
+ sequence_obj = self.pool.get('ir.sequence')
wf_service = netsvc.LocalService("workflow")
+ partner_id = partial_datas.get('partner_id', False)
+ address_id = partial_datas.get('address_id', False)
+ delivery_date = partial_datas.get('delivery_date', False)
+ new_moves = []
if context is None:
context = {}
complete, too_many, too_few = [], [], []
move_product_qty = {}
+ prodlot_ids = {}
for move in self.browse(cr, uid, ids, context=context):
if move.state in ('done', 'cancel'):
continue
@@ -2197,6 +2279,7 @@ class stock_move(osv.osv):
product_uom = partial_data.get('product_uom',False)
product_price = partial_data.get('product_price',0.0)
product_currency = partial_data.get('product_currency',False)
+ prodlot_ids[move.id] = partial_data.get('prodlot_id')
if move.product_qty == product_qty:
complete.append(move)
elif move.product_qty > product_qty:
@@ -2207,42 +2290,46 @@ class stock_move(osv.osv):
# Average price computation
if (move.picking_id.type == 'in') and (move.product_id.cost_method == 'average'):
product = product_obj.browse(cr, uid, move.product_id.id)
- user = users_obj.browse(cr, uid, uid)
- context['currency_id'] = move.company_id.currency_id.id
+ move_currency_id = move.company_id.currency_id.id
+ context['currency_id'] = move_currency_id
qty = uom_obj._compute_qty(cr, uid, product_uom, product_qty, product.uom_id.id)
- pricetype = False
- if user.company_id.property_valuation_price_type:
- pricetype = price_type_obj.browse(cr, uid, user.company_id.property_valuation_price_type.id)
- if pricetype and qty > 0:
+ if qty > 0:
new_price = currency_obj.compute(cr, uid, product_currency,
- user.company_id.currency_id.id, product_price)
+ move_currency_id, product_price)
new_price = uom_obj._compute_price(cr, uid, product_uom, new_price,
product.uom_id.id)
if product.qty_available <= 0:
new_std_price = new_price
else:
# Get the standard price
- amount_unit = product.price_get(pricetype.field, context)[product.id]
+ amount_unit = product.price_get('standard_price', context)[product.id]
new_std_price = ((amount_unit * product.qty_available)\
+ (new_price * qty))/(product.qty_available + qty)
- # Write the field according to price type field
- product_obj.write(cr, uid, [product.id],
- {pricetype.field: new_std_price})
- self.write(cr, uid, [move.id], {'price_unit': new_price})
+ product_obj.write(cr, uid, [product.id],{'standard_price': new_std_price})
+
+ # Record the values that were chosen in the wizard, so they can be
+ # used for inventory valuation if real-time valuation is enabled.
+ self.write(cr, uid, [move.id],
+ {'price_unit': product_price,
+ 'price_currency_id': product_currency,
+ })
for move in too_few:
product_qty = move_product_qty[move.id]
if product_qty != 0:
- new_move = self.copy(cr, uid, move.id,
- {
- 'product_qty' : product_qty,
- 'product_uos_qty': product_qty,
- 'picking_id' : move.picking_id.id,
- 'state': 'assigned',
- 'move_dest_id': False,
- 'price_unit': move.price_unit,
- })
+ defaults = {
+ 'product_qty' : product_qty,
+ 'product_uos_qty': product_qty,
+ 'picking_id' : move.picking_id.id,
+ 'state': 'assigned',
+ 'move_dest_id': False,
+ 'price_unit': move.price_unit,
+ }
+ prodlot_id = prodlot_ids[move.id]
+ if prodlot_id:
+ defaults.update(prodlot_id=prodlot_id)
+ new_move = self.copy(cr, uid, move.id, defaults)
complete.append(self.browse(cr, uid, new_move))
self.write(cr, uid, move.id,
{
@@ -2260,6 +2347,8 @@ class stock_move(osv.osv):
complete.append(move)
for move in complete:
+ if prodlot_ids.get(move.id):
+ self.write(cr, uid, [move.id],{'prodlot_id': prodlot_ids.get(move.id)})
self.action_done(cr, uid, [move.id], context=context)
if move.picking_id.id :
# TOCHECK : Done picking if all moves are done
@@ -2273,10 +2362,7 @@ class stock_move(osv.osv):
picking_obj.action_move(cr, uid, [move.picking_id.id])
wf_service.trg_validate(uid, 'stock.picking', move.picking_id.id, 'button_done', cr)
- done_move_ids = []
- for move in complete:
- done_move_ids.append(move.id)
- return done_move_ids
+ return [move.id for move in complete]
stock_move()
@@ -2312,13 +2398,19 @@ class stock_inventory(osv.osv):
"""
if context is None:
context = {}
+
+ # to perform the correct inventory corrections we need analyze stock location by
+ # location, never recursively, so we use a special context
+ product_context = dict(context, compute_child=False)
+
+ location_obj = self.pool.get('stock.location')
for inv in self.browse(cr, uid, ids):
move_ids = []
+ move_line = []
for line in inv.inventory_line_id:
pid = line.product_id.id
-
- amount = self.pool.get('stock.location')._product_get(cr, uid, line.location_id.id, [pid], {'uom': line.product_uom.id})[pid]
- #TOCHECK: Why put restriction like new inventory qty should greater available qty ?
+ product_context.update(uom=line.product_uom.id)
+ amount = location_obj._product_get(cr, uid, line.location_id.id, [pid], product_context)[pid]
change = line.product_qty - amount
lot_id = line.prod_lot_id.id
if change:
@@ -2329,7 +2421,7 @@ class stock_inventory(osv.osv):
'product_uom': line.product_uom.id,
'prodlot_id': lot_id,
'date': inv.date,
- 'date_planned': inv.date,
+ 'date': inv.date,
'state': 'done'
}
if change > 0:
@@ -2383,7 +2475,7 @@ class stock_inventory_line(osv.osv):
'location_id': fields.many2one('stock.location', 'Location', required=True),
'product_id': fields.many2one('product.product', 'Product', required=True, select=True),
'product_uom': fields.many2one('product.uom', 'Product UOM', required=True),
- 'product_qty': fields.float('Quantity'),
+ 'product_qty': fields.float('Quantity', digits_compute=dp.get_precision('Product UoM')),
'company_id': fields.related('inventory_id','company_id',type='many2one',relation='res.company',string='Company',store=True, select=True),
'prod_lot_id': fields.many2one('stock.production.lot', 'Production Lot', domain="[('product_id','=',product_id)]"),
'state': fields.related('inventory_id','state',type='char',string='State',readonly=True),
@@ -2463,6 +2555,7 @@ class stock_picking_move_wizard(osv.osv_memory):
def action_move(self, cr, uid, ids, context=None):
move_obj = self.pool.get('stock.move')
picking_obj = self.pool.get('stock.picking')
+ account_move_obj = self.pool.get('account.move')
for act in self.read(cr, uid, ids):
move_lines = move_obj.browse(cr, uid, act['move_ids'])
for line in move_lines:
@@ -2479,75 +2572,5 @@ class stock_picking_move_wizard(osv.osv_memory):
stock_picking_move_wizard()
-class report_products_to_received_planned(osv.osv):
- _name = "report.products.to.received.planned"
- _description = "Received Products vs Planned"
- _auto = False
- _columns = {
- 'date':fields.date('Date'),
- 'qty': fields.integer('Actual Qty'),
- 'planned_qty': fields.integer('Planned Qty'),
-
- }
- def init(self, cr):
- tools.drop_view_if_exists(cr, 'report_products_to_received_planned')
- cr.execute("""
- create or replace view report_products_to_received_planned as (
- select stock.date, min(stock.id) as id, sum(stock.product_qty) as qty, 0 as planned_qty
- from stock_picking picking
- inner join stock_move stock
- on picking.id = stock.picking_id and picking.type = 'in'
- where stock.date between (select cast(date_trunc('week', current_date) as date)) and (select cast(date_trunc('week', current_date) as date) + 7)
- group by stock.date
-
- union
-
- select stock.date_planned, min(stock.id) as id, 0 as actual_qty, sum(stock.product_qty) as planned_qty
- from stock_picking picking
- inner join stock_move stock
- on picking.id = stock.picking_id and picking.type = 'in'
- where stock.date_planned between (select cast(date_trunc('week', current_date) as date)) and (select cast(date_trunc('week', current_date) as date) + 7)
- group by stock.date_planned
- )
- """)
-
-report_products_to_received_planned()
-
-class report_delivery_products_planned(osv.osv):
- _name = "report.delivery.products.planned"
- _description = "Delivered products vs Planned"
- _auto = False
- _columns = {
- 'date':fields.date('Date'),
- 'qty': fields.integer('Actual Qty'),
- 'planned_qty': fields.integer('Planned Qty'),
-
- }
-
- def init(self, cr):
- tools.drop_view_if_exists(cr, 'report_delivery_products_planned')
- cr.execute("""
- create or replace view report_delivery_products_planned as (
- select stock.date, min(stock.id) as id, sum(stock.product_qty) as qty, 0 as planned_qty
- from stock_picking picking
- inner join stock_move stock
- on picking.id = stock.picking_id and picking.type = 'out'
- where stock.date between (select cast(date_trunc('week', current_date) as date)) and (select cast(date_trunc('week', current_date) as date) + 7)
- group by stock.date
-
- union
-
- select stock.date_planned, min(stock.id), 0 as actual_qty, sum(stock.product_qty) as planned_qty
- from stock_picking picking
- inner join stock_move stock
- on picking.id = stock.picking_id and picking.type = 'out'
- where stock.date_planned between (select cast(date_trunc('week', current_date) as date)) and (select cast(date_trunc('week', current_date) as date) + 7)
- group by stock.date_planned
-
-
- )
- """)
-
-report_delivery_products_planned()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/stock/stock_data.xml b/addons/stock/stock_data.xml
index 6933db289a0..b9aaff33e72 100644
--- a/addons/stock/stock_data.xml
+++ b/addons/stock/stock_data.xml
@@ -114,7 +114,13 @@
-
+
+
+
+ Shipping Delay
+
+
+
diff --git a/addons/stock/stock_demo.xml b/addons/stock/stock_demo.xml
index 91acb5c6406..1249317a2e4 100644
--- a/addons/stock/stock_demo.xml
+++ b/addons/stock/stock_demo.xml
@@ -274,6 +274,7 @@
outdirect2binvoiced
+
diff --git a/addons/stock/stock_view.xml b/addons/stock/stock_view.xml
index 9e948ea2b77..fa81b7534c3 100644
--- a/addons/stock/stock_view.xml
+++ b/addons/stock/stock_view.xml
@@ -2,13 +2,13 @@
-
-
+
+
-
-
+
+
@@ -41,7 +41,7 @@
+ type="action" icon="terp-stock_effects-object-colorize"/>
@@ -85,45 +85,57 @@
stock.inventoryform
-
@@ -342,13 +369,13 @@
{'full':'1',"search_default_available":1}
-
+
#
# Lot composition (history)
#
- Inventory Moves
+ Stock Movesstock.movetreemove_history_ids
@@ -363,8 +390,8 @@
-
-
+
+
@@ -373,7 +400,7 @@
- Inventory Moves
+ Stock Movesstock.movetreemove_history_ids2
@@ -388,8 +415,8 @@
-
-
+
+
@@ -481,7 +508,7 @@
{'full':1, 'search_default_in_location':1}
-
+ stock.location.tree
@@ -502,7 +529,7 @@
tree
-
+ stock.warehouse
@@ -541,7 +568,7 @@
form
-
+ stock.picking.move.wizard.form
@@ -554,7 +581,7 @@
-
+
@@ -597,7 +624,7 @@
-
+
@@ -624,7 +651,7 @@
-
+
@@ -636,7 +663,6 @@
states="draft,waiting,confirmed,assigned"
groups="base.group_extended"/>
-
-
+
-
@@ -1756,9 +1815,9 @@
search
-
+
-
+
@@ -1770,12 +1829,13 @@
+
-
+
@@ -1793,7 +1853,7 @@
tree,form['|','&',('picking_id','=',False),('location_id.usage', 'in', ['customer','supplier']),'&',('picking_id','!=',False),('picking_id.type','=','in')]
-
+ {"search_default_receive":1}
@@ -1855,7 +1915,7 @@
@@ -1870,7 +1930,7 @@
context="{'location': active_id, 'search_default_future': 1,'search_default_product_id': [active_id]}"
domain="[('state','in',('waiting','confirmed','assigned'))]"
id="act_product_stock_move_futur_open"
- name="Future Inventory Moves"
+ name="Future Stock Moves"
res_model="stock.move"
src_model="product.product"
groups="base.group_extended"/>
@@ -1891,80 +1951,6 @@
-
-
-
-
- Products To Received
- report.products.to.received.planned
- tree
-
-
-
-
-
-
-
-
-
-
- Products To Received
- report.products.to.received.planned
- graph
-
-
-
-
-
-
-
-
-
-
- Number of Products to receive vs planned
- report.products.to.received.planned
- ir.actions.act_window
- form
- graph,tree
-
-
-
-
-
- Delivered Products
- report.delivery.products.planned
- tree
-
-
-
-
-
-
-
-
-
-
- Delivered Products
- report.delivery.products.planned
- graph
-
-
-
-
-
-
-
-
-
-
- Delivered Products Vs Planned
- report.delivery.products.planned
- ir.actions.act_window
- form
- graph,tree
-
-
-
Customers Packingsstock.picking
@@ -2001,7 +1987,7 @@
tree,form['|','&',('picking_id','=',False),('location_dest_id.usage', 'in', ['customer','supplier']),'&',('picking_id','!=',False),('picking_id.type','=','out')]
-
+ {"search_default_receive":1}
@@ -2064,7 +2050,7 @@
diff --git a/addons/stock/stock_workflow.xml b/addons/stock/stock_workflow.xml
index 2a682833a1c..7a1f786ebf8 100644
--- a/addons/stock/stock_workflow.xml
+++ b/addons/stock/stock_workflow.xml
@@ -96,11 +96,13 @@
+ allow_cancel()button_cancel
+ allow_cancel()button_cancel
diff --git a/addons/stock/test/stock_test.yml b/addons/stock/test/stock_test.yml
index 841f2750ca3..8b54e555a7b 100644
--- a/addons/stock/test/stock_test.yml
+++ b/addons/stock/test/stock_test.yml
@@ -53,7 +53,7 @@
I create Receivable Account .
-
!record {model: account.account, id: account_account_receivable0}:
- code: '40000'
+ code: '40000-stock-test'
company_id: base.main_company
currency_mode: current
name: Receivable
@@ -65,7 +65,7 @@
I create Payable Account.
-
!record {model: account.account, id: account_account_payable0}:
- code: '440000'
+ code: '440000-stock-test'
company_id: base.main_company
currency_mode: current
name: Payable
@@ -111,7 +111,7 @@
I create Product Sale Account.
-
!record {model: account.account, id: account_account_productsale0}:
- code: '001'
+ code: '001-stock-test'
company_id: base.main_company
currency_mode: current
name: Product Sale
@@ -123,7 +123,7 @@
I create Product Purchase Account.
-
!record {model: account.account, id: account_account_productpurchase0}:
- code: '0002'
+ code: '0002-stock-test'
company_id: base.main_company
currency_mode: current
name: Product Purchase
@@ -267,7 +267,7 @@
-
!record {model: stock.move.split, id: stock_move_split_0}:
line_ids:
- - name: '00001'
+ - name: '00001-stock-test'
quantity: 5
product_id: stock.product_product_hpcdwriters0
@@ -294,7 +294,7 @@
invoice_state: none
move_lines:
- company_id: base.main_company
- date_planned: '2010-05-11 15:18:57'
+ date: '2010-05-11 15:18:57'
location_dest_id: stock.stock_location_customers
location_id: stock.stock_location_stock
name: HP CD writers
diff --git a/addons/stock/wizard/__init__.py b/addons/stock/wizard/__init__.py
index 7a13dc26fd5..f6021e0fbe2 100644
--- a/addons/stock/wizard/__init__.py
+++ b/addons/stock/wizard/__init__.py
@@ -27,7 +27,6 @@ import stock_partial_move
import stock_replacement
import stock_ups
import stock_inventory_merge
-import stock_inventory_set_stock_zero
import stock_fill_inventory
import stock_inventory_line_split
import stock_invoice_onshipping
diff --git a/addons/stock/wizard/stock_change_standard_price.py b/addons/stock/wizard/stock_change_standard_price.py
index 47fb5387f26..ecbaef57a36 100644
--- a/addons/stock/wizard/stock_change_standard_price.py
+++ b/addons/stock/wizard/stock_change_standard_price.py
@@ -21,12 +21,13 @@
from osv import fields, osv
from tools.translate import _
+import decimal_precision as dp
class change_standard_price(osv.osv_memory):
_name = "stock.change.standard.price"
_description = "Change Standard Price"
_columns = {
- 'new_price': fields.float('Price', required=True,
+ 'new_price': fields.float('Price', required=True, digits_compute=dp.get_precision('Account'),
help="If cost price is increased, stock variation account will be debited "
"and stock output account will be credited with the value = (difference of amount * quantity available).\n"
"If cost price is decreased, stock variation account will be creadited and stock input account will be debited."),
diff --git a/addons/stock/wizard/stock_fill_inventory.py b/addons/stock/wizard/stock_fill_inventory.py
index e133a9fc879..599377fec1e 100644
--- a/addons/stock/wizard/stock_fill_inventory.py
+++ b/addons/stock/wizard/stock_fill_inventory.py
@@ -24,14 +24,32 @@ from tools.translate import _
class stock_fill_inventory(osv.osv_memory):
_name = "stock.fill.inventory"
- _description = "Fill Inventory"
+ _description = "Import Inventory"
_columns = {
'location_id': fields.many2one('stock.location', 'Location', required=True),
- 'recursive': fields.boolean("Include all children for the location"),
+ 'recursive': fields.boolean("Include children",help="If checked, products contained in child locations of selected location will be included as well."),
+ 'set_stock_zero': fields.boolean("Set to zero",help="If checked, all product quantities will be set to zero to help ensure a real physical inventory is done"),
}
+ def view_init(self, cr, uid, fields_list, context=None):
+ """
+ Creates view dynamically and adding fields at runtime.
+ @param self: The object pointer.
+ @param cr: A database cursor
+ @param uid: ID of the user currently logged in
+ @param context: A standard dictionary
+ @return: New arch of view with new columns.
+ """
+ if context==None:
+ context={}
+ res = super(stock_fill_inventory, self).view_init(cr, uid, fields_list, context=context)
+ if context.get('active_id', False):
+ stock = self.pool.get('stock.inventory').browse(cr, uid, context.get('active_id', False))
+ if stock.state=='done':
+ raise osv.except_osv('Error!','Stock Inventory is done')
+ True
- def fill_inventory(self, cr, uid, ids, context):
- """ To fill stock inventory according to products available in the selected locations.
+ def fill_inventory(self, cr, uid, ids, context=None):
+ """ To Import stock inventory according to products available in the selected locations.
@param self: The object pointer.
@param cr: A database cursor
@param uid: ID of the user currently logged in
@@ -39,36 +57,43 @@ class stock_fill_inventory(osv.osv_memory):
@param context: A standard dictionary
@return:
"""
+ if context==None:
+ context={}
inventory_line_obj = self.pool.get('stock.inventory.line')
location_obj = self.pool.get('stock.location')
product_obj = self.pool.get('product.product')
stock_location_obj = self.pool.get('stock.location')
- for fill_inventory in self.browse(cr, uid, ids):
- res = {}
- res_location = {}
- if fill_inventory.recursive :
- location_ids = location_obj.search(cr, uid, [('location_id',
- 'child_of', fill_inventory.location_id.id)])
- for location in location_ids :
- res = location_obj._product_get(cr, uid, location)
- res_location[location] = res
- else:
- context.update({'compute_child': False})
- res = location_obj._product_get(cr, uid,
- fill_inventory.location_id.id, context=context)
- res_location[fill_inventory.location_id.id] = res
-
+ if ids and len(ids):
+ ids = ids[0]
+ else:
+ return {}
+ fill_inventory = self.browse(cr, uid, ids)
+ res = {}
+ res_location = {}
+ if fill_inventory.recursive :
+ location_ids = location_obj.search(cr, uid, [('location_id',
+ 'child_of', fill_inventory.location_id.id)])
+ for location in location_ids :
+ res = location_obj._product_get(cr, uid, location)
+ res_location[location] = res
+ else:
+ context.update({'compute_child': False})
+ res = location_obj._product_get(cr, uid,
+ fill_inventory.location_id.id, context=context)
+ res_location[fill_inventory.location_id.id] = res
+
product_ids = []
for location in res_location.keys():
res = res_location[location]
for product_id in res.keys():
prod = product_obj.browse(cr, uid, [product_id])[0]
uom = prod.uom_id.id
- context.update({'uom': uom})
+ context.update(uom=uom, compute_child=False)
amount = stock_location_obj._product_get(cr, uid,
location, [product_id], context=context)[product_id]
-
if(amount):
+ if fill_inventory.set_stock_zero:
+ amount = 0
line_ids=inventory_line_obj.search(cr, uid,
[('inventory_id', '=', context['active_ids']),
('location_id', '=', location),
diff --git a/addons/stock/wizard/stock_fill_inventory_view.xml b/addons/stock/wizard/stock_fill_inventory_view.xml
index ef95a3bf447..3db42eb7be5 100644
--- a/addons/stock/wizard/stock_fill_inventory_view.xml
+++ b/addons/stock/wizard/stock_fill_inventory_view.xml
@@ -2,28 +2,30 @@
- Fill Inventory
+ Import Inventorystock.fill.inventoryform
-
-
+
+
+
-
+
-
diff --git a/addons/stock/wizard/stock_inventory_merge.py b/addons/stock/wizard/stock_inventory_merge.py
index 46c589f18e9..cbb9f380cb1 100644
--- a/addons/stock/wizard/stock_inventory_merge.py
+++ b/addons/stock/wizard/stock_inventory_merge.py
@@ -25,7 +25,25 @@ from tools.translate import _
class stock_inventory_merge(osv.osv_memory):
_name = "stock.inventory.merge"
_description = "Merge Inventory"
-
+
+ def fields_view_get(self, cr, uid, view_id=None, view_type='form',
+ context=None, toolbar=False, submenu=False):
+ """
+ Changes the view dynamically
+ @param self: The object pointer.
+ @param cr: A database cursor
+ @param uid: ID of the user currently logged in
+ @param context: A standard dictionary
+ @return: New arch of view.
+ """
+ if not context:
+ context={}
+ res = super(stock_inventory_merge, self).fields_view_get(cr, uid, view_id=view_id, view_type=view_type, context=context, toolbar=toolbar,submenu=False)
+ if context.get('active_model','') == 'stock.inventory' and len(context['active_ids']) < 2:
+ raise osv.except_osv(_('Warning'),
+ _('Please select multiple physical inventories to merge in the list view.'))
+ return res
+
def do_merge(self, cr, uid, ids, context):
""" To merge selected Inventories.
@param self: The object pointer.
@@ -37,13 +55,7 @@ class stock_inventory_merge(osv.osv_memory):
"""
invent_obj = self.pool.get('stock.inventory')
invent_line_obj = self.pool.get('stock.inventory.line')
-
invent_lines = {}
-
- if len(context['active_ids']) < 2:
- raise osv.except_osv(_('Warning'),
- _('Please select at least two inventories.'))
-
for inventory in invent_obj.browse(cr, uid, context['active_ids'], context=context):
if inventory.state == "done":
raise osv.except_osv(_('Warning'),
diff --git a/addons/stock/wizard/stock_inventory_merge_view.xml b/addons/stock/wizard/stock_inventory_merge_view.xml
index 5ae2987b59a..62cac877179 100644
--- a/addons/stock/wizard/stock_inventory_merge_view.xml
+++ b/addons/stock/wizard/stock_inventory_merge_view.xml
@@ -12,7 +12,7 @@
-
+
diff --git a/addons/stock/wizard/stock_inventory_set_stock_zero.py b/addons/stock/wizard/stock_inventory_set_stock_zero.py
deleted file mode 100644
index 4cbba93bdb6..00000000000
--- a/addons/stock/wizard/stock_inventory_set_stock_zero.py
+++ /dev/null
@@ -1,74 +0,0 @@
-# -*- coding: utf-8 -*-
-##############################################################################
-#
-# OpenERP, Open Source Management Solution
-# Copyright (C) 2004-2010 Tiny SPRL ().
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero 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 Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see .
-#
-##############################################################################
-
-from osv import fields, osv
-from tools.translate import _
-
-class inventory_set_stock_zero(osv.osv_memory):
- _name = "stock.inventory.set.stock.zero"
- _description = "Set Stock to 0"
-
- _columns = {
- 'location_id': fields.many2one('stock.location', 'Location', required=True),
- }
-
- def do_merge(self, cr, uid, ids, context):
- """ To set stock to Zero
- @param self: The object pointer.
- @param cr: A database cursor
- @param uid: ID of the user currently logged in
- @param ids: the ID or list of IDs if we want more than one
- @param context: A standard dictionary
- @return:
- """
- invent_obj = self.pool.get('stock.inventory')
- invent_line_obj = self.pool.get('stock.inventory.line')
- prod_obj = self.pool.get('product.product')
-
- if len(context['active_ids']) <> 1:
- raise osv.except_osv(_('Warning'),
- _('Please select one and only one inventory !'))
- for id in ids:
- datas = self.read(cr, uid, id)
- loc = str(datas['location_id'])
-
- cr.execute('select distinct location_id,product_id \
- from stock_inventory_line \
- where inventory_id=%s', (context['active_id'],))
- inv = cr.fetchall()
- cr.execute('select distinct product_id from stock_move where \
- location_dest_id=%s or location_id=%s', (loc, loc,))
- stock = cr.fetchall()
- for s in stock:
- if (loc, s[0]) not in inv:
- p = prod_obj.browse(cr, uid, s[0])
- invent_line_obj.create(cr, uid, {
- 'inventory_id': context['active_id'],
- 'location_id': loc,
- 'product_id': s[0],
- 'product_uom': p.uom_id.id,
- 'product_qty': 0.0,
- })
- return {}
-
-inventory_set_stock_zero()
-
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/stock/wizard/stock_inventory_set_stock_zero_view.xml b/addons/stock/wizard/stock_inventory_set_stock_zero_view.xml
deleted file mode 100644
index 0ee84d2045d..00000000000
--- a/addons/stock/wizard/stock_inventory_set_stock_zero_view.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-
-
-
-
-
- Set Stock to Zero
- stock.inventory.set.stock.zero
- form
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/addons/stock/wizard/stock_invoice_onshipping.py b/addons/stock/wizard/stock_invoice_onshipping.py
index f931e575b2f..7e0693b9045 100644
--- a/addons/stock/wizard/stock_invoice_onshipping.py
+++ b/addons/stock/wizard/stock_invoice_onshipping.py
@@ -48,22 +48,22 @@ class stock_invoice_onshipping(osv.osv_memory):
if context is None:
context = {}
picking_obj = self.pool.get('stock.picking')
- usage = 'customer'
+ src_usage = dest_usage = None
pick = picking_obj.browse(cr, uid, context['active_id'], context=context)
if pick.invoice_state == 'invoiced':
raise osv.except_osv(_('UserError'), _('Invoice is already created.'))
if pick.invoice_state == 'none':
raise osv.except_osv(_('UserError'), _('This picking does not require any invoicing.'))
if pick.move_lines:
- usage = pick.move_lines[0].location_id.usage
-
- if pick.type == 'out' and usage == 'supplier':
+ src_usage = pick.move_lines[0].location_id.usage
+ dest_usage = pick.move_lines[0].location_dest_id.usage
+ if pick.type == 'out' and dest_usage == 'supplier':
type = 'in_refund'
- elif pick.type == 'out' and usage == 'customer':
+ elif pick.type == 'out' and dest_usage == 'customer':
type = 'out_invoice'
- elif pick.type == 'in' and usage == 'supplier':
+ elif pick.type == 'in' and src_usage == 'supplier':
type = 'in_invoice'
- elif pick.type == 'in' and usage == 'customer':
+ elif pick.type == 'in' and src_usage == 'customer':
type = 'out_refund'
else:
type = 'out_invoice'
diff --git a/addons/stock/wizard/stock_invoice_onshipping_view.xml b/addons/stock/wizard/stock_invoice_onshipping_view.xml
index e1e742d7aee..eddffb594aa 100644
--- a/addons/stock/wizard/stock_invoice_onshipping_view.xml
+++ b/addons/stock/wizard/stock_invoice_onshipping_view.xml
@@ -16,7 +16,7 @@
-
+
diff --git a/addons/stock/wizard/stock_location_product_view.xml b/addons/stock/wizard/stock_location_product_view.xml
index 518bc6eab35..2459daac189 100644
--- a/addons/stock/wizard/stock_location_product_view.xml
+++ b/addons/stock/wizard/stock_location_product_view.xml
@@ -16,7 +16,7 @@
-
+
diff --git a/addons/stock/wizard/stock_move_view.xml b/addons/stock/wizard/stock_move_view.xml
index 46d3893900b..06f6822e3c7 100644
--- a/addons/stock/wizard/stock_move_view.xml
+++ b/addons/stock/wizard/stock_move_view.xml
@@ -13,7 +13,7 @@
+ string="_Cancel" />
diff --git a/addons/stock/wizard/stock_partial_move.py b/addons/stock/wizard/stock_partial_move.py
index 1016d6c3977..4825fa9c47e 100644
--- a/addons/stock/wizard/stock_partial_move.py
+++ b/addons/stock/wizard/stock_partial_move.py
@@ -47,7 +47,9 @@ class stock_partial_move(osv.osv_memory):
self._columns['move%s_product_qty'%(m.id)] = fields.float("Quantity")
if 'move%s_product_uom'%(m.id) not in self._columns:
self._columns['move%s_product_uom'%(m.id)] = fields.many2one('product.uom',string="Product UOM")
-
+ if 'move%s_prodlot_id'%(m.id) not in self._columns:
+ self._columns['move%s_prodlot_id'%(m.id)] = fields.many2one('stock.production.lot', string="Lot")
+
if (m.picking_id.type == 'in') and (m.product_id.cost_method == 'average'):
if 'move%s_product_price'%(m.id) not in self._columns:
self._columns['move%s_product_price'%(m.id)] = fields.float("Price")
@@ -89,7 +91,12 @@ class stock_partial_move(osv.osv_memory):
'relation': 'product.uom',
'required' : True,
'readonly' : True,
- }
+ },
+ 'move%s_prodlot_id'%(m.id): {
+ 'string': _('Production Lot'),
+ 'type': 'many2one',
+ 'relation': 'stock.production.lot',
+ }
})
_moves_arch_lst += """
@@ -97,7 +104,8 @@ class stock_partial_move(osv.osv_memory):
- """%(m.id, m.id, m.id)
+
+ """%(m.id, m.id, m.id,m.id,m.id)
if (m.picking_id.type == 'in') and (m.product_id.cost_method == 'average'):
_moves_fields.update({
'move%s_product_price'%(m.id) : {
@@ -162,29 +170,11 @@ class stock_partial_move(osv.osv_memory):
res['move%s_product_qty'%(m.id)] = m.product_qty
if 'move%s_product_uom'%(m.id) in fields:
res['move%s_product_uom'%(m.id)] = m.product_uom.id
- if (m.picking_id.type == 'out') and (m.product_id.cost_method == 'average') and 'sale_id' in m.picking_id._columns.keys():
- price = 0
- if hasattr(m, 'sale_line_id') and m.sale_line_id:
- price = m.sale_line_id.price_unit
-
+ if 'move%s_prodlot_id'%(m.id) in fields:
+ res['move%s_prodlot_id'%(m.id)] = m.prodlot_id.id
+ if m.product_id.cost_method == 'average' :
+ price = m.product_id.standard_price
currency = False
- if hasattr(m.picking_id, 'sale_id') and m.picking_id.sale_id:
- currency = m.picking_id.sale_id.pricelist_id.currency_id.id
-
- if 'move%s_product_price'%(m.id) in fields:
- res['move%s_product_price'%(m.id)] = price
- if 'move%s_product_currency'%(m.id) in fields:
- res['move%s_product_currency'%(m.id)] = currency
-
- if (m.picking_id.type == 'in') and (m.product_id.cost_method == 'average') and 'purchase_id' in m.picking_id._columns.keys():
- price = 0
- if hasattr(m, 'purchase_line_id') and m.purchase_line_id:
- price = m.purchase_line_id.price_unit
-
- currency = False
- if hasattr(m.picking_id, 'purchase_id') and m.picking_id.purchase_id:
- currency = m.picking_id.purchase_id.pricelist_id.currency_id.id
-
if 'move%s_product_price'%(m.id) in fields:
res['move%s_product_price'%(m.id)] = price
if 'move%s_product_currency'%(m.id) in fields:
@@ -213,7 +203,8 @@ class stock_partial_move(osv.osv_memory):
partial_datas['move%s'%(m.id)] = {
'product_id' : getattr(partial, 'move%s_product_id'%(m.id)).id,
'product_qty' : getattr(partial, 'move%s_product_qty'%(m.id)),
- 'product_uom' : getattr(partial, 'move%s_product_uom'%(m.id)).id
+ 'product_uom' : getattr(partial, 'move%s_product_uom'%(m.id)).id,
+ 'prodlot_id' : getattr(partial, 'move%s_prodlot_id'%(m.id)).id
}
if (m.picking_id.type == 'in') and (m.product_id.cost_method == 'average'):
diff --git a/addons/stock/wizard/stock_partial_picking.py b/addons/stock/wizard/stock_partial_picking.py
index 3b6ce21b6ee..d52198170a6 100644
--- a/addons/stock/wizard/stock_partial_picking.py
+++ b/addons/stock/wizard/stock_partial_picking.py
@@ -97,7 +97,6 @@ class stock_partial_picking(osv.osv_memory):
'string': _('Production Lot'),
'type': 'many2one',
'relation': 'stock.production.lot',
- 'readonly': True,
}
})
@@ -106,8 +105,8 @@ class stock_partial_picking(osv.osv_memory):
-
- """%(m.id, m.id, m.id, m.id)
+
+ """%(m.id, m.id, m.id, m.id,m.id)
if (m.product_id.cost_method == 'average'):
_moves_fields.update({
'move%s_product_price'%(m.id) : {
@@ -171,20 +170,9 @@ class stock_partial_picking(osv.osv_memory):
res['move%s_product_uom'%(m.id)] = m.product_uom.id
if 'move%s_prodlot_id'%(m.id) in fields:
res['move%s_prodlot_id'%(m.id)] = m.prodlot_id.id
-
if (m.product_id.cost_method == 'average'):
currency = False
- price = 0
- if (pick.type == 'in') and 'purchase_id' in pick._columns.keys():
- if hasattr(m, 'purchase_line_id') and m.purchase_line_id:
- price = m.purchase_line_id.price_unit
- if hasattr(pick, 'purchase_id') and pick.purchase_id:
- currency = pick.purchase_id.pricelist_id.currency_id.id
- if (pick.type == 'out') and 'sale_id' in pick._columns.keys():
- if hasattr(m, 'sale_line_id') and m.sale_line_id:
- price = m.sale_line_id.price_unit
- if hasattr(pick, 'sale_id') and pick.sale_id:
- currency = pick.sale_id.pricelist_id.currency_id.id
+ price = m.product_id.standard_price
if 'move%s_product_price'%(m.id) in fields:
res['move%s_product_price'%(m.id)] = price
if 'move%s_product_currency'%(m.id) in fields:
diff --git a/addons/stock/wizard/stock_picking_make_view.xml b/addons/stock/wizard/stock_picking_make_view.xml
index fa77042c5d8..4ecebed1031 100644
--- a/addons/stock/wizard/stock_picking_make_view.xml
+++ b/addons/stock/wizard/stock_picking_make_view.xml
@@ -12,7 +12,7 @@
width="600" height="300"/>
+ string="_Cancel" />
diff --git a/addons/stock/wizard/stock_replacement_view.xml b/addons/stock/wizard/stock_replacement_view.xml
index ce5f7efbc9f..cea5bfedbe8 100644
--- a/addons/stock/wizard/stock_replacement_view.xml
+++ b/addons/stock/wizard/stock_replacement_view.xml
@@ -9,7 +9,7 @@
-
+
diff --git a/addons/stock/wizard/stock_return_picking.py b/addons/stock/wizard/stock_return_picking.py
index 74aa96e8e63..4bddc9a7ee4 100644
--- a/addons/stock/wizard/stock_return_picking.py
+++ b/addons/stock/wizard/stock_return_picking.py
@@ -65,18 +65,27 @@ class stock_return_picking(osv.osv_memory):
@return: New arch of view with new columns.
"""
res = super(stock_return_picking, self).view_init(cr, uid, fields_list, context=context)
- record_id = context and context.get('active_id', False) or False
+ record_id = context and context.get('active_id', False)
if record_id:
pick_obj = self.pool.get('stock.picking')
- try:
- pick = pick_obj.browse(cr, uid, record_id)
- for m in [line for line in pick.move_lines]:
- if 'return%s'%(m.id) not in self._columns:
- self._columns['return%s'%(m.id)] = fields.float(string=m.name, required=True)
- if 'invoice_state' not in self._columns:
- self._columns['invoice_state'] = fields.selection([('2binvoiced', 'To be Invoiced'), ('none', 'None')], string='Invoice State', required=True)
- except Exception:
- return res
+ pick = pick_obj.browse(cr, uid, record_id)
+ if pick.state not in ['done','confirmed','assigned']:
+ raise osv.except_osv(_('Warning !'), _("You may only return pickings that are Confirmed, Available or Done!"))
+ return_history = {}
+ valid_lines = 0
+ for m in [line for line in pick.move_lines]:
+ if m.state == 'done':
+ return_history[m.id] = 0
+ for rec in m.move_history_ids2:
+ return_history[m.id] += (rec.product_qty * rec.product_uom.factor)
+ if m.product_qty * m.product_uom.factor > return_history[m.id]:
+ valid_lines += 1
+ if 'return%s'%(m.id) not in self._columns:
+ self._columns['return%s'%(m.id)] = fields.float(string=m.name, required=True)
+ if 'invoice_state' not in self._columns:
+ self._columns['invoice_state'] = fields.selection([('2binvoiced', 'To be Invoiced'), ('none', 'None')], string='Invoice State', required=True)
+ if not valid_lines:
+ raise osv.except_osv(_('Warning !'), _("There are no products to return (only lines in Done state and not fully returned yet can be returned)!"))
return res
def fields_view_get(self, cr, uid, view_id=None, view_type='form',
@@ -97,36 +106,29 @@ class stock_return_picking(osv.osv_memory):
if record_id:
pick_obj = self.pool.get('stock.picking')
pick = pick_obj.browse(cr, uid, record_id)
- if pick.state != 'done':
- raise osv.except_osv(_('Warning !'), _("The Picking is not completed yet!\nYou cannot return picking which is not in 'Done' state!"))
-
return_history = {}
- for m_line in pick.move_lines:
- return_history[m_line.id] = 0
- for rec in m_line.move_history_ids2:
- return_history[m_line.id] += rec.product_qty
res['fields'].clear()
arch_lst=['', '
' % _('Return lines'), '' % _('Provide the quantities of the returned products.')]
- for m in [line for line in pick.move_lines]:
+ for m in pick.move_lines:
+ return_history[m.id] = 0
+ for rec in m.move_history_ids2:
+ return_history[m.id] += rec.product_qty
quantity = m.product_qty
- if quantity > return_history[m.id] and (quantity - return_history[m.id])>0:
+ if m.state=='done' and quantity > return_history[m.id]:
arch_lst.append('\n' % (m.id,))
res['fields']['return%s' % m.id]={'string':m.name, 'type':'float', 'required':True}
res.setdefault('returns', []).append(m.id)
- if not res.get('returns',False):
- raise osv.except_osv(_('Warning!'),_('There is no product to return!'))
-
arch_lst.append('\n')
res['fields']['invoice_state']={'string':_('Invoice state'), 'type':'selection','required':True, 'selection':[('2binvoiced', _('To Be Invoiced')), ('none', _('None'))]}
arch_lst.append('')
arch_lst.append('')
- arch_lst.append('')
+ arch_lst.append('')
arch_lst.append('')
arch_lst.append('
')
res['arch'] = '\n'.join(arch_lst)
return res
- def _create_returns(self, cr, uid, ids, context):
+ def create_returns(self, cr, uid, ids, context):
"""
Creates return picking.
@param self: The object pointer.
@@ -149,6 +151,7 @@ class stock_return_picking(osv.osv_memory):
move_ids = [m.id for m in [line for line in pick.move_lines]]
set_invoice_state_to_none = True
+ returned_lines = 0
for move in move_obj.browse(cr, uid, move_ids):
if not new_picking:
if pick.type=='out':
@@ -161,57 +164,45 @@ class stock_return_picking(osv.osv_memory):
'move_lines':[], 'state':'draft', 'type':new_type,
'date':date_cur, 'invoice_state':data['invoice_state'],})
new_location=move.location_dest_id.id
+ if move.state=='done':
+ new_qty = data['return%s' % move.id]
+ returned_qty = move.product_qty
- new_qty = data['return%s' % move.id]
- returned_qty = move.product_qty
-
- for rec in move.move_history_ids2:
- returned_qty -= rec.product_qty
-
- if returned_qty != new_qty:
- set_invoice_state_to_none = False
-
- new_move=move_obj.copy(cr, uid, move.id, {
- 'product_qty': new_qty,
- 'product_uos_qty': uom_obj._compute_qty(cr, uid, move.product_uom.id,
- new_qty, move.product_uos.id),
- 'picking_id':new_picking, 'state':'draft',
- 'location_id':new_location, 'location_dest_id':move.location_id.id,
- 'date':date_cur, 'date_planned':date_cur,})
- move_obj.write(cr, uid, [move.id], {'move_history_ids2':[(4,new_move)]})
-
+ for rec in move.move_history_ids2:
+ returned_qty -= rec.product_qty
+
+ if returned_qty != new_qty:
+ set_invoice_state_to_none = False
+
+ if new_qty:
+ returned_lines += 1
+ new_move=move_obj.copy(cr, uid, move.id, {
+ 'product_qty': new_qty,
+ 'product_uos_qty': uom_obj._compute_qty(cr, uid, move.product_uom.id,
+ new_qty, move.product_uos.id),
+ 'picking_id':new_picking, 'state':'draft',
+ 'location_id':new_location, 'location_dest_id':move.location_id.id,
+ 'date':date_cur,})
+ move_obj.write(cr, uid, [move.id], {'move_history_ids2':[(4,new_move)]})
+
+ if not returned_lines:
+ raise osv.except_osv(_('Warning !'), _("Please specify at least one non-zero quantity!"))
+
if set_invoice_state_to_none:
pick_obj.write(cr, uid, [pick.id], {'invoice_state':'none'})
-
- if new_picking:
- if new_picking:
- wf_service.trg_validate(uid, 'stock.picking', new_picking, 'button_confirm', cr)
- pick_obj.force_assign(cr, uid, [new_picking], context)
- return new_picking
-
- def action_open_window(self, cr, uid, ids, context):
- """
- Opens return picking list.
- @param self: The object pointer.
- @param cr: A database cursor
- @param uid: ID of the user currently logged in
- @param ids: List of ids selected
- @param context: A standard dictionary
- @return: A dictionary which of fields with values.
- """
- res = self._create_returns(cr, uid, ids, context)
- if not res:
- return {}
+ wf_service.trg_validate(uid, 'stock.picking', new_picking, 'button_confirm', cr)
+ pick_obj.force_assign(cr, uid, [new_picking], context)
return {
- 'domain': "[('id', 'in', ["+str(res)+"])]",
+ 'domain': "[('id', 'in', ["+str(new_picking)+"])]",
'name': 'Picking List',
'view_type':'form',
'view_mode':'tree,form',
'res_model':'stock.picking',
'view_id':False,
'type':'ir.actions.act_window',
+ 'context':context,
}
-
+
stock_return_picking()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/stock/wizard/stock_splitinto.py b/addons/stock/wizard/stock_splitinto.py
index 20b1940e1c7..a54e6a485c2 100644
--- a/addons/stock/wizard/stock_splitinto.py
+++ b/addons/stock/wizard/stock_splitinto.py
@@ -21,13 +21,13 @@
from osv import fields, osv
from tools.translate import _
-
+import decimal_precision as dp
class stock_split_into(osv.osv_memory):
_name = "stock.split.into"
_description = "Split into"
_columns = {
- 'quantity': fields.float('Quantity', digits=(16,2)),
+ 'quantity': fields.float('Quantity',digits_compute=dp.get_precision('Product UOM')),
}
_defaults = {
'quantity': lambda *x: 0,
diff --git a/addons/stock/wizard/stock_splitinto_view.xml b/addons/stock/wizard/stock_splitinto_view.xml
index a325519ca75..7ed7266a163 100644
--- a/addons/stock/wizard/stock_splitinto_view.xml
+++ b/addons/stock/wizard/stock_splitinto_view.xml
@@ -10,7 +10,7 @@
+ string="_Cancel" />
diff --git a/addons/stock/wizard/stock_ups_view.xml b/addons/stock/wizard/stock_ups_view.xml
index 89b5fe131e8..cbce721e1b9 100644
--- a/addons/stock/wizard/stock_ups_view.xml
+++ b/addons/stock/wizard/stock_ups_view.xml
@@ -11,7 +11,7 @@
-
+
diff --git a/addons/stock_location/__openerp__.py b/addons/stock_location/__openerp__.py
index beb24997fb8..e5d6f767975 100644
--- a/addons/stock_location/__openerp__.py
+++ b/addons/stock_location/__openerp__.py
@@ -21,29 +21,70 @@
{
- 'name': 'Stock Location Paths',
+ 'name': 'Warehouse Locations Paths',
'version': '1.0',
'category': 'Generic Modules/Inventory Control',
'description': """
-Manages product's path in locations.
+This module supplements the Warehouse application by adding support for per-product
+location paths, effectively implementing Push and Pull inventory flows.
-This module may be useful for different purposes:
-* Manages the product in his whole manufacturing chain
-* Manages different default locations by products
-* Define paths within the warehouse to route products based on operations:
+Typically this could be used to:
+* Manage product manufacturing chains
+* Manage default locations per product
+* Define routes within your warehouse according to business needs, such as:
- Quality Control
- After Sales Services
- - Supplier Return
-* Manage products to be rent.
+ - Supplier Returns
+* Help rental management, by generating automated return moves for rented products
+
+Once this module is installed, an additional tab appear on the product form, where you can add
+Push and Pull flow specifications. The demo data of CPU1 product for that push/pull :
+
+Push flows
+----------
+Push flows are useful when the arrival of certain products in a given location should always
+be followed by a corresponding move to another location, optionally after a certain delay.
+The original Warehouse application already supports such Push flow specifications on the
+Locations themselves, but these cannot be refined per-product.
+
+A push flow specification indicates which location is chained with which location, and with
+what parameters. As soon as a given quantity of products is moved in the source location,
+a chained move is automatically foreseen according to the parameters set on the flow specification
+(destination location, delay, type of move, journal, etc.) The new move can be automatically
+processed, or require a manual confirmation, depending on the parameters.
+
+Pull flows
+----------
+Pull flows are a bit different from Pull flows, in the sense that they are not related to
+the processing of product moves, but rather to the processing of procurement orders.
+What is being pulled is a need, not directly products.
+A classical example of Push flow is when you have an Outlet company, with a parent Company
+that is responsible for the supplies of the Outlet.
+
+ [ Customer ] <- A - [ Outlet ] <- B - [ Holding ] <~ C ~ [ Supplier ]
+
+When a new procurement order (A, coming from the confirmation of a Sale Order for example) arrives
+in the Outlet, it is converted into another procurement (B, via a Push flow of type 'move')
+requested from the Holding. When procurement order B is processed by the Holding company, and
+if the product is out of stock, it can be converted into a Purchase Order (C) from the Supplier
+(Push flow of type Purchase). The result is that the procurement order, the need, is pushed
+all the way between the Customer and Supplier.
+
+Technically, Pull flows allow to process procurement orders differently, not only depending on
+the product being considered, but also depending on which location holds the "need" for that
+product (i.e. the destination location of that procurement order).
+
""",
'author': 'OpenERP SA',
'depends': ['procurement','stock'],
'init_xml': [],
'update_xml': ['stock_location_view.xml', 'security/ir.model.access.csv', 'mrp_pull_workflow.xml'],
- 'demo_xml': [],
+ 'demo_xml': ['stock_location_demo.xml',],
+
'installable': True,
- 'test':[
- 'test/stock_location.yml'
+ 'test':[''
+ 'test/stock_location_pull_flow.yml',
+ 'test/stock_location_push_flow.yml',
],
'active': False,
'certificate': '0046505115101',
diff --git a/addons/stock_location/mrp_pull.py b/addons/stock_location/mrp_pull.py
index b4a68ca1b74..f4c9868a58f 100644
--- a/addons/stock_location/mrp_pull.py
+++ b/addons/stock_location/mrp_pull.py
@@ -18,13 +18,8 @@
#
##############################################################################
-from osv import fields,osv
-import tools
-import ir
-import pooler
+from osv import osv
import netsvc
-from mx import DateTime
-import time
from tools.translate import _
class procurement_order(osv.osv):
@@ -32,7 +27,6 @@ class procurement_order(osv.osv):
def check_buy(self, cr, uid, ids, context=None):
for procurement in self.browse(cr, uid, ids):
for line in procurement.product_id.flow_pull_ids:
-
if line.location_id==procurement.location_id:
return line.type_proc=='buy'
return super(procurement_order, self).check_buy(cr, uid, ids)
@@ -56,7 +50,7 @@ class procurement_order(osv.osv):
def action_move_create(self, cr, uid, ids,context=None):
proc_obj = self.pool.get('procurement.order')
move_obj = self.pool.get('stock.move')
- location_obj = self.pool.get('stock.location')
+ picking_obj=self.pool.get('stock.picking')
wf_service = netsvc.LocalService("workflow")
for proc in proc_obj.browse(cr, uid, ids, context=context):
line = None
@@ -65,7 +59,7 @@ class procurement_order(osv.osv):
break
assert line, 'Line can not be False if we are on this state of the workflow'
origin = (proc.origin or proc.name or '').split(':')[0] +':'+line.name
- picking_id = self.pool.get('stock.picking').create(cr, uid, {
+ picking_id =picking_obj.create(cr, uid, {
'origin': origin,
'company_id': line.company_id and line.company_id.id or False,
'type': line.picking_type,
@@ -75,12 +69,12 @@ class procurement_order(osv.osv):
'note': line.name, # TODO: note on procurement ?
'invoice_state': line.invoice_state,
})
- move_id = self.pool.get('stock.move').create(cr, uid, {
+ move_id =move_obj.create(cr, uid, {
'name': line.name,
'picking_id': picking_id,
'company_id': line.company_id and line.company_id.id or False,
'product_id': proc.product_id.id,
- 'date_planned': proc.date_planned,
+ 'date': proc.date_planned,
'product_qty': proc.product_qty,
'product_uom': proc.product_uom.id,
'product_uos_qty': (proc.product_uos and proc.product_uos_qty)\
@@ -97,10 +91,10 @@ class procurement_order(osv.osv):
'note': line.name, # TODO: same as above
})
if proc.move_id and proc.move_id.state in ('confirmed'):
- self.pool.get('stock.move').write(cr,uid, [proc.move_id.id], {
+ move_obj.write(cr,uid, [proc.move_id.id], {
'state':'waiting'
}, context=context)
- proc_id = self.pool.get('procurement.order').create(cr, uid, {
+ proc_id = proc_obj.create(cr, uid, {
'name': line.name,
'origin': origin,
'company_id': line.company_id and line.company_id.id or False,
@@ -120,7 +114,7 @@ class procurement_order(osv.osv):
wf_service.trg_validate(uid, 'stock.picking', picking_id, 'button_confirm', cr)
wf_service.trg_validate(uid, 'procurement.order', proc_id, 'button_confirm', cr)
if proc.move_id:
- self.pool.get('stock.move').write(cr, uid, [proc.move_id.id],
+ move_obj.write(cr, uid, [proc.move_id.id],
{'location_id':proc.location_id.id})
self.write(cr, uid, [proc.id], {'state':'running','message':_('Moved from other location')})
diff --git a/addons/stock_location/stock_location_demo.xml b/addons/stock_location/stock_location_demo.xml
new file mode 100644
index 00000000000..a828eb76f2d
--- /dev/null
+++ b/addons/stock_location/stock_location_demo.xml
@@ -0,0 +1,89 @@
+
+
+
+
+ This product is configured with example of push/pull flows
+
+
+
+
+
+ internal
+ none
+ Input
+ manual
+
+
+
+
+
+ internal
+ none
+ Quality test
+ manual
+
+
+
+
+
+
+
+ auto
+ none
+
+
+ out
+
+
+
+
+ auto
+ none
+
+
+ out
+
+
+
+
+ auto
+ none
+
+
+ out
+
+
+
+
+
+
+
+ none
+
+ move
+
+ make_to_stock
+ internal
+ L001
+
+
+
+
+ none
+
+ buy
+
+ make_to_stock
+ out
+ L002
+
+
+ 20.0
+ 10.0
+
+
+
+
+
+
+
diff --git a/addons/stock_location/test/stock_location.yml b/addons/stock_location/test/stock_location.yml
deleted file mode 100644
index c5360ce67c5..00000000000
--- a/addons/stock_location/test/stock_location.yml
+++ /dev/null
@@ -1,270 +0,0 @@
-
--
- In order to test the stock location module, I will create product, locations, SO, warehouse.
-
--
- Creating a account.account.type record
--
- !record {model: account.account.type, id: account_account_type_asset0}:
- close_method: balance
- code: asset
- name: Asset
- sign: 1
-
-
--
- Creating a account.account.type record
--
- !record {model: account.account.type, id: account_account_type_income0}:
- close_method: unreconciled
- code: income
- name: Income
- sign: 1
-
-
--
- Creating a account.account.type record
--
- !record {model: account.account.type, id: account_account_type_expense0}:
- close_method: unreconciled
- code: expense
- name: Expense
- sign: 1
--
- Creating a account.account.type record
--
- !record {model: account.account.type, id: account_account_type_receivable0}:
- close_method: balance
- code: receivable
- name: Receivable
- sign: 1
--
-
- Creating a account.account record
--
- !record {model: account.account, id: account_account_receivable0}:
- code: '40000'
- company_id: base.main_company
- currency_mode: current
- name: Receivable
- parent_left: 1
- parent_right: 2
- type: receivable
- user_type: account_account_type_receivable0
--
- Creating a account.account record
--
- !record {model: account.account, id: account_account_payable0}:
- code: '440000'
- company_id: base.main_company
- currency_mode: current
- name: Payable
- parent_left: 3
- parent_right: 4
- type: payable
- user_type: account_account_type_expense0
--
- Creating a account.journal record
--
- !record {model: account.journal, id: account_journal_purchasejournal0}:
- code: pur
- company_id: base.main_company
- name: Purchase Journal
- sequence_id: account.sequence_purchase_journal
- type: purchase
- view_id: account.account_journal_bank_view
-
--
- Creating a account.journal record
--
- !record {model: account.journal, id: account_journal_salejouran0}:
- code: sal
- company_id: base.main_company
- name: Sale Jouran
- sequence_id: account.sequence_sale_journal
- type: sale
- view_id: account.account_journal_view
-
--
- Creating a account.account record
--
- !record {model: account.account, id: account_account_expenseaccount0}:
- code: Expe
- company_id: base.main_company
- currency_mode: current
- name: Expense Account
- parent_left: 5
- parent_right: 6
- type: consolidation
- user_type: account_account_type_asset0
--
- Creating a account.account record
--
- !record {model: account.account, id: account_account_productsale0}:
- code: '001'
- company_id: base.main_company
- currency_mode: current
- name: Product Sale
- type: other
- user_type: account_account_type_income0
-
--
- Creating a account.account record
--
- !record {model: account.account, id: account_account_productpurchase0}:
- code: '0002'
- company_id: base.main_company
- currency_mode: current
- name: Product Purchase
- type: other
- user_type: account_account_type_expense0
-
--
- Creating a res.partner record
--
- !record {model: res.partner, id: res_partner_shawtrust0}:
- address:
- - country_id: base.in
- - street: St James House, Vicar Lane, Sheffield
- lang: en_US
- name: 'Shaw Trust '
- property_account_payable: account_account_payable0
- property_account_receivable: account_account_receivable0
--
- Creating a res.partner record
--
- !record {model: res.partner, id: res_partner_diasorinltd0}:
- address:
- - country_id: base.in
- street: Ash House, Ash Road
- name: DiaSorin Ltd
- supplier: true
-
-
--
- Creating a res.partner record
--
- !record {model: res.partner, id: res_partner_microlinktechnologies0}:
- address:
- - street: Kailash Vaibhav, Parksite
- name: Micro Link Technologies
- property_account_payable: account_account_payable0
- property_account_receivable: account_account_receivable0
- supplier: true
-
--
- Creating a res.partner.address record
--
- !record {model: res.partner.address, id: res_partner_address_0}:
- country_id: base.in
- partner_id: res_partner_microlinktechnologies0
- street: Ash House, Ash Road
- title: base.res_partner_title_miss
-
-
--
- Creating a product.category record
--
- !record {model: product.category, id: product_category_computer0}:
- name: Computer
--
- Creating a stock.location record
--
- !record {model: stock.location, id: stock_location_suppiler0}:
- chained_auto_packing: auto
- chained_delay: 15
- chained_location_type: none
- company_id: base.main_company
- name: Suppiler1
- usage: supplier
--
-
- Creating a product.product record
--
- !record {model: product.product, id: product_product_hpcdwriters0}:
- categ_id: product_category_computer0
- cost_method: standard
- list_price: 1000.0
- mes_type: fixed
- name: HP CD writers
- procure_method: make_to_stock
- seller_ids:
- - delay: 1
- name: res_partner_shawtrust0
- min_qty: 5.0
- supply_method: buy
- type: product
- uom_id: product.product_uom_unit
- uom_po_id: product.product_uom_unit
- property_account_expense: account_account_productpurchase0
- property_account_income: account_account_productsale0
- path_ids:
- - auto: auto
- delay: 30
- location_dest_id: stock.location_inventory
- location_from_id: stock_location_suppiler0
- property_stock_inventory: stock.location_inventory
- property_stock_procurement: stock.location_procurement
- property_stock_production: stock.location_production
--
- Creating a stock.location record
--
- !record {model: stock.location, id: stock_location_suppiler0}:
- address_id: res_partner_address_0
- chained_auto_packing: manual
- chained_delay: 20
- chained_location_type: none
- company_id: base.main_company
- name: Suppiler 1
- usage: internal
--
- I create warehouse.
--
- !record {model: stock.warehouse, id: warehouse0}:
- company_id: base.main_company
- lot_input_id: stock.stock_location_stock
- lot_output_id: stock_location_suppiler0
- lot_stock_id: stock_location_suppiler0
- name: OpenERP S.A.
- partner_address_id: res_partner_address_0
--
- I create Sale order for HP CD writers.
--
- !record {model: sale.order, id: sale_order_01}:
- date_order: '2010-04-20'
- invoice_quantity: order
- name: Test/location
- order_line:
- - name: HP CD writers
- price_unit: 1000.0
- product_uom: product.product_uom_unit
- product_uom_qty: 5.0
- state: draft
- product_id: product_product_hpcdwriters0
- type: make_to_order
- order_policy: manual
- partner_id: base.res_partner_agrolait
- partner_invoice_id: base.res_partner_address_8
- partner_order_id: base.res_partner_address_8
- partner_shipping_id: base.res_partner_address_8
- picking_policy: direct
- pricelist_id: product.list0
- shop_id: sale.shop
--
- I confirm the sale order.
--
- !workflow {model: sale.order, action: order_confirm, ref: sale_order_01}
--
- I check the delay mentioned in pushed flow in move.
--
- !python {model: stock.move}: |
- product_id=self.pool.get('product.product').search(cr,uid,[('name','=','HP CD writers')])
- location_id=self.pool.get('stock.location').search(cr,uid,[('name','=','Inventory loss')])
- move_ids =self.search(cr, uid, [('product_id','in',product_id),('location_dest_id','=',location_id)])
- if move_ids:
- move_obj=self.browse(cr,uid,move_ids)[0]
- import time
- from_dt = time.mktime(time.strptime(move_obj.date,'%Y-%m-%d %H:%M:%S'))
- to_dt = time.mktime(time.strptime(move_obj.date_planned,'%Y-%m-%d %H:%M:%S'))
- diff_day = (to_dt-from_dt)/(3600*24)
- assert (round(diff_day)+1 ==30)
diff --git a/addons/stock_location/test/stock_location_pull_flow.yml b/addons/stock_location/test/stock_location_pull_flow.yml
new file mode 100644
index 00000000000..a6f7a8e913a
--- /dev/null
+++ b/addons/stock_location/test/stock_location_pull_flow.yml
@@ -0,0 +1,226 @@
+-
+ In order to test the product pulled flow , I create ,partner,product,procurement
+-
+ I create a account type Asset.
+-
+ !record {model: account.account.type, id: account_account_type_asset0}:
+ close_method: balance
+ code: asset
+ name: Asset
+ sign: 1
+-
+ I create a account type income.
+-
+ !record {model: account.account.type, id: account_account_type_income0}:
+ close_method: unreconciled
+ code: income
+ name: Income
+ sign: 1
+-
+ I create a account type Expense.
+-
+ !record {model: account.account.type, id: account_account_type_expense0}:
+ close_method: unreconciled
+ code: expense
+ name: Expense
+ sign: 1
+-
+ I create a account type Receivable.
+-
+ !record {model: account.account.type, id: account_account_type_receivable0}:
+ close_method: balance
+ code: receivable
+ name: Receivable
+ sign: 1
+-
+
+ I create a account Receivable.
+-
+ !record {model: account.account, id: account_account_receivable0}:
+ code: '40000'
+ company_id: base.main_company
+ currency_mode: current
+ name: Receivable
+ parent_left: 1
+ parent_right: 2
+ type: receivable
+ user_type: account_account_type_receivable0
+-
+ I create a account Payable.
+-
+ !record {model: account.account, id: account_account_payable0}:
+ code: '440000'
+ company_id: base.main_company
+ currency_mode: current
+ name: Payable
+ parent_left: 3
+ parent_right: 4
+ type: payable
+ user_type: account_account_type_expense0
+-
+ I create a Purchase Journal.
+-
+ !record {model: account.journal, id: account_journal_purchasejournal0}:
+ code: pur
+ company_id: base.main_company
+ name: Purchase Journal
+ sequence_id: account.sequence_purchase_journal
+ type: purchase
+ view_id: account.account_journal_bank_view
+
+-
+ I create a Sale Journal.
+-
+ !record {model: account.journal, id: account_journal_salejouran0}:
+ code: sal
+ company_id: base.main_company
+ name: Sale Journal
+ sequence_id: account.sequence_sale_journal
+ type: sale
+ view_id: account.account_journal_view
+
+-
+ I create a Sale Journal.
+-
+ !record {model: account.account, id: account_account_expenseaccount0}:
+ code: Expe
+ company_id: base.main_company
+ currency_mode: current
+ name: Expense Account
+ parent_left: 5
+ parent_right: 6
+ type: consolidation
+ user_type: account_account_type_asset0
+-
+ I create Product Sale account.
+-
+ !record {model: account.account, id: account_account_productsale0}:
+ code: '001'
+ company_id: base.main_company
+ currency_mode: current
+ name: Product Sale
+ type: other
+ user_type: account_account_type_income0
+
+-
+ I create Product Product Purchase.
+-
+ !record {model: account.account, id: account_account_productpurchase0}:
+ code: '0002'
+ company_id: base.main_company
+ currency_mode: current
+ name: Product Purchase
+ type: other
+ user_type: account_account_type_expense0
+-
+ I create Supplier.
+-
+ !record {model: res.partner, id: res_partner_shawtrust0}:
+ address:
+ - country_id: base.in
+ - street: St James House, Vicar Lane, Sheffield
+ lang: en_US
+ name: 'Shaw Trust '
+ property_account_payable: account_account_payable0
+ property_account_receivable: account_account_receivable0
+-
+ I create product category.
+-
+ !record {model: product.category, id: product_category_computer0}:
+ name: Computer
+-
+ I create product and define the pulled flow condition for stock move.
+ I set shipping type Sending Goods. and set Procurement type move.
+-
+ !record {model: product.product, id: product_product_hpcdwriters0}:
+ categ_id: product_category_computer0
+ cost_method: standard
+ list_price: 1000.0
+ mes_type: fixed
+ name: HP CD writers
+ procure_method: make_to_stock
+ seller_ids:
+ - delay: 1
+ name: res_partner_shawtrust0
+ min_qty: 5.0
+ supply_method: buy
+ type: product
+ uom_id: product.product_uom_unit
+ uom_po_id: product.product_uom_unit
+ property_account_expense: account_account_productpurchase0
+ property_account_income: account_account_productsale0
+ flow_pull_ids:
+ - invoice_state: none
+ location_id: stock.stock_location_shop0
+ location_src_id: stock.stock_location_shop1
+ name: E001
+ partner_address_id: base.res_partner_address_2
+ picking_type: out
+ procure_method: make_to_stock
+ type_proc: move
+ - invoice_state: none
+ location_id: stock.stock_location_shop1
+ location_src_id: stock.stock_location_suppliers
+ name: E002
+ partner_address_id: base.res_partner_address_7
+ picking_type: out
+ procure_method: make_to_stock
+ type_proc: move
+ - invoice_state: none
+ location_id: stock.stock_location_suppliers
+ location_src_id: stock.stock_location_stock
+ name: E003
+ type_proc: buy
+ property_stock_inventory: stock.location_inventory
+ property_stock_procurement: stock.location_procurement
+ property_stock_production: stock.location_production
+ qty_available: 15
+-
+ I create procurement order.
+-
+ !record {model: procurement.order, id: procurement_order_test0}:
+ company_id: base.main_company
+ date_planned: '2010-10-07 18:24:24'
+ location_id: stock.stock_location_shop0
+ name: Testing pulled flow
+ priority: '1'
+ procure_method: make_to_order
+ product_id: product_product_hpcdwriters0
+ product_qty: 5.0
+ product_uom: product.product_uom_unit
+ product_uos: product.product_uom_unit
+ product_uos_qty: 0.0
+-
+ I confirm procurement order.
+-
+ !workflow {model: procurement.order, action: button_confirm, ref: procurement_order_test0}
+-
+ I launch the scheduler to compute procurement.
+-
+ !python {model: procurement.order.compute.all}: |
+ proc_obj = self.pool.get('procurement.order')
+ proc_obj._procure_confirm(cr,uid)
+-
+ I check the state of procurement order is cancel and stock move is cancel.
+-
+ !python {model: procurement.order }: |
+ from tools.translate import _
+ procurement_ids=self.search(cr, uid, [('id', '=', ref('procurement_order_test0'))])
+ if procurement_ids:
+ order=self.browse(cr,uid,procurement_ids)[0]
+ assert(order.state=='cancel')
+-
+ I check the new of procurement order create .
+-
+ !python {model: procurement.order }: |
+ from tools.translate import _
+ procurement_ids=self.search(cr, uid, [('name','=','E001')])
+ assert len(procurement_ids), "Procurement order hasn't Created."
+-
+ I check the Outgoing Picking is create for source location Shop 2 and destination shop1.
+-
+ !python {model: stock.picking }: |
+ from tools.translate import _
+ picking_id = self.search(cr, uid, [('origin','=','Testing pulled flow:E001'),('type','=','out')])
+ assert len(picking_id), "Picking hasn't Created."
+
\ No newline at end of file
diff --git a/addons/stock_location/test/stock_location_push_flow.yml b/addons/stock_location/test/stock_location_push_flow.yml
new file mode 100644
index 00000000000..989134c13bb
--- /dev/null
+++ b/addons/stock_location/test/stock_location_push_flow.yml
@@ -0,0 +1,134 @@
+-
+ In order to test the product pushed flow , I create account ,partner,product,shipment
+ Push flow specification indicates which location is chained with which location.
+-
+ I create product category.
+-
+ !record {model: product.category, id: product_category_computer0}:
+ name: Computer
+
+-
+ I create Supplier.
+-
+ !record {model: res.partner, id: res_partner_microlinktechnologies0}:
+ address:
+ - street: Kailash Vaibhav, Parksite
+ name: Micro Link Technologies
+ property_account_payable: account_account_payable0
+ property_account_receivable: account_account_receivable0
+ supplier: true
+
+-
+ I create Supplier address.
+-
+ !record {model: res.partner.address, id: res_partner_address_0}:
+ country_id: base.in
+ partner_id: res_partner_microlinktechnologies0
+ street: Ash House, Ash Road
+ title: base.res_partner_title_miss
+
+-
+ I create product and define the pushed flow .
+-
+ I set the chain location Supplier to stock Input
+ Stock Input to Quality test and Quality test -Stock .
+-
+ !record {model: product.product, id: product_product_hpcdwriters0}:
+ categ_id: product_category_computer0
+ cost_method: standard
+ list_price: 1000.0
+ mes_type: fixed
+ name: HP CD writers
+ procure_method: make_to_stock
+ seller_ids:
+ - delay: 1
+ name: res_partner_microlinktechnologies0
+ min_qty: 5.0
+ path_ids:
+ - auto: auto
+ invoice_state: none
+ location_dest_id: stock_location.stock_location_input0
+ location_from_id: stock.stock_location_suppliers
+ - auto: auto
+ invoice_state: none
+ location_dest_id: stock_location.stock_location_qualitytest0
+ location_from_id: stock_location.stock_location_input0
+ - auto: auto
+ invoice_state: none
+ location_dest_id: stock.stock_location_stock
+ location_from_id: stock_location.stock_location_qualitytest0
+ supply_method: buy
+ type: product
+ uom_id: product.product_uom_unit
+ uom_po_id: product.product_uom_unit
+ property_stock_inventory: stock.location_inventory
+ property_stock_procurement: stock.location_procurement
+ property_stock_production: stock.location_production
+-
+ In order to test pushed flow .I buy the product from Micro Link Technologies supplier. I create a Picking.
+-
+ !record {model: stock.picking , id: stock_picking_in0}:
+ address_id: res_partner_address_0
+ company_id: base.main_company
+ invoice_state: none
+ move_lines:
+ - date_expected: '2010-10-08 15:36:53'
+ location_dest_id: stock_location.stock_location_input0
+ location_id: stock.stock_location_suppliers
+ name: 'HP CD writers'
+ product_id: product_product_hpcdwriters0
+ product_qty: 6.0
+ product_uom: product.product_uom_unit
+ product_uos_qty: 6.0
+ name: Pushed Flow Test
+ type: in
+-
+ I confirm picking.
+-
+ !python {model: stock.picking }: |
+ self.draft_force_assign(cr, uid, [ref("stock_picking_in0")], {"lang": "en_US",
+ "search_default_available": 1, "tz": False, "active_model": "ir.ui.menu",
+ "contact_display": "partner_address", "active_ids": [ref("stock.menu_action_picking_tree4")],
+ "active_id": ref("stock.menu_action_picking_tree4"), })
+-
+ I check that the outgoing order of the supplier is create with two move line.
+ Stock/Input To Quality test and Quality test To Stock.
+-
+ I check the move is in waiting state.
+-
+ !python {model: stock.picking }: |
+ from tools.translate import _
+ picking_id = self.search(cr, uid, [('origin','=','Pushed Flow Test'),('type','=','out')])
+ if picking_id:
+ pick=self.browse(cr,uid,picking_id[0])
+ for move in pick.move_lines:
+ assert(move.state == 'waiting'), _('Stock is not in waiting state')
+-
+ I receive the order of the supplier Micro Link Technologies from the Incoming Shipments menu.
+-
+ !python {model: stock.picking }: |
+ import time
+ picking_id = self.search(cr, uid, [('address_id.partner_id','=',ref('res_partner_microlinktechnologies0')),('type','=','in')])
+ if picking_id:
+ pick=self.browse(cr,uid,picking_id[0])
+ move =pick.move_lines[0]
+ partial_datas = {
+ 'partner_id':pick.address_id.partner_id.id,
+ 'address_id': pick.address_id.id,
+ 'delivery_date' : time.strftime('%Y-%m-%d'),
+ }
+ partial_datas['move%s'%(move.id)]= {
+ 'product_id': move.product_id.id,
+ 'product_qty': move.product_qty,
+ 'product_uom': move.product_uom.id,
+ }
+ self.do_partial(cr, uid, picking_id,partial_datas)
+-
+ I check the Outgoing Orders is automatically done.
+-
+ !python {model: stock.picking }: |
+ from tools.translate import _
+ picking_id = self.search(cr, uid, [('origin','=','Pushed Flow Test'),('type','=','out')])
+ if picking_id:
+ pick=self.browse(cr,uid,picking_id[0])
+ assert(pick.state == 'done'), _('Picking is not in done state')
diff --git a/addons/stock_planning/stock_planning.py b/addons/stock_planning/stock_planning.py
index 9d9102d67b3..546e83e0053 100644
--- a/addons/stock_planning/stock_planning.py
+++ b/addons/stock_planning/stock_planning.py
@@ -691,7 +691,7 @@ class stock_planning(osv.osv):
'name': _('MPS(') + str(user.login) +') '+ obj.period_id.name,
'picking_id': picking_id,
'product_id': obj.product_id.id,
- 'date_planned': obj.period_id.date_start,
+ 'date': obj.period_id.date_start,
'product_qty': uom_qty,
'product_uom': uom,
'product_uos_qty': uos_qty,