}
_order = "sequence"
- def _bom_find(self, cr, uid, product_uom, product_tmpl_id=None, product_id=None, properties=None):
+ def _bom_find(self, cr, uid, product_uom, product_tmpl_id=None, product_id=None, properties=None, context=None):
""" Finds BoM for particular product and product uom.
@param product_tmpl_id: Selected product.
@param product_uom: Unit of measure of a product.
properties = []
if product_id:
if not product_tmpl_id:
- product_tmpl_id = self.pool['product.product'].browse(cr, uid, product_id).product_tmpl_id.id
+ product_tmpl_id = self.pool['product.product'].browse(cr, uid, product_id, context=context).product_tmpl_id.id
domain = [
'|',
('product_id', '=', product_id),
# neither product nor template, makes no sense to search
return False
if product_uom:
- domain += [('product_uom','=',product_uom)]
+ domain += [('product_uom','=',product_uom)]
domain = domain + [ '|', ('date_start', '=', False), ('date_start', '<=', time.strftime(DEFAULT_SERVER_DATETIME_FORMAT)),
'|', ('date_stop', '=', False), ('date_stop', '>=', time.strftime(DEFAULT_SERVER_DATETIME_FORMAT))]
# order to prioritize bom with product_id over the one without
ids = self.search(cr, uid, domain, order='product_id')
for bom in self.pool.get('mrp.bom').browse(cr, uid, ids):
- if not set(map(int,bom.property_ids or [])) - set(properties or []):
+ if not set(map(int, bom.property_ids or [])) - set(properties or []):
return bom.id
return False
- def _bom_explode(self, cr, uid, bom, product, factor, properties=None, level=0, routing_id=False, previous_products=None, master_bom=None):
+ def _bom_explode(self, cr, uid, bom, product, factor, properties=None, level=0, routing_id=False, previous_products=None, master_bom=None, context=None):
""" Finds Products and Work Centers for related BoM for manufacturing order.
@param bom: BoM of particular product template.
@param product: Select a particular variant of the BoM. If False use BoM without variants.
- @param factor: Factor of product UoM.
+ @param factor: Factor represents the quantity, but in UoM of the BoM, taking into account the numbers produced by the BoM
@param properties: A List of properties Ids.
@param level: Depth level to find BoM lines starts from 10.
@param previous_products: List of product previously use by bom explore to avoid recursion
if bom_line_id.date_start and bom_line_id.date_start > time.strftime(DEFAULT_SERVER_DATETIME_FORMAT) or \
bom_line_id.date_stop and bom_line_id.date_stop > time.strftime(DEFAULT_SERVER_DATETIME_FORMAT):
continue
- # check properties
- if set(map(int,bom_line_id.property_ids or [])) - set(properties or []):
- continue
# all bom_line_id variant values must be in the product
if bom_line_id.attribute_value_ids:
if not product or (set(map(int,bom_line_id.attribute_value_ids or [])) - set(map(int,product.attribute_value_ids))):
if bom_line_id.product_id.id in all_prod:
raise osv.except_osv(_('Invalid Action!'), _('BoM "%s" contains a BoM line with a product recursion: "%s".') % (master_bom.name,bom_line_id.product_id.name_get()[0][1]))
all_prod.append(bom_line_id.product_id.id)
-
- quantity = _factor(bom_line_id.product_qty * factor, bom_line_id.product_efficiency, bom_line_id.product_rounding)
- bom2 = None
- bom_id = self._bom_find(cr, uid, bom_line_id.product_uom.id, product_id=bom_line_id.product_id.id, properties=properties)
- if bom_id:
- bom2 = self.browse(cr, uid, bom_id)
- if bom_line_id.type != "phantom" and (not bom2 or bom2.type != "phantom"):
+ bom_id = self._bom_find(cr, uid, bom_line_id.product_uom.id, product_id=bom_line_id.product_id.id, properties=properties, context=context)
+
+ #If BoM should not behave like PhantoM, just add the product, otherwise explode further
+ if bom_line_id.type != "phantom" and (not bom_id or self.browse(cr, uid, bom_id, context=context).type != "phantom"):
result.append({
'name': bom_line_id.product_id.name,
'product_id': bom_line_id.product_id.id,
- 'product_qty': quantity,
+ 'product_qty': bom_line_id.product_qty * factor,
'product_uom': bom_line_id.product_uom.id,
'product_uos_qty': bom_line_id.product_uos and bom_line_id.product_uos_qty * factor or False,
'product_uos': bom_line_id.product_uos and bom_line_id.product_uos.id or False,
})
- elif bom2:
+ elif bom_id:
+ bom2 = self.browse(cr, uid, bom_id, context=context)
+ quantity = _factor(bom_line_id.product_qty * factor, bom_line_id.product_efficiency, bom_line_id.product_rounding)
res = self._bom_explode(cr, uid, bom2, bom_line_id.product_id, quantity,
- properties=properties, level=level + 10, previous_products=all_prod, master_bom=master_bom)
+ properties=properties, level=level + 10, previous_products=all_prod, master_bom=master_bom, context=context)
result = result + res[0]
result2 = result2 + res[1]
else:
}}
bom_obj = self.pool.get('mrp.bom')
product = self.pool.get('product.product').browse(cr, uid, product_id, context=context)
- bom_id = bom_obj._bom_find(cr, uid, product.uom_id and product.uom_id.id, product_id=product.id, properties=[])
+ bom_id = bom_obj._bom_find(cr, uid, product.uom_id and product.uom_id.id, product_id=product.id, properties=[], context=context)
routing_id = False
if bom_id:
bom_point = bom_obj.browse(cr, uid, bom_id, context=context)
bom_point = production.bom_id
bom_id = production.bom_id.id
if not bom_point:
- bom_id = bom_obj._bom_find(cr, uid, production.product_uom.id, product_id=production.product_id.id, properties=properties)
+ bom_id = bom_obj._bom_find(cr, uid, production.product_uom.id, product_id=production.product_id.id, properties=properties, context=context)
if bom_id:
bom_point = bom_obj.browse(cr, uid, bom_id)
routing_id = bom_point.routing_id.id or False
# get components and workcenter_lines from BoM structure
factor = uom_obj._compute_qty(cr, uid, production.product_uom.id, production.product_qty, bom_point.product_uom.id)
# product_lines, workcenter_lines
- results, results2 = bom_obj._bom_explode(cr, uid, bom_point, production.product_id, factor / bom_point.product_qty, properties, routing_id=production.routing_id.id)
+ results, results2 = bom_obj._bom_explode(cr, uid, bom_point, production.product_id, factor / bom_point.product_qty, properties, routing_id=production.routing_id.id, context=context)
# reset product_lines in production order
for line in results:
line['production_id'] = production.id
return "make_to_order"
return "make_to_stock"
-
-
-
def _create_previous_move(self, cr, uid, move_id, product, source_location_id, dest_location_id, context=None):
'''
When the routing gives a different location than the raw material location of the production order,
stock_move = self.pool.get('stock.move')
type_obj = self.pool.get('stock.picking.type')
# Need to search for a picking type
- 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 = 'internal'
- check_loc = dest_loc
- if src_loc.usage == 'internal' and dest_loc.usage != 'internal':
- code = 'outgoing'
- check_loc = src_loc
- if src_loc.usage != 'internal' and dest_loc.usage == 'internal':
- code = 'incoming'
+ move = stock_move.browse(cr, uid, move_id, context=context)
+ code = stock_move.get_code_from_locs(cr, uid, move, context=context)
+ if code == 'outgoing':
+ check_loc_id = source_location_id
+ else:
+ check_loc_id = dest_location_id
+ check_loc = loc_obj.browse(cr, uid, check_loc_id, context=context)
wh = loc_obj.get_warehouse(cr, uid, check_loc, context=context)
- domain = [('code','=', code)]
+ domain = [('code', '=', code)]
if wh:
domain += [('warehouse_id', '=', wh)]
types = type_obj.search(cr, uid, domain, context=context)
for procurement in self.browse(cr, uid, ids, context=context):
properties = [x.id for x in procurement.property_ids]
bom_id = self.pool.get('mrp.bom')._bom_find(cr, uid, procurement.product_uom.id,
- product_id=procurement.product_id.id, properties=properties)
+ product_id=procurement.product_id.id, properties=properties, context=context)
if not bom_id:
return False
return True
else:
properties = [x.id for x in procurement.property_ids]
bom_id = bom_obj._bom_find(cr, uid, procurement.product_uom.id,
- product_id=procurement.product_id.id, properties=properties)
+ product_id=procurement.product_id.id, properties=properties, context=context)
bom = bom_obj.browse(cr, uid, bom_id, context=context)
routing_id = bom.routing_id.id
for product in product_pool.browse(cr, uid, ids, context=context):
product_uom_name = to_xml(product.uom_id.name)
- bom_id = bom_pool._bom_find(cr, uid, product.uom_id.id, product_id=product.id)
+ bom_id = bom_pool._bom_find(cr, uid, product.uom_id.id, product_id=product.id, context=context)
title = "<title>%s</title>" %(_("Cost Structure"))
title += "<title>%s</title>" % (to_xml(product.name))
xml += "<lines style='header'>" + title + prod_header + "</lines>"
else:
bom = bom_pool.browse(cr, uid, bom_id, context=context)
factor = number * product.uom_id.factor / bom.product_uom.factor
- sub_boms = bom_pool._bom_explode(cr, uid, bom, product, factor / bom.product_qty)
+ sub_boms = bom_pool._bom_explode(cr, uid, bom, product, factor / bom.product_qty, context=context)
total = 0
total_strd = 0
parent_bom = {
if bis:
factor = move.product_qty
bom_point = bom_obj.browse(cr, SUPERUSER_ID, bis[0], context=context)
- res = bom_obj._bom_explode(cr, SUPERUSER_ID, bom_point, move.product_id, factor, [])
+ res = bom_obj._bom_explode(cr, SUPERUSER_ID, bom_point, move.product_id, factor, [], context=context)
state = 'confirmed'
if move.state == 'assigned':
bom_point = prod.bom_id
bom_id = prod.bom_id.id
if not bom_point:
- bom_id = bom_obj._bom_find(cr, uid, prod.product_uom.id, product_id=prod.product_id.id)
+ bom_id = bom_obj._bom_find(cr, uid, prod.product_uom.id, product_id=prod.product_id.id, context=context)
if not bom_id:
raise osv.except_osv(_('Error!'), _("Cannot find bill of material for this product."))
prod_obj.write(cr, uid, [prod.id], {'bom_id': bom_id})
factor = prod.product_qty * prod.product_uom.factor / bom_point.product_uom.factor
product_details, workcenter_details = \
- bom_obj._bom_explode(cr, uid, bom_point, prod.product_id, factor / bom_point.product_qty, [])
+ bom_obj._bom_explode(cr, uid, bom_point, prod.product_id, factor / bom_point.product_qty, [], context=context)
for r in product_details:
if r['product_id'] == move.product_id.id:
move_obj.write(cr, uid, [move.id], {'product_uom_qty': r['product_qty']})
'delay': 1,
'company_id': lambda self,cr,uid,c: self.pool.get('res.company')._company_default_get(cr, uid, 'product.supplierinfo', context=c),
}
- def price_get(self, cr, uid, supplier_ids, product_id, product_qty=1, context=None):
- """
- Calculate price from supplier pricelist.
- @param supplier_ids: Ids of res.partner object.
- @param product_id: Id of product.
- @param product_qty: specify quantity to purchase.
- """
- if type(supplier_ids) in (int,long,):
- supplier_ids = [supplier_ids]
- res = {}
- product_pool = self.pool.get('product.product')
- partner_pool = self.pool.get('res.partner')
- pricelist_pool = self.pool.get('product.pricelist')
- currency_pool = self.pool.get('res.currency')
- currency_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.currency_id.id
- # Compute price from standard price of product
- product_price = product_pool.price_get(cr, uid, [product_id], 'standard_price', context=context)[product_id]
- product = product_pool.browse(cr, uid, product_id, context=context)
- for supplier in partner_pool.browse(cr, uid, supplier_ids, context=context):
- price = product_price
- # Compute price from Purchase pricelist of supplier
- #FIXME: property_product_pricelist_purchase is defined in purchase
- pricelist_id = supplier.property_product_pricelist_purchase.id
- if pricelist_id:
- price = pricelist_pool.price_get(cr, uid, [pricelist_id], product_id, product_qty, context=context).setdefault(pricelist_id, 0)
- price = currency_pool.compute(cr, uid, pricelist_pool.browse(cr, uid, pricelist_id).currency_id.id, currency_id, price)
-
- # Compute price from supplier pricelist which are in Supplier Information
- supplier_info_ids = self.search(cr, uid, [('name','=',supplier.id),('product_tmpl_id','=',product.product_tmpl_id.id)])
- if supplier_info_ids:
- cr.execute('SELECT * ' \
- 'FROM pricelist_partnerinfo ' \
- 'WHERE suppinfo_id IN %s' \
- 'AND min_quantity <= %s ' \
- 'ORDER BY min_quantity DESC LIMIT 1', (tuple(supplier_info_ids),product_qty,))
- res2 = cr.dictfetchone()
- if res2:
- price = res2['price']
- res[supplier.id] = price
- return res
+
_order = 'sequence'
return False
def wkf_action_cancel(self, cr, uid, ids, context=None):
- self.write(cr, uid, ids, {'state': 'cancel'})
+ self.write(cr, uid, ids, {'state': 'cancel'}, context=context)
self.set_order_line_status(cr, uid, ids, 'cancel', context=context)
-
def action_cancel(self, cr, uid, ids, context=None):
for purchase in self.browse(cr, uid, ids, context=context):
for pick in purchase.picking_ids:
return purchase_order.partner_id, purchase_order.create_uid.id, purchase_order.currency_id.id
else:
partner = move.picking_id and move.picking_id.partner_id or False
- if partner:
- if partner.property_product_pricelist_purchase and move.location_id.usage != 'internal' and move.location_dest_id.usage == 'internal':
- currency = partner.property_product_pricelist_purchase.currency_id.id
- return partner, uid, currency
+ code = self.get_code_from_locs(cr, uid, move, context=context)
+ if partner and partner.property_product_pricelist_purchase and code == 'incoming':
+ currency = partner.property_product_pricelist_purchase.currency_id.id
+ return partner, uid, currency
return super(stock_move, self)._get_master_data(cr, uid, move, company, context=context)
"""
Attribute price to move, important in inter-company moves or receipts with only one partner
"""
- if not move.purchase_line_id and move.location_id.usage != 'internal' and move.location_dest_id.usage == 'internal' and not move.price_unit:
+ code = self.get_code_from_locs(cr, uid, move, context=context)
+ if not move.purchase_line_id and code == 'incoming' and not move.price_unit:
partner = move.picking_id and move.picking_id.partner_id or False
price = False
# If partner given, search price in its purchase pricelist
'date': move.date,
})[pricelist]
if price:
- self.write(cr, uid, [move.id], {'price_unit': price}, context=context)
- return True
+ return self.write(cr, uid, [move.id], {'price_unit': price}, context=context)
super(stock_move, self).attribute_price(cr, uid, move, context=context)
-
class stock_picking(osv.osv):
_inherit = 'stock.picking'
'route_id': buy_route_id,
'action': 'buy',
'picking_type_id': warehouse.in_type_id.id,
- 'propagate': False,
'warehouse_id': warehouse.id,
}
product_uom_obj = self.pool.get('product.uom')
product_obj = self.pool.get('product.product')
warning = {}
+ #UoM False due to hack which makes sure uom changes price, ... in product_id_change
res = self.product_id_change(cr, uid, ids, pricelist, product, qty=qty,
uom=False, qty_uos=qty_uos, uos=uos, name=name, partner_id=partner_id,
lang=lang, update_tax=update_tax, date_order=date_order, packaging=packaging, fiscal_position=fiscal_position, flag=flag, context=context)
<separator/>
<filter name="real_stock_available" string="Available Products" domain="[('qty_available','>',0)]"/>
<filter name="virtual_stock_available" string="Forecast Available Products" domain="[('virtual_available','>',0)]"/>
- <filter name="real_stock_negative" string="Exhausted Stock" domain="[('qty_available','<=',0)]"/>
- <filter name="virtual_stock_negative" string="Forecast Exhausted Stock" domain="[('virtual_available','<=',0)]"/>
+ <filter name="real_stock_exhausted" string="Exhausted Stock" domain="[('qty_available','<=',0)]"/>
+ <filter name="virtual_stock_exhausted" string="Forecast Exhausted Stock" domain="[('virtual_available','<=',0)]"/>
+ <filter name="real_stock_negative" string="Negative Stock" domain="[('qty_available','<',0)]"/>
+ <filter name="virtual_stock_negative" string="Forecast Negative Stock" domain="[('virtual_available','<',0)]"/>
</field>
<filter name="consumable" position="before">
<filter string="Products" icon="terp-accessories-archiver" domain="[('type','=','product')]" help="Stockable products"/>
</xpath>
</field>
</record>
-
-
- <act_window
- context="{'location': active_id}"
- domain="[('type','<>','service')]"
- id="act_product_location_open"
- name="Products"
- res_model="product.product"
- src_model="stock.location"/>
-
- <record id="ir_act_product_location_open" model="ir.values">
- <field name="key2">tree_but_open</field>
- <field name="model">stock.location</field>
- <field name="name">Products</field>
- <field eval="'ir.actions.act_window,%d'%act_product_location_open" name="value"/>
- </record>
</data>
</openerp>
price = move.product_id.standard_price
self.write(cr, uid, [move.id], {'price_unit': price})
-
def action_confirm(self, cr, uid, ids, context=None):
""" Confirms stock move or put it in waiting if it's linked to another move.
@return: List of ids.
return self.action_confirm(cr, uid, [new_move], context=context)[0]
+ def get_code_from_locs(self, cr, uid, move, context=None):
+ """
+ Returns the code the picking type should have. This can easily be used
+ to check if a move is internal or not
+ """
+ code = 'internal'
+ src_loc = move.location_id
+ dest_loc = 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
+
+
class stock_inventory(osv.osv):
_name = "stock.inventory"
_description = "Inventory"
'push_rule_id': rule.id,
'warehouse_id': rule.warehouse_id and rule.warehouse_id.id or False,
}
-
def _apply(self, cr, uid, rule, move, context=None):
move_obj = self.pool.get('stock.move')
<field name="res_model">stock.quant</field>
</record>
+ <act_window
+ context="{'location': active_id, 'search_default_real_stock_available': 1, 'search_default_virtual_stock_available': 1,
+ 'search_default_virtual_stock_negative': 1, 'search_default_real_stock_negative': 1}"
+ id="act_product_location_open"
+ name="Products"
+ res_model="product.product"
+ src_model="stock.location"/>
+
<record id="view_location_form" model="ir.ui.view">
<field name="name">stock.location.form</field>
<field name="model">stock.location</field>
class="oe_stat_button"
icon="fa-building-o" name="%(location_open_quants)d" type="action"
context="{'search_default_internal_loc': 1}"/>
+ <button string="Products"
+ class="oe_stat_button"
+ icon="fa-filter" name="%(act_product_location_open)d" type="action"
+ context="{'location_id': active_id}"
+ />
</div>
<label for="name" class="oe_edit_only"/>
<h1><field name="name"/></h1>
currency = company.currency_id.id
partner = move.picking_id and move.picking_id.partner_id
if partner:
- if partner.property_product_pricelist and move.location_id.usage == 'internal' and move.location_dest_id.usage != 'internal':
+ code = self.get_code_from_locs(cr, uid, move, context=context)
+ if partner.property_product_pricelist and code == 'outgoing':
currency = partner.property_product_pricelist.currency_id.id
return partner, uid, currency
if context is None:
context = {}
if type in ('in_invoice', 'in_refund'):
- if move_line.price_unit:
- return move_line.price_unit
- else:
- # Take the company of the move line
- product = move_line.product_id.with_context(company_id=move_line.company_id.id)
- amount_unit = product.price_get('standard_price')[move_line.product_id.id]
- return amount_unit
+ return move_line.price_unit
else:
# If partner given, search price in its sale pricelist
if move_line.partner_id and move_line.partner_id.property_product_pricelist:
res.append(move.picking_id.id)
return res
-
+ def _set_inv_state(self, cr, uid, picking_id, name, value, arg, context=None):
+ pick = self.browse(cr, uid, picking_id, context=context)
+ moves = [x.id for x in pick.move_lines]
+ move_obj= self.pool.get("stock.move")
+ move_obj.write(cr, uid, moves, {'invoice_state': pick.invoice_state})
_columns = {
'invoice_state': fields.function(__get_invoice_state, type='selection', selection=[
("2binvoiced", "To Be Invoiced"),
("none", "Not Applicable")
], string="Invoice Control", required=True,
+ fnct_inv = _set_inv_state,
store={
'stock.picking': (lambda self, cr, uid, ids, c={}: ids, ['state'], 10),
'stock.move': (__get_picking_move, ['picking_id', 'invoice_state'], 10),