[CLEAN] stock: pre-migration cleaning

General remarks concerning cleaning commits preparing the stock migration
=========================================================================

Small update / cleaning before moving and migrating. This commit contains
the cleaning of stock_picking_type and stock_picking models.

The first cleaning performed is to clean methods having a browse record or
a list of records replacing ids parameter. Those are converted into classic
old API methods using cr, uid, ids. This eases the migration as they are
then migrated into multi methods.

Another cleaning concerns naming things. Some methods are renamed to ease
the code readability. Some parameters are also renamed as well as some
variables. This kind of change is performed only when the readability
is improved. Otherwise the code is kept as it is to avoid rewriting too
much of the code.

Finally unnecessary stuff is also removed. This concerns dead code (methods
not used in community nor in enterprise) and unnecessary code splitting
(tools methods called once without real added value are directly put into
their called method).

- stock.move: no specific changes; note that a method called only in mrp
  has been moved to mrp.
- product: no specific changes
- config settings: no specific changes
- wizards: no specific changes

stock.quant main changes
========================

 - ``apply_removal_strategy`` method updated to return only the removal
   strategy instead of having a mix of several things (overrides updated)
 - ``quants_get_**`` methods renamed to ease code understanding
 - quants_unreserve from stock.quant to stock.move model because its primary
   record is a move, not quants

stock.inventory and stock.inventory.line main changes
=====================================================

 - stock.inventory: ``move_ids_exist`` field remove. Unused.

stock.picking and stock.picking.type main changes
=================================================

 - stock.picking: ``move_lines_related`` is removed. Indeed it is used only
   to display a slightly different view of moves contained inside a picking.
   However this field causes a lot of issues when migrated into the new API.
   The view is updated to keep the same look and feel while avoiding an
   unnecessary field.
 - stock.picking.type: complete_name field is removed. Using display_name is
   sufficient for the existing use cases.

stock.warehouse and stock.orderpoint main changes
=================================================

 - stock.warehouse: removed ``resupply_from_wh``, unused field

stock.pack.operation main changes
=================================

 - stock.pack.operation: ``processed_boolean`` fields renamed to ``is_done``
   to match its use in the code.

stock.location main changes
===========================

Note that in this commit a strange override in create has been replaced by a
cleaner override in default_get in order to have something more understandable

procurement main changes
========================

 - in this commit some code located in procurement has been moved to the
   orderpoint model. Indeed those methods take an orderpoint as main
   argument, making it more orderpoint methods than procurement methods
 - ``_prepare_orderpoint_procurement`` method has been merged with its
   strange override in stock_calendar to have something a bit more modular
This commit is contained in:
Thibault Delavallée
2016-04-29 12:18:34 +02:00
parent bdd2080d04
commit 304c81c91c
22 changed files with 676 additions and 933 deletions
+3 -3
View File
@@ -1178,12 +1178,12 @@ class mrp_production(osv.osv):
move = stock_move.browse(cr, uid, move_id, context=context)
src_loc = loc_obj.browse(cr, uid, source_location_id, context=context)
dest_loc = loc_obj.browse(cr, uid, dest_location_id, context=context)
code = stock_move.get_code_from_locs(cr, uid, move, src_loc, dest_loc, context=context)
code = stock_move.get_code_from_locs(cr, uid, [move.id], src_loc, dest_loc, context=context)
if code == 'outgoing':
check_loc = src_loc
else:
check_loc = dest_loc
wh = loc_obj.get_warehouse(cr, uid, check_loc, context=context)
wh = loc_obj.get_warehouse(cr, uid, [check_loc.id], context=context)
domain = [('code', '=', code)]
if wh:
domain += [('warehouse_id', '=', wh)]
@@ -1230,7 +1230,7 @@ class mrp_production(osv.osv):
#this saves us a browse in create()
'price_unit': product.standard_price,
'origin': production.name,
'warehouse_id': loc_obj.get_warehouse(cr, uid, production.location_src_id, context=context),
'warehouse_id': loc_obj.get_warehouse(cr, uid, [production.location_src_id.id], context=context),
'group_id': production.move_prod_id.group_id.id,
}, context=context)
+6 -4
View File
@@ -29,16 +29,18 @@ class procurement_order(osv.osv):
self.pool.get('mrp.production').action_cancel(cr, uid, [procurement.production_id.id], context=context)
return super(procurement_order, self).propagate_cancels(cr, uid, ids, context=context)
def _run(self, cr, uid, procurement, context=None):
def _run(self, cr, uid, ids, context=None):
procurement = self.browse(cr, uid, ids[0], context=context)
if procurement.rule_id and procurement.rule_id.action == 'manufacture':
#make a manufacturing order for the procurement
return self.make_mo(cr, uid, [procurement.id], context=context)[procurement.id]
return super(procurement_order, self)._run(cr, uid, procurement, context=context)
return super(procurement_order, self)._run(cr, uid, ids, context=context)
def _check(self, cr, uid, procurement, context=None):
def _check(self, cr, uid, ids, context=None):
procurement = self.browse(cr, uid, ids[0], context=context)
if procurement.production_id and procurement.production_id.state == 'done': # TOCHECK: no better method?
return True
return super(procurement_order, self)._check(cr, uid, procurement, context=context)
return super(procurement_order, self)._check(cr, uid, ids, context=context)
def check_bom_exists(self, cr, uid, ids, context=None):
""" Finds the bill of material for the product from procurement order.
+30 -9
View File
@@ -19,8 +19,26 @@ class StockMove(osv.osv):
'consumed_for': fields.many2one('stock.move', 'Consumed for', help='Technical field used to make the traceability of produced products'),
}
def check_tracking(self, cr, uid, move, ops, context=None):
super(StockMove, self).check_tracking(cr, uid, move, ops, context=context)
def get_code_from_locs(self, cr, uid, ids, location_id=False, location_dest_id=False, context=None):
"""
Returns the code the picking type should have. This can easily be used
to check if a move is internal or not
move, location_id and location_dest_id are browse records
"""
move = self.browse(cr, uid, ids[0], context=context)
# TDE note: called only in MRP
code = 'internal'
src_loc = location_id or move.location_id
dest_loc = location_dest_id or move.location_dest_id
if src_loc.usage == 'internal' and dest_loc.usage != 'internal':
code = 'outgoing'
if src_loc.usage != 'internal' and dest_loc.usage == 'internal':
code = 'incoming'
return code
def check_tracking(self, cr, uid, ids, ops, context=None):
super(StockMove, self).check_tracking(cr, uid, ids, ops, context=context)
move = self.browse(cr, uid, ids[0], context=context)
if move.raw_material_production_id and move.product_id.tracking!='none' and move.location_dest_id.usage == 'production' and move.raw_material_production_id.product_id.tracking != 'none' and not move.consumed_for:
raise UserError(_("Because the product %s requires it, you must assign a serial number to your raw material %s to proceed further in your production. Please use the 'Produce' button to do so.") % (move.raw_material_production_id.product_id.name, move.product_id.name))
@@ -152,7 +170,7 @@ class StockMove(osv.osv):
# Compare with numbers of move uom as we want to avoid a split with 0 qty
quantity_rest_uom = move.product_uom_qty - self.pool.get("product.uom")._compute_qty_obj(cr, uid, move.product_id.uom_id, product_qty, move.product_uom)
if float_compare(quantity_rest_uom, 0, precision_rounding=move.product_uom.rounding) != 0:
new_mov = self.split(cr, uid, move, quantity_rest, context=context)
new_mov = self.split(cr, uid, [move.id], quantity_rest, context=context)
if move.production_id:
self.write(cr, uid, [new_mov], {'production_id': move.production_id.id}, context=context)
res.append(new_mov)
@@ -237,9 +255,10 @@ class stock_warehouse(osv.osv):
'warehouse_id': warehouse.id,
}
def create_routes(self, cr, uid, ids, warehouse, context=None):
def create_routes(self, cr, uid, ids, context=None):
pull_obj = self.pool.get('procurement.rule')
res = super(stock_warehouse, self).create_routes(cr, uid, ids, warehouse, context=context)
res = super(stock_warehouse, self).create_routes(cr, uid, ids, context=context)
warehouse = self.browse(cr, uid, ids, context=context)[0]
if warehouse.manufacture_to_resupply:
manufacture_pull_vals = self._get_manufacture_pull_rule(cr, uid, warehouse, context=context)
manufacture_pull_id = pull_obj.create(cr, uid, manufacture_pull_vals, context=context)
@@ -264,14 +283,16 @@ class stock_warehouse(osv.osv):
pull_obj.unlink(cr, uid, warehouse.manufacture_pull_id.id, context=context)
return super(stock_warehouse, self).write(cr, uid, ids, vals, context=context)
def get_all_routes_for_wh(self, cr, uid, warehouse, context=None):
all_routes = super(stock_warehouse, self).get_all_routes_for_wh(cr, uid, warehouse, context=context)
def get_all_routes_for_wh(self, cr, uid, ids, context=None):
all_routes = super(stock_warehouse, self).get_all_routes_for_wh(cr, uid, ids, context=context)
warehouse = self.browse(cr, uid, ids[0], context=context)
if warehouse.manufacture_to_resupply and warehouse.manufacture_pull_id and warehouse.manufacture_pull_id.route_id:
all_routes += [warehouse.manufacture_pull_id.route_id.id]
return all_routes
def _handle_renaming(self, cr, uid, warehouse, name, code, context=None):
res = super(stock_warehouse, self)._handle_renaming(cr, uid, warehouse, name, code, context=context)
def _handle_renaming(self, cr, uid, ids, name, code, context=None):
res = super(stock_warehouse, self)._handle_renaming(cr, uid, ids, name, code, context=context)
warehouse = self.browse(cr, uid, ids[0], context=context)
pull_obj = self.pool.get('procurement.rule')
#change the manufacture procurement rule name
if warehouse.manufacture_pull_id:
+10 -16
View File
@@ -170,12 +170,9 @@ class procurement_order(osv.osv):
return {'value': v}
return {}
def get_cancel_ids(self, cr, uid, ids, context=None):
return [proc.id for proc in self.browse(cr, uid, ids, context=context) if proc.state != 'done']
def cancel(self, cr, uid, ids, context=None):
#cancel only the procurements that aren't done already
to_cancel_ids = self.get_cancel_ids(cr, uid, ids, context=context)
to_cancel_ids = [proc.id for proc in self.browse(cr, uid, ids, context=context) if proc.state != 'done']
if to_cancel_ids:
return self.write(cr, uid, to_cancel_ids, {'state': 'cancel'}, context=context)
@@ -190,8 +187,8 @@ class procurement_order(osv.osv):
procurement = self.browse(cr, uid, procurement_id, context=context)
if procurement.state not in ("running", "done"):
try:
if self._assign(cr, uid, procurement, context=context):
res = self._run(cr, uid, procurement, context=context or {})
if self._assign(cr, uid, [procurement.id], context=context):
res = self._run(cr, uid, [procurement.id], context=context or {})
if res:
self.write(cr, uid, [procurement.id], {'state': 'running'}, context=context)
else:
@@ -213,7 +210,7 @@ class procurement_order(osv.osv):
done_ids = []
for procurement in self.browse(cr, uid, ids, context=context):
try:
result = self._check(cr, uid, procurement, context=context)
result = self._check(cr, uid, [procurement.id], context=context)
if result:
done_ids.append(procurement.id)
if autocommit:
@@ -231,41 +228,38 @@ class procurement_order(osv.osv):
#
# Method to overwrite in different procurement modules
#
def _find_suitable_rule(self, cr, uid, procurement, context=None):
def _find_suitable_rule(self, cr, uid, ids, context=None):
'''This method returns a procurement.rule that depicts what to do with the given procurement
in order to complete its needs. It returns False if no suiting rule is found.
:param procurement: browse record
:rtype: int or False
'''
return False
def _assign(self, cr, uid, procurement, context=None):
def _assign(self, cr, uid, ids, context=None):
'''This method check what to do with the given procurement in order to complete its needs.
It returns False if no solution is found, otherwise it stores the matching rule (if any) and
returns True.
:param procurement: browse record
:rtype: boolean
'''
procurement = self.browse(cr, uid, ids[0], context=context)
#if the procurement already has a rule assigned, we keep it (it has a higher priority as it may have been chosen manually)
if procurement.rule_id:
return True
elif procurement.product_id.type not in ('service', 'digital'):
rule_id = self._find_suitable_rule(cr, uid, procurement, context=context)
rule_id = self._find_suitable_rule(cr, uid, ids, context=context)
if rule_id:
self.write(cr, uid, [procurement.id], {'rule_id': rule_id}, context=context)
return True
return False
def _run(self, cr, uid, procurement, context=None):
def _run(self, cr, uid, ids, context=None):
'''This method implements the resolution of the given procurement
:param procurement: browse record
:returns: True if the resolution of the procurement was a success, False otherwise to set it in exception
'''
return True
def _check(self, cr, uid, procurement, context=None):
def _check(self, cr, uid, ids, context=None):
'''Returns True if the given procurement is fulfilled, False otherwise
:param procurement: browse record
:rtype: boolean
'''
return False
+3 -3
View File
@@ -10,7 +10,7 @@ class StockQuant(models.Model):
removal_date = fields.Datetime(related='lot_id.removal_date', store=True)
@api.model
def apply_removal_strategy(self, qty, move, ops=False, domain=None, removal_strategy='fifo'):
def _quants_removal_get_order(self, removal_strategy):
if removal_strategy == 'fefo':
return self._quants_get_order(qty, move, ops=ops, domain=domain, orderby='removal_date, in_date, id')
return super(StockQuant, self).apply_removal_strategy(qty, move, ops=ops, domain=domain, removal_strategy=removal_strategy)
return 'removal_date, in_date, id'
return super(StockQuant, self)._quants_removal_get_order(removal_strategy=removal_strategy)
+12 -11
View File
@@ -339,6 +339,7 @@ class PurchaseOrder(models.Model):
for pick in order.picking_ids.filtered(lambda r: r.state != 'cancel'):
pick.action_cancel()
# TDE FIXME: I don' think context key is necessary, as actions are not related / called from each other
if not self.env.context.get('cancel_procurement'):
procurements = order.order_line.mapped('procurement_ids')
procurements.filtered(lambda r: r.state not in ('cancel', 'exception') and r.rule_id.propagate).write({'state': 'cancel'})
@@ -811,19 +812,19 @@ class ProcurementOrder(models.Model):
return result
@api.model
def _run(self, procurement):
if procurement.rule_id and procurement.rule_id.action == 'buy':
return procurement.make_po()
return super(ProcurementOrder, self)._run(procurement)
@api.multi
def _run(self):
if self.rule_id and self.rule_id.action == 'buy':
return self.make_po()
return super(ProcurementOrder, self)._run()
@api.model
def _check(self, procurement):
if procurement.purchase_line_id:
if not procurement.move_ids:
@api.multi
def _check(self):
if self.purchase_line_id:
if not self.move_ids:
return False
return all(move.state == 'done' for move in procurement.move_ids)
return super(ProcurementOrder, self)._check(procurement)
return all(move.state == 'done' for move in self.move_ids)
return super(ProcurementOrder, self)._check()
@api.v8
def _get_purchase_schedule_date(self):
+22 -16
View File
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from openerp import api
from openerp.osv import fields, osv
from openerp.tools.translate import _
from openerp.exceptions import UserError
@@ -29,22 +30,23 @@ class stock_move(osv.osv):
readonly=True),
}
def get_price_unit(self, cr, uid, move, context=None):
@api.multi
def get_price_unit(self):
""" Returns the unit price to store on the quant """
if move.purchase_line_id:
order = move.purchase_line_id.order_id
if self.purchase_line_id:
order = self.purchase_line_id.order_id
#if the currency of the PO is different than the company one, the price_unit on the move must be reevaluated
#(was created at the rate of the PO confirmation, but must be valuated at the rate of stock move execution)
if order.currency_id != move.company_id.currency_id:
if order.currency_id != self.company_id.currency_id:
#we don't pass the move.date in the compute() for the currency rate on purpose because
# 1) get_price_unit() is supposed to be called only through move.action_done(),
# 2) the move hasn't yet the correct date (currently it is the expected date, after
# completion of action_done() it will be now() )
price_unit = move.purchase_line_id._get_stock_move_price_unit()
move.write({'price_unit': price_unit})
price_unit = self.purchase_line_id._get_stock_move_price_unit()
self.write({'price_unit': price_unit})
return price_unit
return move.price_unit
return super(stock_move, self).get_price_unit(cr, uid, move, context=context)
return self.price_unit
return super(stock_move, self).get_price_unit()
def copy(self, cr, uid, id, default=None, context=None):
default = default or {}
@@ -87,9 +89,10 @@ class stock_warehouse(osv.osv):
'group_propagation_option': 'none',
}
def create_routes(self, cr, uid, ids, warehouse, context=None):
def create_routes(self, cr, uid, ids, context=None):
pull_obj = self.pool.get('procurement.rule')
res = super(stock_warehouse, self).create_routes(cr, uid, ids, warehouse, context=context)
res = super(stock_warehouse, self).create_routes(cr, uid, ids, context=context)
warehouse = self.browse(cr, uid, ids, context=context)[0]
if warehouse.buy_to_resupply:
buy_pull_vals = self._get_buy_pull_rule(cr, uid, warehouse, context=context)
buy_pull_id = pull_obj.create(cr, uid, buy_pull_vals, context=context)
@@ -114,8 +117,9 @@ class stock_warehouse(osv.osv):
buy_pull_id = pull_obj.unlink(cr, uid, warehouse.buy_pull_id.id, context=context)
return super(stock_warehouse, self).write(cr, uid, ids, vals, context=None)
def get_all_routes_for_wh(self, cr, uid, warehouse, context=None):
all_routes = super(stock_warehouse, self).get_all_routes_for_wh(cr, uid, warehouse, context=context)
def get_all_routes_for_wh(self, cr, uid, ids, context=None):
all_routes = super(stock_warehouse, self).get_all_routes_for_wh(cr, uid, ids, context=context)
warehouse = self.browse(cr, uid, ids[0], context=context)
if warehouse.buy_to_resupply and warehouse.buy_pull_id and warehouse.buy_pull_id.route_id:
all_routes += [warehouse.buy_pull_id.route_id.id]
return all_routes
@@ -130,16 +134,18 @@ class stock_warehouse(osv.osv):
break
return res
def _handle_renaming(self, cr, uid, warehouse, name, code, context=None):
res = super(stock_warehouse, self)._handle_renaming(cr, uid, warehouse, name, code, context=context)
def _handle_renaming(self, cr, uid, ids, name, code, context=None):
res = super(stock_warehouse, self)._handle_renaming(cr, uid, ids, name, code, context=context)
warehouse = self.browse(cr, uid, ids[0], context=context)
pull_obj = self.pool.get('procurement.rule')
#change the buy procurement rule name
if warehouse.buy_pull_id:
pull_obj.write(cr, uid, warehouse.buy_pull_id.id, {'name': warehouse.buy_pull_id.name.replace(warehouse.name, name, 1)}, context=context)
return res
def change_route(self, cr, uid, ids, warehouse, new_reception_step=False, new_delivery_step=False, context=None):
res = super(stock_warehouse, self).change_route(cr, uid, ids, warehouse, new_reception_step=new_reception_step, new_delivery_step=new_delivery_step, context=context)
def change_route(self, cr, uid, ids, new_reception_step=False, new_delivery_step=False, context=None):
res = super(stock_warehouse, self).change_route(cr, uid, ids, new_reception_step=new_reception_step, new_delivery_step=new_delivery_step, context=context)
warehouse = self.browse(cr, uid, ids[0], context=context)
if warehouse.in_type_id.default_location_dest_id != warehouse.buy_pull_id.location_id:
self.pool.get('procurement.rule').write(cr, uid, warehouse.buy_pull_id.id, {'location_id': warehouse.in_type_id.default_location_dest_id.id}, context=context)
return res
+5 -5
View File
@@ -70,11 +70,11 @@ class SaleOrderLine(models.Model):
class StockMove(models.Model):
_inherit = 'stock.move'
@api.model
def _prepare_procurement_from_move(self, move):
res = super(StockMove, self)._prepare_procurement_from_move(move)
if res and move.procurement_id and move.procurement_id.property_ids:
res['property_ids'] = [(6, 0, move.procurement_id.property_ids.ids)]
@api.multi
def _prepare_procurement_from_move(self):
res = super(StockMove, self)._prepare_procurement_from_move()
if res and self.procurement_id and self.procurement_id.property_ids:
res['property_ids'] = [(6, 0, self.procurement_id.property_ids.ids)]
return res
@api.model
+3 -3
View File
@@ -265,9 +265,9 @@ class AccountInvoice(models.Model):
class ProcurementOrder(models.Model):
_inherit = "procurement.order"
@api.model
def _run_move_create(self, procurement):
vals = super(ProcurementOrder, self)._run_move_create(procurement)
@api.multi
def _run_move_create(self):
vals = super(ProcurementOrder, self)._run_move_create()
if self.sale_line_id:
vals.update({'sequence': self.sale_line_id.sequence})
return vals
+10 -7
View File
@@ -14,21 +14,24 @@ class procurement_order(osv.osv):
'task_id': fields.many2one('project.task', 'Task', copy=False),
}
def _is_procurement_task(self, cr, uid, procurement, context=None):
def _is_procurement_task(self, cr, uid, ids, context=None):
procurement = self.browse(cr, uid, ids[0], context=context)
return procurement.product_id.type == 'service' and procurement.product_id.track_service=='task' or False
def _assign(self, cr, uid, procurement, context=None):
res = super(procurement_order, self)._assign(cr, uid, procurement, context=context)
def _assign(self, cr, uid, ids, context=None):
procurement = self.browse(cr, uid, ids[0], context=context)
res = super(procurement_order, self)._assign(cr, uid, ids, context=context)
if not res:
#if there isn't any specific procurement.rule defined for the product, we may want to create a task
return self._is_procurement_task(cr, uid, procurement, context=context)
return procurement._is_procurement_task()
return res
def _run(self, cr, uid, procurement, context=None):
if self._is_procurement_task(cr, uid, procurement, context=context) and not procurement.task_id:
def _run(self, cr, uid, ids, context=None):
procurement = self.browse(cr, uid, ids[0], context=context)
if procurement._is_procurement_task() and not procurement.task_id:
#create a task for the procurement
return self._create_service_task(cr, uid, procurement, context=context)
return super(procurement_order, self)._run(cr, uid, procurement, context=context)
return super(procurement_order, self)._run(cr, uid, ids, context=context)
def _convert_qty_company_hours(self, cr, uid, procurement, context=None):
product_uom = self.pool.get('product.uom')
+93 -222
View File
@@ -6,6 +6,8 @@ from openerp.tools.translate import _
from openerp.tools import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT, float_compare, float_round
from openerp import SUPERUSER_ID
from collections import defaultdict
from dateutil.relativedelta import relativedelta
from datetime import datetime
from psycopg2 import OperationalError
@@ -89,7 +91,8 @@ class procurement_order(osv.osv):
self.propagate_cancels(cr, uid, to_cancel_ids, context=ctx)
return super(procurement_order, self).cancel(cr, uid, to_cancel_ids, context=ctx)
def _find_parent_locations(self, cr, uid, procurement, context=None):
def _find_parent_locations(self, cr, uid, ids, context=None):
procurement = self.browse(cr, uid, ids[0], context=context)
location = procurement.location_id
res = [location.id]
while location.location_id:
@@ -103,141 +106,9 @@ class procurement_order(osv.osv):
return {'value': {'location_id': warehouse.lot_stock_id.id}}
return {}
#Doing assignation, ... in multi
def _assign_multi(self, cr, uid, procurements, context=None):
res = {}
todo_procs = []
context = context or {}
for procurement in procurements:
if procurement.rule_id:
res[procurement.id] = True
elif procurement.product_id.type in ['product', 'consu']:
todo_procs += [procurement]
res_dict = self._find_suitable_rule_multi(cr, uid, todo_procs, context=context)
rule_dict = {}
for proc in res_dict.keys():
if res_dict[proc]:
if rule_dict.get(res_dict[proc]):
rule_dict[res_dict[proc]] += [proc]
else:
rule_dict[res_dict[proc]] = [proc]
for rule in rule_dict.keys():
self.write(cr, uid, rule_dict[rule], {'rule_id': rule}, context=context)
def _get_route_group_dict(self, cr, uid, procurements, context=None):
"""
Returns a dictionary with key the routes and values the products associated
"""
ids = [x.id for x in procurements]
cr.execute("""
SELECT proc_id, route_id FROM
((SELECT p.id AS proc_id, route_id
FROM stock_route_product AS link, procurement_order AS p, product_template AS pt, product_product pp
WHERE pp.product_tmpl_id = pt.id AND link.product_id = pt.id AND pp.id = p.product_id
AND p.id in %s)
UNION (SELECT p.id AS proc_id, link.route_id AS route_id
FROM stock_location_route_categ AS link, product_product AS pp, procurement_order AS p,
product_template AS pt, product_category AS pc, product_category AS pc_product
WHERE p.product_id = pp.id AND pp.product_tmpl_id = pt.id AND pc_product.id = pt.categ_id AND
pc.parent_left <= pc_product.parent_left AND pc.parent_right >= pc_product.parent_left
AND link.categ_id = pc.id AND pp.id IN %s)) p ORDER BY proc_id, route_id
""", (tuple(ids), tuple(ids), ))
product_routes = cr.fetchall()
old_proc = False
key = tuple()
key_routes = {}
proc = False
for proc, route in product_routes:
if not old_proc:
old_proc = proc
if old_proc == proc:
key += (route,)
else:
if key_routes.get(key):
key_routes[key] += [old_proc]
else:
key_routes[key] = [old_proc]
old_proc = proc
key = (route,)
if proc: #do not forget last one as we passed through it
if key_routes.get(key):
key_routes[key] += [proc]
else:
key_routes[key] = [proc]
return key_routes
def _get_wh_loc_dict(self, cr, uid, procurements, context=None):
wh_dict = {}
for procurement in procurements:
if wh_dict.get(procurement.warehouse_id.id):
if wh_dict[procurement.warehouse_id.id].get(procurement.location_id):
wh_dict[procurement.warehouse_id.id][procurement.location_id] += [procurement]
else:
wh_dict[procurement.warehouse_id.id][procurement.location_id] = [procurement]
else:
wh_dict[procurement.warehouse_id.id] = {}
wh_dict[procurement.warehouse_id.id][procurement.location_id] = [procurement]
return wh_dict
def _find_suitable_rule_multi(self, cr, uid, procurements, domain = [], context=None):
'''we try to first find a rule among the ones defined on the procurement order group and if none is found, we try on the routes defined for the product, and finally we fallback on the default behavior'''
results_dict = {}
pull_obj = self.pool.get('procurement.rule')
warehouse_route_ids = []
for procurement in procurements: #Could be replaced by one query for all route_ids
if procurement.route_ids:
procurement_route_ids = [x.id for x in procurement.route_ids]
loc = procurement.location_id
loc_domain = [('location_id.parent_left', '<=', loc.parent_left),
('location_id.parent_right', '>=', loc.parent_left)]
if procurement.warehouse_id:
domain += ['|', ('warehouse_id', '=', procurement.warehouse_id.id), ('warehouse_id', '=', False)]
res = pull_obj.search(cr, uid, loc_domain + [('route_id', 'in', procurement_route_ids)], order='route_sequence, sequence', context=context)
if res and res[0]:
results_dict[procurement.id] = res[0]
procurements_to_check = [x for x in procurements if x.id not in results_dict.keys()]
#group by warehouse_id:
wh_dict = self._get_wh_loc_dict(cr, uid, procurements_to_check, context=context)
for wh in wh_dict.keys():
warehouse_route_ids = []
domain = []
check_wh = False
for loc in wh_dict[wh].keys():
procurement = wh_dict[wh][loc][0]
loc_domain = [('location_id.parent_left', '<=', loc.parent_left),
('location_id.parent_right', '>=', loc.parent_left)]
if wh and not check_wh:
domain += ['|', ('warehouse_id', '=', procurement.warehouse_id.id), ('warehouse_id', '=', False)]
warehouse_route_ids = [x.id for x in procurement.warehouse_id.route_ids]
check_wh = True
key_routes = self._get_route_group_dict(cr, uid, wh_dict[wh][loc], context=context)
for key in key_routes.keys():
procurements = self.browse(cr, uid, key_routes[key], context=context)
domain = loc_domain + domain
res = pull_obj.search(cr, uid, domain + [('route_id', 'in', list(key))], order='route_sequence, sequence', context=context)
result = False
if res and res[0]:
result = res[0]
elif warehouse_route_ids:
res = pull_obj.search(cr, uid, domain + [('route_id', 'in', warehouse_route_ids)], order='route_sequence, sequence', context=context)
result = res and res[0]
if not result:
res = pull_obj.search(cr, uid, domain + [('route_id', '=', False)], order='sequence', context=context)
result = res and res[0]
for proc in key_routes[key]:
results_dict[proc] = result
return results_dict
def _search_suitable_rule(self, cr, uid, procurement, domain, context=None):
def _search_suitable_rule(self, cr, uid, ids, domain, context=None):
'''we try to first find a rule among the ones defined on the procurement order group and if none is found, we try on the routes defined for the product, and finally we fallback on the default behavior'''
procurement = self.browse(cr, uid, ids[0], context=context)
pull_obj = self.pool.get('procurement.rule')
warehouse_route_ids = []
if procurement.warehouse_id:
@@ -254,22 +125,24 @@ class procurement_order(osv.osv):
res = pull_obj.search(cr, uid, domain + [('route_id', '=', False)], order='sequence', context=context)
return res
def _find_suitable_rule(self, cr, uid, procurement, context=None):
rule_id = super(procurement_order, self)._find_suitable_rule(cr, uid, procurement, context=context)
def _find_suitable_rule(self, cr, uid, ids, context=None):
procurement = self.browse(cr, uid, ids[0], context=context)
rule_id = super(procurement_order, self)._find_suitable_rule(cr, uid, ids, context=context)
if not rule_id:
#a rule defined on 'Stock' is suitable for a procurement in 'Stock\Bin A'
all_parent_location_ids = self._find_parent_locations(cr, uid, procurement, context=context)
rule_id = self._search_suitable_rule(cr, uid, procurement, [('location_id', 'in', all_parent_location_ids)], context=context)
all_parent_location_ids = procurement._find_parent_locations()
rule_id = procurement._search_suitable_rule([('location_id', 'in', all_parent_location_ids)])
rule_id = rule_id and rule_id[0] or False
return rule_id
def _run_move_create(self, cr, uid, procurement, context=None):
def _run_move_create(self, cr, uid, ids, context=None):
''' Returns a dictionary of values that will be used to create a stock move from a procurement.
This function assumes that the given procurement has a rule (action == 'move') set on it.
:param procurement: browse record
:rtype: dictionary
'''
procurement = self.browse(cr, uid, ids[0], context=context)
newdate = (datetime.strptime(procurement.date_planned, '%Y-%m-%d %H:%M:%S') - relativedelta(days=procurement.rule_id.delay or 0)).strftime('%Y-%m-%d %H:%M:%S')
group_id = False
if procurement.rule_id.group_propagation_option == 'propagate':
@@ -307,17 +180,18 @@ class procurement_order(osv.osv):
}
return vals
def _run(self, cr, uid, procurement, context=None):
def _run(self, cr, uid, ids, context=None):
procurement = self.browse(cr, uid, ids[0], context=context)
if procurement.rule_id and procurement.rule_id.action == 'move':
if not procurement.rule_id.location_src_id:
self.message_post(cr, uid, [procurement.id], body=_('No source location defined!'), context=context)
return False
move_obj = self.pool.get('stock.move')
move_dict = self._run_move_create(cr, uid, procurement, context=context)
move_dict = procurement._run_move_create()
#create the move as SUPERUSER because the current user may not have the rights to do it (mto product launched by a sale for example)
move_obj.create(cr, SUPERUSER_ID, move_dict, context=context)
return True
return super(procurement_order, self)._run(cr, uid, procurement, context=context)
return super(procurement_order, self)._run(cr, uid, ids, context=context)
def run(self, cr, uid, ids, autocommit=False, context=None):
new_ids = [x.id for x in self.browse(cr, uid, ids, context=context) if x.state not in ('running', 'done', 'cancel')]
@@ -338,10 +212,11 @@ class procurement_order(osv.osv):
res = res and self.run(cr, uid, procurement_ids, autocommit=autocommit, context=context)
return res
def _check(self, cr, uid, procurement, context=None):
def _check(self, cr, uid, ids, context=None):
''' Implement the procurement checking for rules of type 'move'. The procurement will be satisfied only if all related
moves are done/cancel and if the requested quantity is moved.
'''
procurement = self.browse(cr, uid, ids[0], context=context)
if procurement.rule_id and procurement.rule_id.action == 'move':
uom_obj = self.pool.get('product.uom')
# In case Phantom BoM splits only into procurements
@@ -421,28 +296,19 @@ class procurement_order(osv.osv):
pass
return {}
def _get_orderpoint_date_planned(self, cr, uid, orderpoint, start_date, context=None):
days = orderpoint.lead_days or 0.0
if orderpoint.lead_type=='purchase':
# These days will be substracted when creating the PO
days += orderpoint.product_id._select_seller(orderpoint.product_id).delay or 0.0
date_planned = start_date + relativedelta(days=days)
return date_planned.strftime(DEFAULT_SERVER_DATE_FORMAT)
def _procurement_from_orderpoint_get_order(self, cr, uid, context=None):
return 'location_id'
def _prepare_orderpoint_procurement(self, cr, uid, orderpoint, product_qty, context=None):
return {
'name': orderpoint.name,
'date_planned': self._get_orderpoint_date_planned(cr, uid, orderpoint, datetime.today(), context=context),
'product_id': orderpoint.product_id.id,
'product_qty': product_qty,
'company_id': orderpoint.company_id.id,
'product_uom': orderpoint.product_uom.id,
'location_id': orderpoint.location_id.id,
'origin': orderpoint.name,
'warehouse_id': orderpoint.warehouse_id.id,
'orderpoint_id': orderpoint.id,
'group_id': orderpoint.group_id.id,
}
def _procurement_from_orderpoint_get_grouping_key(self, cr, uid, orderpoint_ids, context=None):
orderpoint = self.pool['stock.warehouse.orderpoint'].browse(cr, uid, orderpoint_ids[0], context=context)
return orderpoint.location_id.id
def _procurement_from_orderpoint_get_groups(self, cr, uid, orderpoint_ids, context=None):
""" Make groups for a given orderpoint; by default schedule all operations in one without date """
return [{'to_date': False, 'procurement_values': dict()}]
def _procurement_from_orderpoint_post_process(self, cr, uid, orderpoint_ids, context=None):
return True
def _procure_orderpoint_confirm(self, cr, uid, use_new_cursor=False, company_id=False, context=None):
'''
@@ -453,74 +319,83 @@ class procurement_order(osv.osv):
'''
if context is None:
context = {}
procurement_context = dict(context, procurement_autorun_defer=True)
if use_new_cursor:
cr = openerp.registry(cr.dbname).cursor()
orderpoint_obj = self.pool.get('stock.warehouse.orderpoint')
procurement_obj = self.pool.get('procurement.order')
product_obj = self.pool.get('product.product')
dom = company_id and [('company_id', '=', company_id)] or []
orderpoint_ids = orderpoint_obj.search(cr, uid, dom, order="location_id")
prev_ids = []
tot_procs = []
orderpoint_ids = orderpoint_obj.search(
cr, uid,
company_id and [('company_id', '=', company_id)] or [],
order=self._procurement_from_orderpoint_get_order(cr, uid, context=context))
procurement_ids = []
while orderpoint_ids:
ids = orderpoint_ids[:1000]
del orderpoint_ids[:1000]
product_dict = {}
ops_dict = {}
ops = orderpoint_obj.browse(cr, uid, ids, context=context)
current_ids = orderpoint_ids[:1000]
orderpoint_ids = orderpoint_ids[1000:]
orderpoints = orderpoint_obj.browse(cr, uid, current_ids, context=context)
#Calculate groups that can be executed together
for op in ops:
key = (op.location_id.id,)
if not product_dict.get(key):
product_dict[key] = [op.product_id]
ops_dict[key] = [op]
else:
product_dict[key] += [op.product_id]
ops_dict[key] += [op]
# Calculate groups that can be executed together
location_data = defaultdict(lambda: dict(products=list(), orderpoints=list(), groups=list()))
for key in product_dict.keys():
ctx = context.copy()
ctx.update({'location': ops_dict[key][0].location_id.id})
prod_qty = product_obj._product_available(cr, uid, [x.id for x in product_dict[key]],
context=ctx)
subtract_qty = orderpoint_obj.subtract_procurements_from_orderpoints(cr, uid, [x.id for x in ops_dict[key]], context=context)
for op in ops_dict[key]:
try:
prods = prod_qty[op.product_id.id]['virtual_available']
if prods is None:
continue
if float_compare(prods, op.product_min_qty, precision_rounding=op.product_uom.rounding) <= 0:
qty = max(op.product_min_qty, op.product_max_qty) - prods
reste = op.qty_multiple > 0 and qty % op.qty_multiple or 0.0
if float_compare(reste, 0.0, precision_rounding=op.product_uom.rounding) > 0:
qty += op.qty_multiple - reste
for orderpoint in orderpoints:
key = self._procurement_from_orderpoint_get_grouping_key(cr, uid, [orderpoint.id], context=context)
location_data[key]['products'] += orderpoint.product_id
location_data[key]['orderpoints'] += orderpoint
location_data[key]['groups'] = self._procurement_from_orderpoint_get_groups(cr, uid, [orderpoint.id], context=context)
if float_compare(qty, 0.0, precision_rounding=op.product_uom.rounding) < 0:
for location_id, location_data in location_data.iteritems():
location_orderpoints = location_data['orderpoints']
product_context = dict(context, location=location_orderpoints[0].location_id.id)
substract_quantity = self.pool['stock.warehouse.orderpoint'].subtract_procurements_from_orderpoints(
cr, uid,
[op.id for op in location_orderpoints],
context=context)
for group in location_data['groups']:
if group['to_date']:
product_context['to_date'] = group['to_date'].strftime(DEFAULT_SERVER_DATETIME_FORMAT)
product_quantity = product_obj._product_available(cr, uid, [prod.id for prod in location_data['products']], context=product_context)
for orderpoint in location_orderpoints:
try:
op_product_virtual = product_quantity[orderpoint.product_id.id]['virtual_available']
if op_product_virtual is None:
continue
if float_compare(op_product_virtual, orderpoint.product_min_qty, precision_rounding=orderpoint.product_uom.rounding) <= 0:
qty = max(orderpoint.product_min_qty, orderpoint.product_max_qty) - op_product_virtual
remainder = orderpoint.qty_multiple > 0 and qty % orderpoint.qty_multiple or 0.0
qty -= subtract_qty[op.id]
if float_compare(remainder, 0.0, precision_rounding=orderpoint.product_uom.rounding) > 0:
qty += orderpoint.qty_multiple - remainder
qty_rounded = float_round(qty, precision_rounding=op.product_uom.rounding)
if qty_rounded > 0:
proc_id = procurement_obj.create(cr, uid,
self._prepare_orderpoint_procurement(cr, uid, op, qty_rounded, context=context),
context=dict(context, procurement_autorun_defer=True))
tot_procs.append(proc_id)
if float_compare(qty, 0.0, precision_rounding=orderpoint.product_uom.rounding) < 0:
continue
qty -= substract_quantity[orderpoint.id]
qty_rounded = float_round(qty, precision_rounding=orderpoint.product_uom.rounding)
if qty_rounded > 0:
new_procurement_id = procurement_obj.create(
cr, uid,
orderpoint._prepare_procurement_values(qty_rounded, **group['procurement_values']),
context=procurement_context)
procurement_ids.append(new_procurement_id)
self._procurement_from_orderpoint_post_process(cr, uid, [orderpoint.id], context=context)
if use_new_cursor:
cr.commit()
except OperationalError:
if use_new_cursor:
cr.commit()
except OperationalError:
if use_new_cursor:
orderpoint_ids.append(op.id)
cr.rollback()
continue
else:
raise
orderpoint_ids += orderpoint.id
cr.rollback()
continue
else:
raise
try:
tot_procs.reverse()
self.run(cr, uid, tot_procs, context=context)
tot_procs = []
procurement_ids.reverse()
self.run(cr, uid, procurement_ids, context=context)
procurement_ids = []
if use_new_cursor:
cr.commit()
except OperationalError:
@@ -532,10 +407,6 @@ class procurement_order(osv.osv):
if use_new_cursor:
cr.commit()
if prev_ids == ids:
break
else:
prev_ids = ids
if use_new_cursor:
cr.commit()
+2 -13
View File
@@ -209,12 +209,6 @@ class product_product(osv.osv):
quants = dict((k, v) for k, v in quants.iteritems() if eval(str(v) + operator + str(value)))
return(list(quants))
def _product_available_text(self, cr, uid, ids, field_names=None, arg=False, context=None):
res = {}
for product in self.browse(cr, uid, ids, context=context):
res[product.id] = str(product.qty_available) + _(" On Hand")
return res
def _compute_nbr_reordering_rules(self, cr, uid, ids, field_names=None, arg=None, context=None):
res = dict.fromkeys(ids, {'nbr_reordering_rules': 0, 'reordering_min_qty': 0, 'reordering_max_qty': 0})
product_data = self.pool['stock.warehouse.orderpoint'].read_group(cr, uid, [('product_id', 'in', ids)], ['product_id', 'product_min_qty', 'product_max_qty'], ['product_id'], context=context)
@@ -382,12 +376,6 @@ class product_template(osv.osv):
product_variant_ids = prod.search(cr, uid, domain, context=context)
return [('product_variant_ids', 'in', product_variant_ids)]
def _product_available_text(self, cr, uid, ids, field_names=None, arg=False, context=None):
res = {}
for product in self.browse(cr, uid, ids, context=context):
res[product.id] = str(product.qty_available) + _(" On Hand")
return res
def _compute_nbr_reordering_rules(self, cr, uid, ids, field_names=None, arg=None, context=None):
res = {k: {'nbr_reordering_rules': 0, 'reordering_min_qty': 0, 'reordering_max_qty': 0} for k in ids}
product_data = self.pool['stock.warehouse.orderpoint'].read_group(cr, uid, [('product_id.product_tmpl_id', 'in', ids)], ['product_id', 'product_min_qty', 'product_max_qty'], ['product_id'], context=context)
@@ -552,7 +540,8 @@ class product_putaway_strategy(osv.osv):
'method': 'fixed',
}
def putaway_apply(self, cr, uid, putaway_strat, product, context=None):
def putaway_apply(self, cr, uid, ids, product, context=None):
putaway_strat = self.browse(cr, uid, ids[0], context=context)
if putaway_strat.method == 'fixed':
for strat in putaway_strat.fixed_location_ids:
categ = product.categ_id
-9
View File
@@ -159,15 +159,6 @@ class stock_config_settings(osv.osv_memory):
user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
return user.company_id.id
def get_default_dp(self, cr, uid, fields, context=None):
dp = self.pool.get('ir.model.data').get_object(cr, uid, 'product', 'decimal_stock_weight')
return {'decimal_precision': dp.digits}
def set_default_dp(self, cr, uid, ids, context=None):
config = self.browse(cr, uid, ids[0], context)
dp = self.pool.get('ir.model.data').get_object(cr, uid, 'product', 'decimal_stock_weight')
dp.write({'digits': config.decimal_precision})
_defaults = {
'company_id': _default_company,
}
+425 -460
View File
File diff suppressed because it is too large Load Diff
-1
View File
@@ -4,7 +4,6 @@
<field name="model">stock.picking.type</field>
<field name="arch" type="xml">
<kanban class="oe_background_grey o_kanban_dashboard o_stock_kanban" create="0">
<field name="complete_name"/>
<field name="color"/>
<field name="code"/>
<field name="count_picking_ready"/>
+6 -7
View File
@@ -108,7 +108,7 @@
<button name="prepare_inventory" states="draft" string="Start Inventory" type="object" class="oe_highlight" groups="stock.group_stock_user"/>
<button name="action_done" states="confirm" string="Validate Inventory" type="object" class="oe_highlight" groups="stock.group_stock_manager"/>
<button name="action_cancel_draft" states="cancel" string="Set to Draft" type="object"/>
<button name="action_cancel_inventory" states="confirm" string="Cancel Inventory" type="object"/>
<button name="action_cancel_draft" states="confirm" string="Cancel Inventory" type="object"/>
<field name="state" widget="statusbar" statusbar_visible="draft,confirm,done"/>
</header>
<sheet>
@@ -600,7 +600,7 @@
<button name="toggle_active" type="object" class="oe_stat_button" icon="fa-archive">
<field name="active" widget="boolean_button" options='{"terminology": "archive"}'/>
</button>
<button name="view_all_routes_for_wh"
<button name="action_view_all_routes"
string="Routes"
icon="fa-refresh"
class="oe_stat_button"
@@ -1008,7 +1008,7 @@
<field name="state" invisible="1"/>
<field name="qty_done" invisible="1"/>
<field name="lots_visible" invisible="1"/>
<field name="processed_boolean"/>
<field name="is_done"/>
<button name="show_details" string="Modify" type="object" icon="fa-pencil"
states="confirmed,assigned,waiting,partially_available" groups="base.group_no_one"/>
</tree>
@@ -1016,7 +1016,7 @@
<button class="oe_link oe_right" name="put_in_pack" type="object" string="&#8627;Put in Pack" attrs="{'invisible': [('state', 'in', ('done', 'cancel'))]}" groups="stock.group_tracking_lot"/>
</page>
<page string="Initial Demand">
<field name="move_lines" attrs="{'invisible': [('state', 'not in', ('draft', 'confirmed', 'waiting'))]}" options="{'reload_on_button': True}" mode="tree,kanban" context="{'address_in_id': partner_id, 'form_view_ref':'stock.view_move_picking_form', 'tree_view_ref':'stock.view_move_picking_tree', 'default_picking_type_id': picking_type_id, 'default_location_id': location_id, 'default_location_dest_id': location_dest_id}">
<field name="move_lines" attrs="{'readonly': [('state', 'in', ('cancel', 'partially_available', 'assigned', 'done'))]}" options="{'reload_on_button': True}" mode="tree,kanban" context="{'address_in_id': partner_id, 'form_view_ref':'stock.view_move_picking_form', 'tree_view_ref':'stock.view_move_picking_tree', 'default_picking_type_id': picking_type_id, 'default_location_id': location_id, 'default_location_dest_id': location_dest_id}">
<kanban class="o_kanban_mobile">
<field name="product_id"/>
<field name="availability"/>
@@ -1044,7 +1044,6 @@
</templates>
</kanban>
</field>
<field name="move_lines_related" attrs="{'invisible': [('state', 'in', ('draft', 'confirmed', 'waiting', 'done'))]}" readonly="1" context="{'address_in_id': partner_id, 'form_view_ref':'stock.view_move_picking_form', 'tree_view_ref':'stock.view_move_picking_tree', 'default_picking_type_id': picking_type_id, 'default_location_id': location_id, 'default_location_dest_id': location_dest_id}"/>
<field name="pack_operation_exist" invisible="1"/>
<field name="note" placeholder="Add an internal note..." class="oe_inline"/>
</page>
@@ -2265,7 +2264,7 @@
<separator string="Contained Packages"/>
<field name="children_ids">
<tree string="Contained Packages">
<field name="complete_name"/>
<field name="display_name"/>
<field name="packaging_id"/>
<field name="location_id"/>
</tree>
@@ -2284,7 +2283,7 @@
<field eval="10" name="priority"/>
<field name="arch" type="xml">
<tree string="Package">
<field name="complete_name"/>
<field name="display_name"/>
<field name="packaging_id"/>
<field name="location_id"/>
<field name="company_id"/>
@@ -2,9 +2,9 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from openerp import models, fields, api, _
from openerp.tools import float_compare
from openerp.exceptions import UserError
class stock_immediate_transfer(models.TransientModel):
_name = 'stock.immediate.transfer'
_description = 'Immediate Transfer'
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<record id="view_immediate_transfer" model="ir.ui.view">
<field name="name">view_immediate_transfer</field>
<field name="name">stock.immediate.transfer.view.form</field>
<field name="model">stock.immediate.transfer</field>
<field name="arch" type="xml">
<form string="Immediate transfer?">
@@ -13,7 +13,7 @@
</group>
<footer>
<button name="process" string="_Apply" type="object" class="btn-primary"/>
<button string="_Cancel" class="btn-default" special="cancel" />
<button string="Cancel" class="btn-default" special="cancel" />
</footer>
</form>
</field>
+13 -11
View File
@@ -16,13 +16,14 @@ class stock_inventory(osv.osv):
'accounting_date': fields.date('Force Accounting Date', help="Choose the accounting date at which you want to value the stock moves created by the inventory instead of the default one (the inventory end date)"),
}
def post_inventory(self, cr, uid, inv, context=None):
def post_inventory(self, cr, uid, ids, context=None):
if context is None:
context = {}
ctx = context.copy()
inv = self.browse(cr, uid, ids, context=context)[0]
if inv.accounting_date:
ctx['force_period_date'] = inv.accounting_date
return super(stock_inventory, self).post_inventory(cr, uid, inv, context=ctx)
return super(stock_inventory, self).post_inventory(cr, uid, ids, context=ctx)
class account_invoice_line(osv.osv):
@@ -161,20 +162,21 @@ class stock_quant(osv.osv):
if quant.product_id.cost_method == 'real' and quant.location_id.usage != 'internal':
self.pool.get('stock.move')._store_average_cost_price(cr, uid, move, context=context)
def _account_entry_move(self, cr, uid, quants, move, context=None):
def _account_entry_move(self, cr, uid, ids, move, context=None):
"""
Accounting Valuation Entries
quants: browse record list of Quants to create accounting valuation entries for. Unempty and all quants are supposed to have the same location id (thay already moved in)
move: Move to use. browse record
"""
quants = self.browse(cr, uid, ids, context=context)
if context is None:
context = {}
location_obj = self.pool.get('stock.location')
location_from = move.location_id
location_to = quants[0].location_id
company_from = location_obj._location_owner(cr, uid, location_from, context=context)
company_to = location_obj._location_owner(cr, uid, location_to, context=context)
company_from = location_from and (location_from.usage == 'internal') and location_from.company_id or False
company_to = location_to and (location_to.usage == 'internal') and location_to.company_id or False
if move.product_id.valuation != 'real_time':
return False
@@ -214,10 +216,10 @@ class stock_quant(osv.osv):
else:
self._create_account_move_line(cr, uid, quants, move, acc_valuation, acc_dest, journal_id, context=ctx)
def _quant_create(self, cr, uid, qty, move, lot_id=False, owner_id=False, src_package_id=False, dest_package_id=False, force_location_from=False, force_location_to=False, context=None):
def _quant_create_from_move(self, cr, uid, qty, move, lot_id=False, owner_id=False, src_package_id=False, dest_package_id=False, force_location_from=False, force_location_to=False, context=None):
quant_obj = self.pool.get('stock.quant')
quant = super(stock_quant, self)._quant_create(cr, uid, qty, move, lot_id=lot_id, owner_id=owner_id, src_package_id=src_package_id, dest_package_id=dest_package_id, force_location_from=force_location_from, force_location_to=force_location_to, context=context)
self._account_entry_move(cr, uid, [quant], move, context)
quant = super(stock_quant, self)._quant_create_from_move(cr, uid, qty, move, lot_id=lot_id, owner_id=owner_id, src_package_id=src_package_id, dest_package_id=dest_package_id, force_location_from=force_location_from, force_location_to=force_location_to, context=context)
quant._account_entry_move(move)
if move.product_id.valuation == 'real_time':
# If the precision required for the variable quant cost is larger than the accounting
# precision, inconsistencies between the stock valuation and the accounting entries
@@ -237,14 +239,14 @@ class stock_quant(osv.osv):
and float_compare(quant.qty, 2.0, precision_rounding=quant.product_id.uom_id.rounding) >= 0:
qty = quant.qty
cost = quant.cost
quant_correct = quant_obj._quant_split(cr, uid, quant, quant.qty - 1.0, context=context)
quant_correct = quant._quant_split(quant.qty - 1.0)
cost_correct += (qty * cost) - (qty * cost_rounded)
quant_obj.write(cr, SUPERUSER_ID, [quant.id], {'cost': cost_rounded}, context=context)
quant_obj.write(cr, SUPERUSER_ID, [quant_correct.id], {'cost': cost_correct}, context=context)
return quant
def move_quants_write(self, cr, uid, quants, move, location_dest_id, dest_package_id, lot_id=False, entire_pack=False, context=None):
res = super(stock_quant, self).move_quants_write(cr, uid, quants, move, location_dest_id, dest_package_id, lot_id=lot_id, entire_pack=entire_pack, context=context)
def _quant_update_from_move(self, cr, uid, quants, move, location_dest_id, dest_package_id, lot_id=False, entire_pack=False, context=None):
res = super(stock_quant, self)._quant_update_from_move(cr, uid, quants, move, location_dest_id, dest_package_id, lot_id=lot_id, entire_pack=entire_pack, context=context)
self._account_entry_move(cr, uid, quants, move, context=context)
return res
+22 -129
View File
@@ -80,23 +80,6 @@ class procurement_order(osv.osv):
res.update({'date_planned': procurement.next_delivery_date})
return res
def _prepare_orderpoint_procurement(self, cr, uid, orderpoint, product_qty, date=False, purchase_date = False, group=False, context=None):
return {
'name': orderpoint.name,
'date_planned': date or self._get_orderpoint_date_planned(cr, uid, orderpoint, datetime.today(), context=context),
'next_delivery_date': date,
'next_purchase_date': purchase_date,
'product_id': orderpoint.product_id.id,
'product_qty': product_qty,
'company_id': orderpoint.company_id.id,
'product_uom': orderpoint.product_uom.id,
'location_id': orderpoint.location_id.id,
'origin': orderpoint.name,
'warehouse_id': orderpoint.warehouse_id.id,
'orderpoint_id': orderpoint.id,
'group_id': group or orderpoint.group_id.id,
}
def _get_previous_dates(self, cr, uid, orderpoint, start_date = False, context=None):
"""
Date should be given in utc
@@ -197,6 +180,7 @@ class procurement_order(osv.osv):
new_date = datetime.strptime(orderpoint.last_execution_date, DEFAULT_SERVER_DATETIME_FORMAT)
else:
new_date = datetime.utcnow()
# TDE note: I bet accessing interval[2] will crash, no ? this code seems very louche
intervals = calendar_obj._schedule_days(cr, uid, orderpoint.purchase_calendar_id.id, 1, new_date, compute_leaves=True, context=context)
for interval in intervals:
# If last execution date, interval should start after it in order not to execute the same orderpoint twice
@@ -209,119 +193,28 @@ class procurement_order(osv.osv):
return [(now_date, None)]
return res_intervals
def _procure_orderpoint_confirm(self, cr, uid, use_new_cursor=False, company_id=False, context=None):
'''
Create procurement based on Orderpoint
def _procurement_from_orderpoint_get_order(self, cr, uid, context=None):
return 'location_id, purchase_calendar_id, calendar_id'
:param bool use_new_cursor: if set, use a dedicated cursor and auto-commit after processing each procurement.
This is appropriate for batch jobs only.
'''
if context is None:
context = {}
if use_new_cursor:
cr = openerp.registry(cr.dbname).cursor()
orderpoint_obj = self.pool.get('stock.warehouse.orderpoint')
procurement_obj = self.pool.get('procurement.order')
product_obj = self.pool.get('product.product')
dom = company_id and [('company_id', '=', company_id)] or []
orderpoint_ids = orderpoint_obj.search(cr, uid, dom, order="location_id, purchase_calendar_id, calendar_id")
prev_ids = []
tot_procs = []
while orderpoint_ids:
ids = orderpoint_ids[:1000]
del orderpoint_ids[:1000]
dates_dict = {}
product_dict = {}
ops_dict = {}
ops = orderpoint_obj.browse(cr, uid, ids, context=context)
def _procurement_from_orderpoint_get_grouping_key(self, cr, uid, orderpoint_ids, context=None):
orderpoint = self.pool['stock.warehouse.orderpoint'].browse(cr, uid, orderpoint_ids[0], context=context)
return (orderpoint.location_id.id, orderpoint.purchase_calendar_id.id, orderpoint.calendar_id.id)
#Calculate groups that can be executed together
for op in ops:
key = (op.location_id.id, op.purchase_calendar_id.id, op.calendar_id.id)
res_groups=[]
if not dates_dict.get(key):
date_groups = self._get_group(cr, uid, op, context=context)
for date, group in date_groups:
if op.calendar_id and op.calendar_id.attendance_ids:
date1, date2 = self._get_next_dates(cr, uid, op, date, group, context=context)
res_groups += [(group, date1, date2, date)] #date1/date2 as deliveries and date as purchase confirmation date
else:
res_groups += [(group, date, False, date)]
dates_dict[key] = res_groups
product_dict[key] = [op.product_id]
ops_dict[key] = [op]
else:
product_dict[key] += [op.product_id]
ops_dict[key] += [op]
for key in product_dict.keys():
for res_group in dates_dict[key]:
ctx = context.copy()
ctx.update({'location': ops_dict[key][0].location_id.id})
if res_group[2]:
ctx.update({'to_date': res_group[2].strftime(DEFAULT_SERVER_DATETIME_FORMAT)})
prod_qty = product_obj._product_available(cr, uid, [x.id for x in product_dict[key]],
context=ctx)
group = res_group[0]
date = res_group[1]
subtract_qty = orderpoint_obj.subtract_procurements_from_orderpoints(cr, uid, [x.id for x in ops_dict[key]], context=context)
first_op = True
ndelivery = date
npurchase = res_group[3]
for op in ops_dict[key]:
try:
prods = prod_qty[op.product_id.id]['virtual_available']
if prods is None:
continue
if float_compare(prods, op.product_min_qty, precision_rounding=op.product_uom.rounding) < 0:
qty = max(op.product_min_qty, op.product_max_qty) - prods
reste = op.qty_multiple > 0 and qty % op.qty_multiple or 0.0
if float_compare(reste, 0.0, precision_rounding=op.product_uom.rounding) > 0:
qty += op.qty_multiple - reste
if float_compare(qty, 0.0, precision_rounding=op.product_uom.rounding) <= 0:
continue
qty -= subtract_qty[op.id]
qty_rounded = float_round(qty, precision_rounding=op.product_uom.rounding)
if qty_rounded > 0:
proc_id = procurement_obj.create(cr, uid,
self._prepare_orderpoint_procurement(cr, uid, op, qty_rounded, date=ndelivery, purchase_date=npurchase, group=group, context=context),
context=context)
tot_procs.append(proc_id)
orderpoint_obj.write(cr, uid, [op.id], {'last_execution_date': datetime.utcnow().strftime(DEFAULT_SERVER_DATETIME_FORMAT)}, context=context)
if use_new_cursor:
cr.commit()
except OperationalError:
if use_new_cursor:
orderpoint_ids.append(op.id)
cr.rollback()
continue
else:
raise
try:
tot_procs.reverse()
self.run(cr, uid, tot_procs, context=context)
tot_procs = []
if use_new_cursor:
cr.commit()
except OperationalError:
if use_new_cursor:
cr.rollback()
continue
else:
raise
if use_new_cursor:
cr.commit()
if prev_ids == ids:
break
def _procurement_from_orderpoint_get_groups(self, cr, uid, orderpoint_ids, context=None):
orderpoint = self.pool['stock.warehouse.orderpoint'].browse(cr, uid, orderpoint_ids[0], context=context)
res_groups = []
date_groups = self._get_group(cr, uid, orderpoint, context=context)
for date, group in date_groups:
if orderpoint.calendar_id and orderpoint.calendar_id.attendance_ids:
date1, date2 = self._get_next_dates(cr, uid, orderpoint, date, group, context=context)
res_groups += [{'to_date': date2, 'procurement_values': {'group': group, 'date': date1, 'purchase_date': date}}] # date1/date2 as deliveries and date as purchase confirmation date
else:
prev_ids = ids
res_groups += [{'to_date': False, 'procurement_values': {'group': group, 'date': date, 'purchase_date': date}}]
return res_groups
if use_new_cursor:
cr.commit()
cr.close()
return {}
def _procurement_from_orderpoint_post_process(self, cr, uid, orderpoint_ids, context=None):
self.pool['stock.warehouse.orderpoint'].write(
cr, uid, orderpoint_ids, {
'last_execution_date': datetime.utcnow().strftime(DEFAULT_SERVER_DATETIME_FORMAT)
}, context=context)
return super(procurement_order, self)._procurement_from_orderpoint_post_process(cr, uid, orderpoint_ids, context=context)
+7
View File
@@ -9,3 +9,10 @@ class stock_warehouse_orderpoint(osv.osv):
'purchase_calendar_id': fields.many2one('resource.calendar', 'Purchase Calendar'),
'last_execution_date': fields.datetime('Last Execution Date', readonly=True),
}
def _prepare_procurement_values(self, cr, uid, ids, product_qty, date=False, purchase_date=False, group=False, context=None):
res = super(stock_warehouse_orderpoint, self)._prepare_procurement_values(cr, uid, ids, product_qty, date=date, group=group, context=context)
res.update({
'next_delivery_date': date,
'next_purchase_date': purchase_date})
return res
@@ -232,7 +232,7 @@ class stock_landed_cost(osv.osv):
# Search for existing quant of quantity = 1.0 to avoid creating a new one
quant_correct = quants.filtered(lambda r: float_compare(r.qty, 1.0, precision_rounding=quants[0].product_id.uom_id.rounding) == 0)
if not quant_correct:
quant_correct = quant_obj._quant_split(cr, uid, quants[0], quants[0].qty - 1.0, context=context)
quant_correct = quants[0]._quant_split(quants[0].qty - 1.0)
else:
quant_correct = quant_correct[0]
quants = quants - quant_correct