[MRG] merge with lp:~openerp-dev/openobject-addons/trunk-wmsimplement-seduce-fp

bzr revid: tpa@tinyerp.com-20130904125022-xziws8dxenywro48
bzr revid: tpa@tinyerp.com-20130905092701-kul8m0h1jg3pnjdf
This commit is contained in:
Turkesh Patel (Open ERP) 2013-09-05 14:57:01 +05:30
commit a1af9d9479
52 changed files with 1206 additions and 454 deletions

View File

@ -44,11 +44,6 @@ class mrp_config_settings(osv.osv_memory):
Without this module: A + B + C -> D.
With this module: A + B + C -> D + E.
This installs the module mrp_byproduct."""),
'module_mrp_jit': fields.boolean("Generate procurement in real time",
help="""This allows Just In Time computation of procurement orders.
All procurement orders will be processed immediately, which could in some
cases entail a small performance impact.
This installs the module mrp_jit."""),
'module_stock_no_autopicking': fields.boolean("Manage manual picking to fulfill manufacturing orders ",
help="""This module allows an intermediate picking process to provide raw materials to production orders.
For example to manage production made by your suppliers (sub-contracting).

View File

@ -44,10 +44,6 @@
<group >
<label for="id" string="Planning"/>
<div>
<div>
<field name="module_mrp_jit" class="oe_inline"/>
<label for="module_mrp_jit"/>
</div>
<div>
<field name="group_mrp_routings" class="oe_inline"/>
<label for="group_mrp_routings"/>

View File

@ -44,6 +44,6 @@ In that case, you can not use priorities any more on the different picking.
'demo': [],
'test': ['test/mrp_jit.yml'],
'installable': True,
'auto_install': False,
'auto_install': True,
}
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -27,7 +27,6 @@ class procurement_order(osv.osv):
def create(self, cr, uid, vals, context=None):
procurement_id = super(procurement_order, self).create(cr, uid, vals, context=context)
# TODO: maybe this is not necessary anymore as we do this already
self.run(cr, uid, [procurement_id], context=context)
self.check(cr, uid, [procurement_id], context=context)
return procurement_id

View File

@ -642,6 +642,7 @@ class pos_order(osv.osv):
'invoice_id': fields.many2one('account.invoice', 'Invoice'),
'account_move': fields.many2one('account.move', 'Journal Entry', readonly=True),
'picking_id': fields.many2one('stock.picking', 'Picking', readonly=True),
'picking_type_id': fields.many2one('stock.picking.type', 'Picking Type', required=True),
'note': fields.text('Internal Notes'),
'nb_print': fields.integer('Number of Print', readonly=True),
'pos_reference': fields.char('Receipt Ref', size=64, readonly=True),
@ -660,6 +661,13 @@ class pos_order(osv.osv):
return session_record.config_id.pricelist_id and session_record.config_id.pricelist_id.id or False
return False
def _get_out_picking_type(self, cr, uid, context=None):
try:
picking_type = self.pool.get('ir.model.data').get_object(cr, uid, 'stock', 'picking_type_out', context=context)
except:
picking_type = False
return picking_type
_defaults = {
'user_id': lambda self, cr, uid, context: uid,
'state': 'draft',
@ -669,6 +677,7 @@ class pos_order(osv.osv):
'session_id': _default_session,
'company_id': lambda self,cr,uid,c: self.pool.get('res.users').browse(cr, uid, uid, c).company_id.id,
'pricelist_id': _default_pricelist,
'picking_type_id': _get_out_picking_type,
}
def create(self, cr, uid, values, context=None):
@ -697,18 +706,21 @@ class pos_order(osv.osv):
if not order.state=='draft':
continue
addr = order.partner_id and partner_obj.address_get(cr, uid, [order.partner_id.id], ['delivery']) or {}
picking_type = order.picking_type_id
picking_id = picking_obj.create(cr, uid, {
'origin': order.name,
'partner_id': addr.get('delivery',False),
'picking_type_id': self.pool.get('ir.model.data').get_object(cr, uid, 'stock', 'picking_type_out', context=context).id,
'picking_type_id': picking_type.id,
'company_id': order.company_id.id,
'move_type': 'direct',
'note': order.note or "",
'invoice_state': 'none',
}, context=context)
self.write(cr, uid, [order.id], {'picking_id': picking_id}, context=context)
location_id = order.warehouse_id.lot_stock_id.id
output_id = order.warehouse_id.lot_output_id.id
location_id = picking_type.default_location_src_id.id
output_id = picking_type.default_location_dest_id.id
if not location_id or not output_id:
raise osv.except_osv(_('Error!'), _('Missing source or destination location for picking type %s. Please configure those fields and try again.' % (picking_type.name,)))
for line in order.lines:
if line.product_id and line.product_id.type == 'service':

View File

@ -67,13 +67,13 @@ Dashboard / Reports for Purchase Management will include:
'res_config_view.xml',
],
'test': [
'test/process/run_scheduler.yml',
'test/fifo_price.yml',
'test/fifo_returns.yml',
#'test/costmethodchange.yml',
'test/process/cancel_order.yml',
'test/process/rfq2order2done.yml',
'test/process/generate_invoice_from_reception.yml',
'test/process/run_scheduler.yml',
'test/process/merge_order.yml',
'test/process/edi_purchase_order.yml',
'test/process/invoice_on_poline.yml',

View File

@ -26,7 +26,6 @@
<field name="product_uom" invisible="1"/>
<field name="day" invisible="1"/>
<field name="name" invisible="1"/>
<field name="warehouse_id" invisible="1"/>
<field name="validator" invisible="1"/>
<field name="company_id" invisible="1"/>
<field name="state" invisible="1"/>

View File

@ -188,7 +188,6 @@ class purchase_order(osv.osv):
help="Put an address if you want to deliver directly from the supplier to the customer. " \
"Otherwise, keep empty to deliver to your own company."
),
'warehouse_id': fields.many2one('stock.warehouse', 'Destination Warehouse'),
'location_id': fields.many2one('stock.location', 'Destination', required=True, domain=[('usage','<>','view')], states={'confirmed':[('readonly',True)], 'approved':[('readonly',True)],'done':[('readonly',True)]} ),
'pricelist_id':fields.many2one('product.pricelist', 'Pricelist', required=True, states={'confirmed':[('readonly',True)], 'approved':[('readonly',True)],'done':[('readonly',True)]}, help="The pricelist sets the currency used for this purchase order. It also computes the supplier price for the selected products/quantities."),
'currency_id': fields.many2one('res.currency','Currency', readonly=True, required=True,states={'draft': [('readonly', False)],'sent': [('readonly', False)]}),
@ -234,7 +233,9 @@ class purchase_order(osv.osv):
'journal_id': fields.many2one('account.journal', 'Journal'),
'bid_date': fields.date('Bid Received On', readonly=True, help="Date on which the bid was received"),
'bid_validity': fields.date('Bid Valid Until', help="Date on which the bid expired"),
'picking_type_id': fields.many2one('stock.picking.type', 'Picking Type', help="This will determine picking type of incoming shipment"),
'picking_type_id': fields.many2one('stock.picking.type', 'Picking Type', help="This will determine picking type of incoming shipment", required=True,
states={'confirmed':[('readonly',True)], 'approved':[('readonly',True)],'done':[('readonly',True)]}),
'related_location_id':fields.related('picking_type_id', 'default_location_dest_id', type='many2one', relation='stock.location', string="Related location", store=True),
}
_defaults = {
'date_order': fields.date.context_today,
@ -301,18 +302,22 @@ class purchase_order(osv.osv):
if not address_id:
return {}
address = self.pool.get('res.partner')
values = {'warehouse_id': False}
values = {}
supplier = address.browse(cr, uid, address_id)
if supplier:
location_id = supplier.property_stock_customer.id
values.update({'location_id': location_id})
return {'value':values}
def onchange_warehouse_id(self, cr, uid, ids, warehouse_id):
if not warehouse_id:
return {}
warehouse = self.pool.get('stock.warehouse').browse(cr, uid, warehouse_id)
return {'value':{'location_id': warehouse.lot_input_id.id, 'dest_address_id': False}}
def onchange_picking_type_id(self, cr, uid, ids, picking_type_id, context=None):
value = {}
if picking_type_id:
picktype = self.pool.get("stock.picking.type").browse(cr, uid, picking_type_id, context=context)
if picktype.default_location_dest_id:
value.update({'location_id': picktype.default_location_dest_id.id})
value.update({'related_location_id': picktype.default_location_dest_id and picktype.default_location_dest_id.id or False})
return {'value': value}
def onchange_partner_id(self, cr, uid, ids, partner_id):
partner = self.pool.get('res.partner')
@ -396,7 +401,9 @@ class purchase_order(osv.osv):
action_model, action_id = tuple(mod_obj.get_object_reference(cr, uid, 'stock', 'action_picking_tree'))
action = self.pool[action_model].read(cr, uid, action_id, context=context)
active_id = context.get('active_id',ids[0])
ctx = eval(action['context'],{'active_id': active_id}, nocopy=True)
picking_type_id = self.browse(cr, uid, active_id, context=context)['picking_type_id'].id
ctx = eval(action['context'],{'active_id': picking_type_id}, nocopy=True)
ctx.update({
'search_default_purchase_id': ids[0]
})
@ -738,30 +745,26 @@ class purchase_order(osv.osv):
return [picking_id]
def test_moves_done(self, cr, uid, ids, context=None):
done = True
'''PO is done at the delivery side if all the incoming shipments are done'''
for purchase in self.browse(cr, uid, ids, context=context):
for line in purchase.order_line:
for move in line.move_ids:
if move.state != 'done':
done = False
return done
for picking in purchase.picking_ids:
if picking.state != 'done':
return False
return True
def test_moves_except(self, cr, uid, ids, context=None):
''' PO is in exception at the delivery side if one of the picking is canceled
and the other pickings are completed (done or canceled)
'''
If one of the pickings is cancel and the other pickings are done: except
'''
cancel = False
at_least_one_canceled = False
alldoneorcancel = True
for purchase in self.browse(cr, uid, ids, context=context):
for line in purchase.order_line:
for move in line.move_ids:
if move.state == 'cancel':
cancel = True
if move.state not in ['done', 'cancel']:
alldoneorcancel = False
return cancel and alldoneorcancel
for picking in purchase.picking_ids:
if picking.state == 'cancel':
at_least_one_canceled = True
if picking.state not in ['done', 'cancel']:
alldoneorcancel = False
return at_least_one_canceled and alldoneorcancel
def move_lines_get(self, cr, uid, ids, *args):
res = []
@ -770,7 +773,6 @@ class purchase_order(osv.osv):
res += [x.id for x in line.move_ids]
return res
def action_picking_create(self, cr, uid, ids, context=None):
picking_ids = []
for order in self.browse(cr, uid, ids):
@ -856,7 +858,7 @@ class purchase_order(osv.osv):
'date_order': porder.date_order,
'partner_id': porder.partner_id.id,
'dest_address_id': porder.dest_address_id.id,
'warehouse_id': porder.warehouse_id.id,
'picking_type_id': porder.picking_type_id.id,
'location_id': porder.location_id.id,
'pricelist_id': porder.pricelist_id.id,
'state': 'draft',
@ -1242,7 +1244,6 @@ class procurement_order(osv.osv):
partner_id = partner.id
address_id = partner_obj.address_get(cr, uid, [partner_id], ['delivery'])['delivery']
pricelist_id = partner.property_product_pricelist_purchase.id
warehouse_id = warehouse_obj.search(cr, uid, [('company_id', '=', procurement.company_id.id or company.id)], context=context)
uom_id = procurement.product_id.uom_po_id.id
qty = uom_obj._compute_qty(cr, uid, procurement.product_uom.id, procurement.product_qty, uom_id)
if seller_qty:
@ -1278,7 +1279,7 @@ class procurement_order(osv.osv):
'origin': procurement.origin,
'partner_id': partner_id,
'location_id': procurement.location_id.id,
'warehouse_id': warehouse_id and warehouse_id[0] or False,
'picking_type_id': procurement.rule_id.picking_type_id.id,
'pricelist_id': pricelist_id,
'date_order': purchase_date.strftime(DEFAULT_SERVER_DATETIME_FORMAT),
'company_id': procurement.company_id.id,

View File

@ -44,7 +44,7 @@
</record>
<function
eval="('default',False,'warehouse_id', [('purchase.order', False)], ref('stock.warehouse0'), True, False, False, False, True)"
eval="('default',False,'picking_type_id', [('purchase.order', False)], ref('stock.picking_type_in'), True, False, False, False, True)"
id="purchase_default_set"
model="ir.values"
name="set"/>
@ -66,5 +66,16 @@
<field name="res_model">purchase.order</field>
</record>
<!--
Procurement rules
-->
<record id="procurement_rule_supply_stock" model="procurement.rule">
<field name="name">Buy to refill stock</field>
<field name="action">buy</field>
<field name="location_id" ref="stock.stock_location_stock"/>
<field name="picking_type_id" ref="stock.picking_type_in"/>
</record>
</data>
</openerp>

View File

@ -170,7 +170,7 @@
<button name="invoice_ok" states="except_invoice" string="Manually Corrected"/>
<button name="purchase_approve" states="confirmed" string="Approve Order" class="oe_highlight" groups="purchase.group_purchase_manager"/>
<button name="wkf_send_rfq" states="approved" string="Send PO by Email" type="object" context="{'send_rfq':False}"/>
<button name="view_picking" string="Receive Products" type="object" attrs="{'invisible': ['|', ('shipped','=',True), ('state','!=', 'approved')]}" class="oe_highlight"/>
<button name="view_picking" string="Receptions" type="object" attrs="{'invisible': ['|', ('shipped','=',True), ('state','!=', 'approved')]}" class="oe_highlight"/>
<button name="view_invoice" string="Receive Invoice" type="object" attrs="{'invisible': ['|', ('invoice_method','=','picking'), '|', ('state','!=', 'approved'), ('invoiced','=',True) ]}" class="oe_highlight"/>
<button name="action_cancel_draft" states="cancel,sent,confirmed" string="Set to Draft" type="object" />
<button name="purchase_cancel" states="draft,confirmed,sent,bid" string="Cancel"/>
@ -196,9 +196,13 @@
<group>
<field name="date_order"/>
<field name="origin" attr="{'invisible': [('origin','=',False)]}"/>
<field name="warehouse_id" on_change="onchange_warehouse_id(warehouse_id)" widget="selection" groups="stock.group_locations"/>
<field name="company_id" groups="base.group_multi_company" widget="selection"/>
<field name="picking_type_id"/>
<field name="picking_type_id" on_change="onchange_picking_type_id(picking_type_id, context)" domain="[('code_id','=','incoming')]" string="Warehouse / Operation" widget="selection"/>
<field name="related_location_id" invisible="1"/>
<field name="dest_address_id" string="Customer Address" on_change="onchange_dest_address_id(dest_address_id)"
attrs="{'invisible':['|', ('picking_type_id','=',False), ('related_location_id','!=', False)],
'required': [('picking_type_id','!=',False), ('related_location_id','=', False)]}"
groups="stock.group_locations"/>
</group>
</group>
<notebook>
@ -241,7 +245,7 @@
<page string="Deliveries &amp; Invoices">
<group>
<group>
<field name="dest_address_id" string="Customer Address" on_change="onchange_dest_address_id(dest_address_id)" groups="stock.group_locations"/>
<field name="minimum_planned_date"/>
<field name="location_id" groups="stock.group_locations"/>
<field name="shipped" groups="base.group_no_one"/>

View File

@ -186,13 +186,13 @@
<field name="act_to" ref="act_cancel"/>
<field name="signal">purchase_cancel</field>
</record>
<!--<record id="trans_picking_except_picking" model="workflow.transition">
<record id="trans_picking_except_picking" model="workflow.transition">
<field name="act_from" ref="act_picking"/>
<field name="act_to" ref="act_except_picking"/>
<field name="trigger_model">stock.move</field>
<field name="trigger_expr_id">move_lines_get()</field>
<field name="condition">test_moves_except()</field>
</record>-->
</record>
<record id="trans_invoice_except_invoice" model="workflow.transition">
<field name="act_from" ref="act_invoice"/>
<field name="act_to" ref="act_except_invoice"/>

View File

@ -144,8 +144,8 @@
<tr>
<td>
<para style="terp_default_Bold_9"><b>Shipping address :</b></para>
<para style="terp_default_9">[[ (o.dest_address_id and o.dest_address_id.name) or (o.warehouse_id and o.warehouse_id.name) or '']]</para>
<para style="terp_default_9">[[ (o.dest_address_id and display_address(o.dest_address_id)) or (o.warehouse_id and display_address(o.warehouse_id.partner_id)) or '']]</para>
<para style="terp_default_9">[[ (o.dest_address_id and o.dest_address_id.name) or (o.picking_type_id.warehouse_id and o.picking_type_id.warehouse_id.name) or '']]</para>
<para style="terp_default_9">[[ (o.dest_address_id and display_address(o.dest_address_id)) or (o.picking_type_id.warehouse_id and display_address(o.picking_type_id.warehouse_id.partner_id)) or '']]</para>
</td>
</tr>
</blockTable>

View File

@ -42,7 +42,7 @@ class purchase_report(osv.osv):
('done', 'Done'),
('cancel', 'Cancelled')],'Order Status', readonly=True),
'product_id':fields.many2one('product.product', 'Product', readonly=True),
'warehouse_id': fields.many2one('stock.warehouse', 'Warehouse', readonly=True),
'picking_type_id': fields.many2one('stock.warehouse', 'Warehouse', readonly=True),
'location_id': fields.many2one('stock.location', 'Destination', readonly=True),
'partner_id':fields.many2one('res.partner', 'Supplier', readonly=True),
'pricelist_id':fields.many2one('product.pricelist', 'Pricelist', readonly=True),
@ -82,7 +82,7 @@ class purchase_report(osv.osv):
s.dest_address_id,
s.pricelist_id,
s.validator,
s.warehouse_id as warehouse_id,
s.picking_type_id as picking_type_id,
s.partner_id as partner_id,
s.create_uid as user_id,
s.company_id as company_id,
@ -128,7 +128,7 @@ class purchase_report(osv.osv):
to_char(s.date_order, 'MM'),
to_char(s.date_order, 'YYYY-MM-DD'),
s.state,
s.warehouse_id,
s.picking_type_id,
u.uom_type,
u.category_id,
t.uom_id,

View File

@ -28,7 +28,7 @@
<field name="day" invisible="1"/>
<field name="name" invisible="1"/>
<field name="month" invisible="1"/>
<field name="warehouse_id" invisible="1"/>
<field name="picking_type_id" invisible="1"/>
<field name="validator" invisible="1"/>
<field name="company_id" invisible="1"/>
<field name="state" invisible="1"/>
@ -58,7 +58,7 @@
<field name="user_id"/>
<field name="validator"/>
<field name="location_id"/>
<field name="warehouse_id"/>
<field name="picking_type_id"/>
<field name="company_id" groups="base.group_multi_company"/>
<field name="date"/>
<field name="date_approve"/>
@ -72,9 +72,9 @@
<filter string="Product" name="group_product_id" icon="terp-accessories-archiver" context="{'group_by':'product_id'}"/>
<filter string="Category" name="group_category_id" icon="terp-stock_symbol-selection" context="{'group_by':'category_id'}"/>
<filter string="Reference Unit of Measure" name="group_product_uom" icon="terp-mrp" context="{'group_by':'product_uom'}"/>
<filter string="Warehouse" icon="terp-go-home" context="{'group_by':'warehouse_id'}"/>
<filter string="Warehouse" icon="terp-go-home" context="{'group_by':'picking_type_id'}"/>
<filter string="Reference UOM" name="group_product_uom" icon="terp-mrp" context="{'group_by':'product_uom'}"/>
<filter string="Warehouse" icon="terp-go-home" context="{'group_by':'warehouse_id'}"/>
<filter string="Warehouse" icon="terp-go-home" context="{'group_by':'picking_type_id'}"/>
<filter string="Destination" icon="terp-gtk-jump-to-ltr" context="{'group_by':'location_id'}"/>
<filter string="Status" icon="terp-stock_effects-object-colorize" context="{'group_by':'state'}"/>
<filter string="Company" icon="terp-go-home" context="{'group_by':'company_id'}" groups="base.group_multi_company"/>

View File

@ -62,7 +62,7 @@
<tr>
<td>
<para style="terp_default_Bold_9"><b>Expected Delivery address:</b></para>
<para style="terp_default_9">[[ (order.dest_address_id and order.dest_address_id.name) or (order.warehouse_id and order.warehouse_id.name) or '']]</para>
<para style="terp_default_9">[[ (order.dest_address_id and order.dest_address_id.name) or (order.picking_type_id.warehouse_id and order.picking_type_id.warehouse_id.name) or '']]</para>
<para style="P1">[[ order.dest_address_id and display_address(order.dest_address_id) ]]</para>
</td>
</tr>

View File

@ -29,8 +29,10 @@ class stock_move(osv.osv):
readonly=True),
}
def action_done(self, cr ,uid, ids, context=None):
res = super(stock_move, self).action_done(cr, uid, ids, context)
def write(self, cr, uid, ids, vals, context=None):
if isinstance(ids, (int, long)):
ids = [ids]
res = super(stock_move, self).write(cr, uid, ids, vals, context=context)
wf_service = netsvc.LocalService('workflow')
for id in ids:
wf_service.trg_trigger(uid, 'stock.move', id, cr)
@ -44,7 +46,6 @@ class stock_picking(osv.osv):
_columns = {
'purchase_id': fields.many2one('purchase.order', 'Purchase Order',
ondelete='set null', select=True),
'warehouse_id': fields.related('purchase_id', 'warehouse_id', type='many2one', relation='stock.warehouse', string='Destination Warehouse'),
}
_defaults = {

View File

@ -20,9 +20,6 @@
<xpath expr="//field[@name='move_type']" position="before">
<field name="purchase_id" domain="[('invoice_method','=','picking')]" groups="base.group_no_one" context="{'search_default_partner_id':partner_id,'default_partner_id':partner_id, 'default_invoice_method':'picking'}"/>
</xpath>
<xpath expr="//field[@name='company_id']" position="after">
<field name="warehouse_id" groups="stock.group_locations"/>
</xpath>
</field>
</record>

View File

@ -28,6 +28,26 @@
product_uom: product.product_uom_categ_kgm
price_unit: 50.0
name: 'FIFO Ice Cream'
-
I confirm the first purchase order
-
!workflow {model: purchase.order, action: purchase_confirm, ref: purchase_order_fifo1}
-
I check the "Approved" status of purchase order 1
-
!assert {model: purchase.order, id: purchase_order_fifo1}:
- state == 'approved'
-
Process the reception of purchase order 1 and set date
-
!python {model: stock.picking}: |
picking_obj = self.pool.get('purchase.order').browse(cr, uid, ref("purchase_order_fifo1")).picking_ids[0]
picking_obj.do_partial(context=context)
-
Check the standard price of the product (fifo icecream), that should have not changed because the standard price is supposed to be updated only when goods are going out of the stock
-
!python {model: product.product}: |
assert self.browse(cr, uid, ref("product_fifo_icecream")).standard_price == 70.0, 'Standard price should not have changed!'
-
I create a draft Purchase Order for second shipment for 30 kg at 80 euro
-
@ -41,27 +61,6 @@
product_uom: product.product_uom_categ_kgm
price_unit: 80.0
name: 'FIFO Ice Cream'
-
I confirm the first purchase order
-
!workflow {model: purchase.order, action: purchase_confirm, ref: purchase_order_fifo1}
-
I check the "Approved" status of purchase order 1
-
!assert {model: purchase.order, id: purchase_order_fifo1}:
- state == 'approved'
-
Process the reception of purchase order 1 and set date
-
!python {model: stock.partial.picking}: |
pick_ids = self.pool.get('purchase.order').browse(cr, uid, ref("purchase_order_fifo1")).picking_ids
partial_id = self.create(cr, uid, {}, context={'active_model': 'stock.picking','active_ids': [pick_ids[0].id]})
self.do_partial(cr, uid, [partial_id])
-
Check the standard price of the product (fifo icecream)
-
!python {model: product.product}: |
assert self.browse(cr, uid, ref("product_fifo_icecream")).standard_price == 70.0, 'Standard price should not have changed!'
-
I confirm the second purchase order
-
@ -69,26 +68,11 @@
-
Process the reception of purchase order 2
-
!python {model: stock.partial.picking}: |
pick_ids = self.pool.get('purchase.order').browse(cr, uid, ref("purchase_order_fifo2")).picking_ids
partial_id = self.create(cr, uid, {}, context={'active_model': 'stock.picking','active_ids': [pick_ids[0].id]})
self.do_partial(cr, uid, [partial_id])
!python {model: stock.picking}: |
picking_obj = self.pool.get('purchase.order').browse(cr, uid, ref("purchase_order_fifo2")).picking_ids[0]
picking_obj.do_partial(context=context)
-
A purchase order towards the Chicago shop TODO code for receiving
-
!record {model: purchase.order, id: purchase_order_fifo_comp}:
partner_id: base.res_partner_3
company_id: stock.res_company_1
location_id: stock.stock_location_shop0
pricelist_id: 1
order_line:
- product_id: product_fifo_icecream
product_qty: 35.0
product_uom: product.product_uom_categ_kgm
price_unit: 50.0
name: 'FIFO Ice Cream'
-
Check the standard price should not have changed
Check the standard price of the product, that should have not changed because the standard price is supposed to be updated only when goods are going out of the stock
-
!python {model: product.product}: |
assert self.browse(cr, uid, ref("product_fifo_icecream")).standard_price == 70.0, 'Standard price as fifo price of second reception incorrect!'
@ -96,7 +80,7 @@
Let us send some goods
-
!record {model: stock.picking, id: outgoing_fifo_shipment}:
type: out
picking_type_id: stock.picking_type_out
-
Picking needs movement from stock
-
@ -104,8 +88,10 @@
picking_id: outgoing_fifo_shipment
product_id: product_fifo_icecream
product_uom: product.product_uom_kgm
product_qty: 20.0
type: out
product_uom_qty: 20.0
location_id: stock.stock_location_stock
location_dest_id: stock.stock_location_customers
picking_type_id: stock.picking_type_out
-
I assign this outgoing shipment
-
@ -114,19 +100,19 @@
-
Process the delivery of the outgoing shipment
-
!python {model: stock.partial.picking}: |
partial_id = self.create(cr, uid, {}, context={'active_model': 'stock.picking','active_ids': [ref("outgoing_fifo_shipment")], 'default_type':'out'})
self.do_partial(cr, uid, [partial_id])
!python {model: stock.picking}: |
pick_obj = self.pool.get('stock.picking').browse(cr, uid, ref("outgoing_fifo_shipment"))
pick_obj.do_partial(context=context)
-
Check product standard price changed to 65.0
Check product standard price changed to 65.0 (because last outgoing shipment was made of 10 kg at 50€ and 10 kg at 80€)
-
!python {model: product.product}: |
assert self.browse(cr, uid, ref("product_fifo_icecream")).standard_price == 65.0
assert self.browse(cr, uid, ref("product_fifo_icecream")).standard_price == 65.0, "Product price not updated accordingly. %s found instead of 65" %(self.browse(cr, uid, ref("product_fifo_icecream")).standard_price,)
-
Do a delivery of an extra 500 g (delivery order)
-
!record {model: stock.picking, id: outgoing_fifo_shipment_uom}:
type: out
picking_type_id: stock.picking_type_out
-
Picking needs movement from stock
-
@ -134,8 +120,10 @@
picking_id: outgoing_fifo_shipment_uom
product_id: product_fifo_icecream
product_uom: product.product_uom_gram
product_qty: 500.0
type: out
location_id: stock.stock_location_stock
location_dest_id: stock.stock_location_customers
product_uom_qty: 500.0
picking_type_id: stock.picking_type_out
-
I assign this outgoing shipment
-
@ -144,14 +132,14 @@
-
Process the delivery of the outgoing shipment
-
!python {model: stock.partial.picking}: |
partial_id = self.create(cr, uid, {}, context={'active_model': 'stock.picking','active_ids': [ref("outgoing_fifo_shipment_uom")], 'default_type':'out'})
self.do_partial(cr, uid, [partial_id])
!python {model: stock.picking}: |
pick_obj = self.pool.get('stock.picking').browse(cr, uid, ref("outgoing_fifo_shipment_uom"))
pick_obj.do_partial(context=context)
-
Check product price changed to 80.0
Check product price changed to 80.0 (because last outgoing shipment was made of 0.5 kg at 80€)
-
!python {model: product.product}: |
assert self.browse(cr, uid, ref("product_fifo_icecream")).standard_price == 80.0
assert self.browse(cr, uid, ref("product_fifo_icecream")).standard_price == 80.0, "Product price not updated accordingly. %s found instead of 80" %(self.browse(cr, uid, ref("product_fifo_icecream")).standard_price,)
-
We will temporarily change the currency rate on the sixth of June to have the same results all year
-
@ -201,15 +189,14 @@
-
Process the reception of purchase order with usd
-
!python {model: stock.partial.picking}: |
pick_ids = self.pool.get('purchase.order').browse(cr, uid, ref("purchase_order_fifo_usd")).picking_ids
partial_id = self.create(cr, uid, {}, context={'active_model': 'stock.picking','active_ids': [pick_ids[0].id]})
self.do_partial(cr, uid, [partial_id])
!python {model: stock.picking}: |
pick_obj = self.pool.get('stock.picking').browse(cr, uid, ref("purchase_order_fifo_usd"))
pick_obj.do_partial(context=context)
-
We create delivery order of 49.5 kg
-
!record {model: stock.picking, id: outgoing_fifo_shipment_cur}:
type: out
picking_type_id: stock.picking_type_out
-
Picking needs movement from stock
-
@ -217,8 +204,10 @@
picking_id: outgoing_fifo_shipment_cur
product_id: product_fifo_icecream
product_uom: product.product_uom_kgm
product_qty: 49.5
type: out
product_uom_qty: 49.5
location_id: stock.stock_location_stock
location_dest_id: stock.stock_location_customers
picking_type_id: stock.picking_type_out
-
I assign this outgoing shipment
-
@ -227,19 +216,19 @@
-
Process the delivery of the outgoing shipment
-
!python {model: stock.partial.picking}: |
partial_id = self.create(cr, uid, {}, context={'active_model': 'stock.picking','active_ids': [ref("outgoing_fifo_shipment_cur")], 'default_type':'out'})
self.do_partial(cr, uid, [partial_id])
!python {model: stock.picking}: |
picking_obj = self.pool.get('stock.picking').browse(cr, uid, ref("outgoing_fifo_shipment_cur"))
picking_obj.do_partial(context=context)
-
Check rounded price is 102 euro
Check rounded price is 102 euro (because last outgoing shipment was made of 19.5kg at 80€ and 30kg at $150 (rate=1.2834)
-
!python {model: product.product}: |
assert round(self.browse(cr, uid, ref("product_fifo_icecream")).standard_price) == 102, "Price after doing other currency is wrong"
assert round(self.browse(cr, uid, ref("product_fifo_icecream")).standard_price) == 102, "Product price not updated accordingly. %s found instead of 102 (rounded values)" %(round(self.browse(cr, uid, ref("product_fifo_icecream")).standard_price),)
-
Do a delivery of an extra 10 kg
-
!record {model: stock.picking, id: outgoing_fifo_shipment_ret}:
type: out
picking_type_id: stock.picking_type_out
-
Picking needs movement from stock
-
@ -247,8 +236,10 @@
picking_id: outgoing_fifo_shipment_ret
product_id: product_fifo_icecream
product_uom: product.product_uom_kgm
product_qty: 10.0
type: out
product_uom_qty: 10.0
location_id: stock.stock_location_stock
location_dest_id: stock.stock_location_customers
picking_type_id: stock.picking_type_out
-
I assign this outgoing shipment
-
@ -257,19 +248,19 @@
-
Process the delivery of the outgoing shipment
-
!python {model: stock.partial.picking}: |
partial_id = self.create(cr, uid, {}, context={'active_model': 'stock.picking','active_ids': [ref("outgoing_fifo_shipment_ret")], 'default_type':'out'})
self.do_partial(cr, uid, [partial_id])
!python {model: stock.picking}: |
picking_obj = self.pool.get('stock.picking').browse(cr, uid, ref("outgoing_fifo_shipment_ret"))
picking_obj.do_partial(context=context)
-
Check rounded price is 150.0 / 1.2834
-
!python {model: product.product}: |
assert round(self.browse(cr, uid, ref("product_fifo_icecream")).standard_price) == round(150.0 / 1.2834)
assert round(self.browse(cr, uid, ref("product_fifo_icecream")).standard_price) == round(150.0 / 1.2834), "Product price not updated accordingly. %s found instead of %s" %(self.browse(cr, uid, ref("product_fifo_icecream")).standard_price, round(150.0/1.2834))
-
Let us create some outs to get negative stock. Create outpicking. We create delivery order of 200 kg, but will pick only 100 kg
-
!record {model: stock.picking, id: outgoing_fifo_shipment_neg}:
type: out
picking_type_id: stock.picking_type_out
-
Picking needs movement from stock
-
@ -277,13 +268,15 @@
picking_id: outgoing_fifo_shipment_neg
product_id: product_fifo_icecream
product_uom: product.product_uom_kgm
product_qty: 200.0
type: out
product_uom_qty: 200.0
location_id: stock.stock_location_stock
location_dest_id: stock.stock_location_customers
picking_type_id: stock.picking_type_out
-
Let us create another out of 400 kg, but will pick only 50 kg
-
!record {model: stock.picking, id: outgoing_fifo_shipment_neg2}:
type: out
picking_type_id: stock.picking_type_out
-
Picking needs movement from stock
-
@ -291,20 +284,18 @@
picking_id: outgoing_fifo_shipment_neg2
product_id: product_fifo_icecream
product_uom: product.product_uom_kgm
product_qty: 400.0
type: out
product_uom_qty: 400.0
location_id: stock.stock_location_stock
location_dest_id: stock.stock_location_customers
picking_type_id: stock.picking_type_out
-
Process the delivery of the outgoing shipments
-
!python {model: stock.partial.picking}: |
partial_id = self.create(cr, uid, {}, context={'active_model': 'stock.picking','active_ids': [ref("outgoing_fifo_shipment_neg")], 'default_type':'out'})
line = self.browse(cr, uid, partial_id, context=context).move_ids[0]
self.pool.get("stock.partial.picking.line").write(cr, uid, [line.id], {'quantity':100})
self.do_partial(cr, uid, [partial_id])
partial_id = self.create(cr, uid, {}, context={'active_model': 'stock.picking','active_ids': [ref("outgoing_fifo_shipment_neg2")], 'default_type':'out'})
line = self.browse(cr, uid, partial_id, context=context).move_ids[0]
self.pool.get("stock.partial.picking.line").write(cr, uid, [line.id], {'quantity':50})
self.do_partial(cr, uid, [partial_id])
!python {model: stock.picking}: |
picking_obj = self.browse(cr, uid, ref("outgoing_fifo_shipment_neg"))
picking_obj.do_partial(context=context)
picking_obj1 = self.browse(cr, uid, ref("outgoing_fifo_shipment_neg2"))
picking_obj1.do_partial(context=context)
-
Receive purchase order with 50 kg FIFO Ice Cream at 50 euro/kg
-
@ -325,15 +316,14 @@
-
Process the reception of purchase order 1
-
!python {model: stock.partial.picking}: |
pick_ids = self.pool.get('purchase.order').browse(cr, uid, ref("purchase_order_fifo_neg")).picking_ids
partial_id = self.create(cr, uid, {}, context={'active_model': 'stock.picking','active_ids': [pick_ids[0].id]})
self.do_partial(cr, uid, [partial_id])
!python {model: stock.picking}: |
picking_obj = self.pool.get('purchase.order').browse(cr, uid, ref("purchase_order_fifo_neg")).picking_ids[0]
picking_obj.do_partial(context=context)
-
Assert price on product is still the old price as the out move has not been received fully yet
-
!python {model: product.product}: |
assert round(self.browse(cr, uid, ref("product_fifo_icecream")).standard_price) == round(150.0 / 1.2834)
assert round(self.browse(cr, uid, ref("product_fifo_icecream")).standard_price) == round(150.0 / 1.2834), 'The product price should not have been updated'
-
Receive purchase order with 60 kg FIFO Ice Cream at 80 euro/kg
-
@ -354,12 +344,11 @@
-
Process the reception of purchase order 2
-
!python {model: stock.partial.picking}: |
pick_ids = self.pool.get('purchase.order').browse(cr, uid, ref("purchase_order_fifo_neg2")).picking_ids
partial_id = self.create(cr, uid, {}, context={'active_model': 'stock.picking','active_ids': [pick_ids[0].id]})
self.do_partial(cr, uid, [partial_id])
!python {model: stock.picking}: |
picking_obj = self.pool.get('purchase.order').browse(cr, uid, ref("purchase_order_fifo_neg2")).picking_ids[0]
picking_obj.do_partial(context=context)
-
The price of the product should have changed back to 65.0
-
!python {model: product.product}: |
assert self.browse(cr, uid, ref("product_fifo_icecream")).standard_price == 65.0
assert self.browse(cr, uid, ref("product_fifo_icecream")).standard_price == 65.0, "Product price not updated accordingly. %s found instead of 65" %(self.browse(cr, uid, ref("product_fifo_icecream")).standard_price,)

View File

@ -5,6 +5,7 @@
default_code: FIFO
name: FIFO Ice Cream
type: product
standard_price: 0.0
categ_id: product.product_category_1
uom_id: product.product_uom_kgm
uom_po_id: product.product_uom_kgm
@ -46,10 +47,9 @@
-
Process the reception of purchase order 1
-
!python {model: stock.partial.picking}: |
pick_ids = self.pool.get('purchase.order').browse(cr, uid, ref("purchase_order_fiforet1")).picking_ids
partial_id = self.create(cr, uid, {}, context={'active_model': 'stock.picking','active_ids': [pick_ids[0].id]})
self.do_partial(cr, uid, [partial_id])
!python {model: stock.picking}: |
picking_obj = self.pool.get('purchase.order').browse(cr, uid, ref("purchase_order_fiforet1")).picking_ids[0]
self.do_partial(cr,uid,[picking_obj.id], context=context)
-
Check the standard price of the product (fifo icecream)
-
@ -62,23 +62,19 @@
-
Process the reception of purchase order 2
-
!python {model: stock.partial.picking}: |
pick_ids = self.pool.get('purchase.order').browse(cr, uid, ref("purchase_order_fiforet2")).picking_ids
partial_id = self.create(cr, uid, {}, context={'active_model': 'stock.picking','active_ids': [pick_ids[0].id]})
self.do_partial(cr, uid, [partial_id])
!python {model: stock.picking}: |
picking_obj = self.pool.get('purchase.order').browse(cr, uid, ref("purchase_order_fiforet2")).picking_ids[0]
self.do_partial(cr,uid,[picking_obj.id], context=context)
-
Return the goods of purchase order 2
-
!python {model: stock.return.picking}: |
pick_ids = self.pool.get('purchase.order').browse(cr, uid, ref("purchase_order_fiforet2")).picking_ids
return_id = self.create(cr, uid, {}, context={'active_model':'stock.picking', 'active_id': pick_ids[0].id})
res = self.create_returns(cr, uid, [return_id], context={'active_id': pick_ids[0].id})
return_move_id = self.pool.get("stock.move").search(cr, uid, [('move_returned_from','=', pick_ids[0].move_lines[0].id)])
movepick_obj = self.pool.get("stock.partial.move")
movepick_id = movepick_obj.create(cr, uid, {}, context={'active_model': 'stock.move', 'active_ids': return_move_id})
movepick_obj.do_partial(cr, uid, [movepick_id])
return_picking_id, dummy = self._create_returns(cr, uid, [return_id], context={'active_id': pick_ids[0].id})
self.pool.get('stock.picking').do_partial(cr, uid, [return_picking_id])
-
Check the standard price of the product (fifo return icecream) changed to 70.0
Check the standard price of the product changed to 70.0 (as returns to supplier are processed as normal outgoing shipment we have sent 10kg at 50€ and 20kg at 80€, because of FIFO, so the average to store on the product is 70€)
-
!python {model: product.product}: |
assert self.browse(cr, uid, ref("product_fiforet_icecream")).standard_price == 70.0, 'Standard price should have changed to 70.0!'
assert self.browse(cr, uid, ref("product_fiforet_icecream")).standard_price == 70.0, 'Standard price should have changed to 70.0! %s found instead' % (self.browse(cr, uid, ref("product_fiforet_icecream")).standard_price,)

View File

@ -27,7 +27,6 @@
assert total_new_qty == total_qty,"product quantities are not correspond"
assert order.partner_id == order3.partner_id ,"partner is not correspond"
assert order.warehouse_id == order3.warehouse_id or order7.warehouse_id,"Warehouse is not correspond"
assert order.state == 'draft',"New created order state should be in draft"
assert order.pricelist_id == order3.pricelist_id,"Price list is not correspond"
assert order.date_order == order3.date_order ,"Date of order is not correspond"

View File

@ -1,12 +1,21 @@
-
In order to test the scheduler to generate RFQ.
In order to test the scheduler to generate RFQ, I create a new product
-
!record {model: product.product, id: scheduler_product}:
name: scheduler prod
type: product
supply_method: buy
seller_ids:
- delay: 1
name: base.res_partner_2
min_qty: 5.0
-
I create a procurement order.
-
!record {model: procurement.order, id: procurement_order_testcase0}:
location_id: stock.stock_location_stock
name: Test scheduler for RFQ
product_id: product.product_product_45
product_id: scheduler_product
product_qty: 15.0
-
I run the scheduler.

View File

@ -136,22 +136,13 @@ class purchase_requisition(osv.osv):
return res
def _prepare_purchase_order(self, cr, uid, requisition, supplier, context=None):
if not requisition.warehouse_id:
warehouse_obj = self.pool.get('stock.warehouse')
warehouse_id = warehouse_obj.search(cr, uid, [('company_id', '=', requisition.company_id.id)], context=context)
if not warehouse_id:
raise osv.except_osv(_('Warning!'), _('No warehouse found for this company.'))
location_id = warehouse_obj.browse(cr, uid, warehouse_id, context=context)[0].lot_input_id.id
else:
location_id = requisition.warehouse_id.lot_input_id.id
supplier_pricelist = supplier.property_product_pricelist_purchase and supplier.property_product_pricelist_purchase.id or False
return {
'origin': requisition.name,
'date_order': requisition.date_end or fields.date.context_today(self, cr, uid, context=context),
'partner_id': supplier.id,
'pricelist_id': supplier_pricelist,
'location_id': location_id,
#'location_id': location_id,
'company_id': requisition.company_id.id,
'fiscal_position': supplier.property_account_position and supplier.property_account_position.id or False,
'requisition_id': requisition.id,

View File

@ -504,8 +504,7 @@
</xpath>
</field>
</record>
<menuitem id="base.menu_invoiced" name="Invoicing" parent="base.menu_base_partner" sequence="5"/>
<menuitem id="menu_invoicing_sales_order_lines" parent="base.menu_invoiced" action="action_order_line_tree2" sequence="10" groups="sale.group_invoice_so_lines"/>
<record model="ir.ui.view" id="view_company_inherit_form2">
<field name="name">res.company.form.inherit</field>

View File

@ -26,7 +26,7 @@
<field name="company_id" position="replace">
<field name="company_id" readonly="True"/>
</field>
<field name="fiscal_position" position="after">
<field name="client_order_ref" position="after">
<field name="warehouse_id" on_change="onchange_warehouse_id(warehouse_id)" widget="selection" groups="stock.group_locations"/>
</field>
<field name="product_id" position="replace">

View File

@ -13,6 +13,7 @@
<field name="context">{'default_type': 'out', 'contact_display': 'partner_address', 'search_default_to_invoice': 1, 'search_default_done': 1}</field>
<field name="search_view_id" ref="stock.view_picking_internal_search"/>
</record>
<menuitem id="base.menu_invoiced" name="Invoicing" parent="base.menu_base_partner" sequence="5"/>
<menuitem action="outgoing_picking_list_to_invoice" id="menu_action_picking_list_to_invoice" parent="base.menu_invoiced" groups="sale_stock.group_invoice_deli_orders" sequence="20"/>
</data>

View File

@ -58,7 +58,6 @@
assert picking.invoice_state == (sale_order.order_policy=='picking' and '2binvoiced') or 'none',"Invoice policy is not correspond with sale order."
assert len(picking.move_lines) == len(sale_order.order_line), "Total move of delivery order are not corresposning with total sale order lines."
location_id = sale_order.warehouse_id.lot_stock_id.id
output_id = sale_order.warehouse_id.lot_output_id.id
for move in picking.move_lines:
order_line = move.procurement_id.sale_line_id
date_planned = datetime.strptime(sale_order.date_order, DEFAULT_SERVER_DATETIME_FORMAT) + relativedelta(days=order_line.delay or 0.0)
@ -72,7 +71,6 @@
assert move.product_packaging.id == order_line.product_packaging.id,"Product packaging is not correspond."
assert move.partner_id.id == order_line.address_allotment_id.id or sale_order.partner_shipping_id.id,"Address is not correspond"
#assert move.location_id.id == location_id,"Source Location is not correspond."
#assert move.location_dest_id == output_id,"Destination Location is not correspond."
assert move.price_unit == order_line.product_id.standard_price or 0.0,"Price Unit is not correspond"
-
Now, I dispatch delivery order.

View File

@ -21,6 +21,10 @@
from openerp.osv import fields, osv
from openerp.tools.translate import _
from openerp.tools import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT
from dateutil.relativedelta import relativedelta
from datetime import datetime
import openerp
class procurement_group(osv.osv):
@ -139,9 +143,17 @@ class procurement_order(osv.osv):
company = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id
move_obj = self.pool.get('stock.move')
#Minimum stock rules
self. _procure_orderpoint_confirm(cr, uid, automatic=False,use_new_cursor=False, context=context, user_id=False)
#Search all confirmed stock_moves and try to assign them
confirmed_ids = move_obj.search(cr, uid, [('state', '=', 'confirmed'), ('company_id','=', company.id)], context=context) #Type = stockable product?
move_obj.action_assign(cr, uid, confirmed_ids, context=context)
confirmed_ids = move_obj.search(cr, uid, [('state', '=', 'confirmed'), ('company_id','=', company.id)], limit = None, context=context) #Type = stockable product?
for x in xrange(0, len(confirmed_ids), 100):
move_obj.action_assign(cr, uid, confirmed_ids[x:x+100], context=context)
if use_new_cursor:
cr.commit()
if use_new_cursor:
cr.commit()
finally:
@ -152,3 +164,148 @@ class procurement_order(osv.osv):
pass
return {}
def _prepare_automatic_op_procurement(self, cr, uid, product, warehouse, location_id, context=None):
return {'name': _('Automatic OP: %s') % (product.name,),
'origin': _('SCHEDULER'),
'date_planned': datetime.today().strftime(DEFAULT_SERVER_DATETIME_FORMAT),
'product_id': product.id,
'product_qty': -product.virtual_available,
'product_uom': product.uom_id.id,
'location_id': location_id,
'company_id': warehouse.company_id.id,
'procure_method': 'make_to_order',}
def create_automatic_op(self, cr, uid, context=None):
"""
Create procurement of virtual stock < 0
@param self: The object pointer
@param cr: The current row, from the database cursor,
@param uid: The current user ID for security checks
@param context: A standard dictionary for contextual values
@return: Dictionary of values
"""
if context is None:
context = {}
product_obj = self.pool.get('product.product')
proc_obj = self.pool.get('procurement.order')
warehouse_obj = self.pool.get('stock.warehouse')
warehouse_ids = warehouse_obj.search(cr, uid, [], context=context)
products_ids = product_obj.search(cr, uid, [], order='id', context=context)
for warehouse in warehouse_obj.browse(cr, uid, warehouse_ids, context=context):
context['warehouse'] = warehouse
# Here we check products availability.
# We use the method 'read' for performance reasons, because using the method 'browse' may crash the server.
for product_read in product_obj.read(cr, uid, products_ids, ['virtual_available'], context=context):
if product_read['virtual_available'] >= 0.0:
continue
product = product_obj.browse(cr, uid, [product_read['id']], context=context)[0]
if product.supply_method == 'buy':
location_id = warehouse.lot_input_id.id
elif product.supply_method == 'produce':
location_id = warehouse.lot_stock_id.id
else:
continue
proc_id = proc_obj.create(cr, uid,
self._prepare_automatic_op_procurement(cr, uid, product, warehouse, location_id, context=context),
context=context)
self.signal_button_confirm(cr, uid, [proc_id])
self.signal_button_check(cr, uid, [proc_id])
return True
def _get_orderpoint_date_planned(self, cr, uid, orderpoint, start_date, context=None):
date_planned = start_date + \
relativedelta(days=orderpoint.product_id.seller_delay or 0.0)
return date_planned.strftime(DEFAULT_SERVER_DATE_FORMAT)
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,
'procure_method': 'make_to_order',
'origin': orderpoint.name}
def _product_virtual_get(self, cr, uid, order_point):
product_obj = self.pool.get('product.product')
return product_obj._product_available(cr, uid,
[order_point.product_id.id],
{'location': order_point.location_id.id})[order_point.product_id.id]['virtual_available']
def _procure_orderpoint_confirm(self, cr, uid, automatic=False,\
use_new_cursor=False, context=None, user_id=False):
'''
Create procurement based on Orderpoint
use_new_cursor: False or the dbname
@param self: The object pointer
@param cr: The current row, from the database cursor,
@param user_id: The current user ID for security checks
@param context: A standard dictionary for contextual values
@param param: False or the dbname
@return: Dictionary of values
"""
'''
if context is None:
context = {}
if use_new_cursor:
cr = openerp.registry(use_new_cursor).db.cursor()
orderpoint_obj = self.pool.get('stock.warehouse.orderpoint')
procurement_obj = self.pool.get('procurement.order')
offset = 0
ids = [1]
if automatic:
self.create_automatic_op(cr, uid, context=context)
while ids:
ids = orderpoint_obj.search(cr, uid, [], offset=offset, limit=100)
for op in orderpoint_obj.browse(cr, uid, ids, context=context):
prods = self._product_virtual_get(cr, uid, op)
if prods is None:
continue
if prods < op.product_min_qty:
qty = max(op.product_min_qty, op.product_max_qty)-prods
reste = qty % op.qty_multiple
if reste > 0:
qty += op.qty_multiple - reste
if qty <= 0:
continue
if op.product_id.type not in ('consu'):
if op.procurement_draft_ids:
# Check draft procurement related to this order point
pro_ids = [x.id for x in op.procurement_draft_ids]
procure_datas = procurement_obj.read(
cr, uid, pro_ids, ['id', 'product_qty'], context=context)
to_generate = qty
for proc_data in procure_datas:
if to_generate >= proc_data['product_qty']:
self.signal_button_confirm(cr, uid, [proc_data['id']])
procurement_obj.write(cr, uid, [proc_data['id']], {'origin': op.name}, context=context)
to_generate -= proc_data['product_qty']
if not to_generate:
break
qty = to_generate
if qty:
proc_id = procurement_obj.create(cr, uid,
self._prepare_orderpoint_procurement(cr, uid, op, qty, context=context),
context=context)
self.check(cr, uid, [proc_id])
self.run(cr, uid, [proc_id])
orderpoint_obj.write(cr, uid, [op.id],
{'procurement_id': proc_id}, context=context)
offset += len(ids)
if use_new_cursor:
cr.commit()
if use_new_cursor:
cr.commit()
cr.close()
return {}

View File

@ -58,6 +58,9 @@ class product_product(osv.osv):
return _('Products: ')+self.pool.get('stock.location').browse(cr, user, context['active_id'], context).name
return res
#
# TODO: Needs to be rechecked
#
def _get_domain_locations(self, cr, uid, ids, context=None):
'''
Parses the context and returns a list of location_ids based on it.
@ -118,7 +121,6 @@ class product_product(osv.osv):
domain_move_in += self._get_domain_dates(cr, uid, ids, context=context) + [('state','not in',('done','cancel'))] + domain_products
domain_move_out += self._get_domain_dates(cr, uid, ids, context=context) + [('state','not in',('done','cancel'))] + domain_products
domain_quant += domain_products
if context.get('lot_id', False):
domain_quant.append(('lot_id','=',context['lot_id']))
moves_in = []

View File

@ -26,6 +26,11 @@ class stock_config_settings(osv.osv_memory):
_inherit = 'res.config.settings'
_columns = {
'module_mrp_jit': fields.boolean("Generate procurement in real time",
help="""This allows Just In Time computation of procurement orders.
All procurement orders will be processed immediately, which could in some
cases entail a small performance impact.
This installs the module mrp_jit."""),
'module_claim_from_delivery': fields.boolean("Allow claim on deliveries",
help="""Adds a Claim link to the delivery order.
This installs the module claim_from_delivery."""),
@ -66,7 +71,6 @@ This installs the module product_expiry."""),
implied_group='stock.group_tracking_owner',
help="""This way you can receive products attributed to a certain owner. """),
'module_stock_account': fields.boolean("Generate accounting entries per stock movement",
implied_group='stock.group_inventory_valuation',
help="""Allows to configure inventory valuations on products and product categories."""),
'group_stock_multiple_locations': fields.boolean("Manage multiple locations and warehouses",
implied_group='stock.group_locations',

View File

@ -62,6 +62,10 @@
<group>
<label for="id" string="Logistic"/>
<div>
<div>
<field name="module_mrp_jit" class="oe_inline"/>
<label for="module_mrp_jit"/>
</div>
<div>
<field name="group_stock_multiple_locations" class="oe_inline"/>
<label for="group_stock_multiple_locations"/>

View File

@ -231,6 +231,7 @@ class stock_quant(osv.osv):
result += self._quants_get_lifo(cr, uid, location, product, qty, domain, prefered_order=prefered_order, context=context)
else:
raise osv.except_osv(_('Error!'), _('Removal strategy %s not implemented.' % (removal_strategy,)))
print 'Quant get result', result
return result
#
@ -241,7 +242,7 @@ class stock_quant(osv.osv):
def _quant_create(self, cr, uid, qty, move, lot_id = False, context=None):
# FP Note: TODO: compute the right price according to the move, with currency convert
# QTY is normally already converted to main product's UoM
price_unit = move.price_unit
price_unit = self.pool.get('stock.move').get_price_unit(cr, uid, move, context=context)
vals = {
'product_id': move.product_id.id,
'location_id': move.location_dest_id.id,
@ -250,10 +251,9 @@ class stock_quant(osv.osv):
'history_ids': [(4, move.id)],
'in_date': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
'company_id': move.company_id.id,
'lot_id': lot_id,
'lot_id': lot_id,
}
negative_quant_id = False
if move.location_id.usage == 'internal':
#if we were trying to move something from an internal location and reach here (quant creation),
#it means that a negative quant has to be created as well.
@ -262,9 +262,9 @@ class stock_quant(osv.osv):
negative_vals['qty'] = -qty
negative_vals['cost'] = price_unit
negative_quant_id = self.create(cr, uid, negative_vals, context=context)
vals.update({'propagated_from_id': negative_quant_id})
#create the quant
vals.update({'propagated_from_id': negative_quant_id})
quant_id = self.create(cr, uid, vals, context=context)
return self.browse(cr, uid, quant_id, context=context)
@ -710,8 +710,12 @@ class stock_picking(osv.osv):
Needed for parameter create
'''
self.rereserve(cr, uid, picking_ids, context=context)
#
# TODO:rereserve should be improved for giving negative quants when a certain lot is not there
# (Suppose you have a pack op for 20 lot B and lot B does not have any quants in the source location
# and could be used also instead of do_split
#
def rereserve(self, cr, uid, picking_ids, create=False, context=None):
"""
This will unreserve all products and reserve the quants from the operations
@ -750,7 +754,7 @@ class stock_picking(osv.osv):
qty = move.remaining_qty
qty_to_do -= move.remaining_qty
if create and move.location_id != 'internal':
if create and move.location_id.usage != 'internal':
# Create quants
vals = {
'product_id': move.product_id.id,
@ -773,8 +777,8 @@ class stock_picking(osv.osv):
quants = quant_obj.quants_get(cr, uid, move.location_id, move.product_id, qty, domain=domain, prefered_order=prefered_order, context=context)
quant_obj.quants_reserve(cr, uid, quants, move, context=context)
#In the end, move quants in correct package
if create and ops.result_package_id:
quant_obj.write(cr, uid, [x[0] for x in quants], {'package_id': ops.result_package_id.id}, context=context)
if create:
quant_obj.write(cr, uid, [x[0].id for x in quants if x[0] != None], {'package_id': ops.result_package_id and ops.result_package_id.id or False}, context=context)
res2[move.id] -= qty
res[ops.id] = {}
res[ops.id][ops.product_id.id] = qty_to_do
@ -791,13 +795,13 @@ class stock_picking(osv.osv):
else:
qty = move.remaining_qty
qty_to_do -= move.remaining_qty
quant_obj.quants_reserve(cr, uid, [(quant.id, qty)], move, context=context)
quant_obj.quants_reserve(cr, uid, [(quant, qty)], move, context=context)
res2[move.id] -= qty
res.setdefault(ops.id, {}).setdefault(quant.product_id.id, 0.0)
res[ops.id][quant.product_id.id] += qty_to_do
#Add parent package
if create and ops.result_package_id:
self.pool.get("stock.package").write(cr, uid, [ops.package_id.id], {'parent_id': ops.result_package_id.id}, context=context)
if create:
self.pool.get("stock.quant.package").write(cr, uid, [ops.package_id.id], {'parent_id': ops.result_package_id and ops.result_package_id.id or False}, context=context)
return (res, res2)
@ -840,8 +844,7 @@ class stock_picking(osv.osv):
for move in res2.keys():
if res2[move] > 0:
mov = stock_move_obj.browse(cr, uid, move, context=context)
newmove_id = stock_move_obj.split(cr, uid, mov, res2[move], context=context)
stock_move_obj.split(cr, uid, mov, res2[move], context=context)
stock_move_obj.action_done(cr, uid, extra_moves + [x.id for x in orig_moves], context=context)
picking.refresh()
self._create_backorder(cr, uid, picking, context=context)
@ -849,7 +852,7 @@ class stock_picking(osv.osv):
def do_split(self, cr, uid, picking_ids, context=None):
"""
just spit the picking without making it 'done'
just split the picking without making it 'done'
"""
if context is None:
context = {}
@ -976,6 +979,10 @@ class stock_move(osv.osv):
_order = 'date_expected desc, id'
_log_create = False
def get_price_unit(self, cr, uid, move, context=None):
""" Returns the unit price to store on the move """
return move.price_unit or move.product_id.standard_price
def name_get(self, cr, uid, ids, context=None):
res = []
for line in self.browse(cr, uid, ids, context=context):
@ -1009,7 +1016,7 @@ class stock_move(osv.osv):
uom_obj = self.pool.get('product.uom')
res = {}
for m in self.browse(cr, uid, ids, context=context):
res[m.id] = uom_obj._compute_qty_obj(cr, uid, m.product_uom, m.product_uom_qty, m.product_id.uom_id)
res[m.id] = uom_obj._compute_qty_obj(cr, uid, m.product_uom, m.product_uom_qty, m.product_id.uom_id, round=False)
return res
# def _get_remaining_qty(self, cr, uid, ids, field_name, args, context=None):
@ -1030,11 +1037,10 @@ class stock_move(osv.osv):
def _get_remaining_qty(self, cr, uid, ids, field_name, args, context=None):
res = {}
for move in self.browse(cr, uid, ids, context=context):
res[move.id] = move.product_uom_qty
res[move.id] = move.product_qty
for quant in move.reserved_quant_ids:
res[move.id] -= quant.qty
return res
def _get_lot_ids(self, cr, uid, ids, field_name, args, context=None):
res = dict.fromkeys(ids, False)
@ -1135,7 +1141,7 @@ class stock_move(osv.osv):
'reserved_quant_ids': fields.one2many('stock.quant', 'reservation_id', 'Reserved quants'),
'remaining_qty': fields.function(_get_remaining_qty, type='float', string='Remaining Quantity',
digits_compute=dp.get_precision('Product Unit of Measure'), states={'done': [('readonly', True)]},
store = {'stock.move': (lambda self, cr, uid, ids, c={}: ids , ['product_qty', 'product_uom', 'reserved_quant_ids'], 10),
store = {'stock.move': (lambda self, cr, uid, ids, c={}: ids , ['product_uom_qty', 'product_uom', 'reserved_quant_ids'], 20),
'stock.quant': (_get_move, ['reservation_id'], 10)}),
'procurement_id': fields.many2one('procurement.order', 'Procurement'),
'group_id': fields.related('procurement_id', 'group_id', type='many2one', relation="procurement.group", string='Procurement Group'),
@ -1498,20 +1504,19 @@ class stock_move(osv.osv):
for move in self.browse(cr, uid, ids, context=context):
if move.picking_id:
pickings.add(move.picking_id.id)
qty = move.product_uom_qty
qty = move.product_qty
# for qty, location_id in move_id.prefered_location_ids:
# quants = quant_obj.quants_get(cr, uid, move.location_id, move.product_id, qty, context=context)
# quant_obj.quants_move(cr, uid, quants, move, location_dest_id, context=context)
# should replace the above 2 lines
domain = ['|', ('reservation_id', '=', False), ('reservation_id', '=', move.id)]
prefered_order = 'reservation_id <> ' + str(move.id)
prefered_order = 'reservation_id'
# if lot_id:
# prefered_order = 'lot_id<>' + lot_id + ", " + prefered_order
# if pack_id:
# prefered_order = 'pack_id<>' + pack_id + ", " + prefered_order
quants = quant_obj.quants_get(cr, uid, move.location_id, move.product_id, qty, domain=domain, prefered_order = prefered_order, context=context)
#Will move all quants_get and as such create negative quants
quant_obj.quants_move(cr, uid, quants, move, context=context)
quant_obj.quants_unreserve(cr, uid, move, context=context)
@ -1552,9 +1557,10 @@ class stock_move(osv.osv):
source_location = move.location_id
if move.state == 'done':
source_location = move.location_dest_id
if source_location.usage != 'internal':
#Previously used to prevent scraping from virtual location but not necessary anymore
#if source_location.usage != 'internal':
#restrict to scrap from a virtual location because it's meaningless and it may introduce errors in stock ('creating' new products from nowhere)
raise osv.except_osv(_('Error!'), _('Forbidden operation: it is not allowed to scrap products from a virtual location.'))
#raise osv.except_osv(_('Error!'), _('Forbidden operation: it is not allowed to scrap products from a virtual location.'))
move_qty = move.product_qty
uos_qty = quantity / move_qty * move.product_uos_qty
default_val = {
@ -1925,25 +1931,17 @@ class stock_warehouse(osv.osv):
_columns = {
'name': fields.char('Name', size=128, required=True, select=True),
'company_id': fields.many2one('res.company', 'Company', required=True, select=True),
'partner_id': fields.many2one('res.partner', 'Owner Address'),
'lot_input_id': fields.many2one('stock.location', 'Location Input', required=True, domain=[('usage', '<>', 'view')]),
'partner_id': fields.many2one('res.partner', 'Address'),
'lot_stock_id': fields.many2one('stock.location', 'Location Stock', required=True, domain=[('usage', '=', 'internal')]),
'lot_output_id': fields.many2one('stock.location', 'Location Output', required=True, domain=[('usage', '<>', 'view')]),
}
def _default_lot_input_stock_id(self, cr, uid, context=None):
def _default_stock_id(self, cr, uid, context=None):
lot_input_stock = self.pool.get('ir.model.data').get_object(cr, uid, 'stock', 'stock_location_stock')
return lot_input_stock.id
def _default_lot_output_id(self, cr, uid, context=None):
lot_output = self.pool.get('ir.model.data').get_object(cr, uid, 'stock', 'stock_location_output')
return lot_output.id
_defaults = {
'company_id': lambda self, cr, uid, c: self.pool.get('res.company')._company_default_get(cr, uid, 'stock.inventory', context=c),
'lot_input_id': _default_lot_input_stock_id,
'lot_stock_id': _default_lot_input_stock_id,
'lot_output_id': _default_lot_output_id,
'lot_stock_id': _default_stock_id,
}
@ -2290,13 +2288,6 @@ class product_template(osv.osv):
'supply_method': 'buy',
}
class stock_picking_code(osv.osv):
_name = "stock.picking.code"
_description = "Will group picking types for kanban view"
_columns = {
'name': fields.char("Picking Type", translate=True),
}
class stock_picking_type(osv.osv):
_name = "stock.picking.type"
_description = "The picking type determines the picking view"
@ -2388,17 +2379,57 @@ class stock_picking_type(osv.osv):
result[type_id]['latest_picking_waiting'] = cmp(pick.date[:10], time.strftime('%Y-%m-%d'))
return result
def onchange_picking_code(self, cr, uid, ids, picking_code=False):
if not picking_code:
return False
obj_data = self.pool.get('ir.model.data')
stock_loc = obj_data.get_object_reference(cr, uid, 'stock','stock_location_stock')[1]
result = {
'default_location_src_id': stock_loc,
'default_location_dest_id': stock_loc,
}
if picking_code == 'incoming':
result['default_location_src_id'] = obj_data.get_object_reference(cr, uid, 'stock','stock_location_suppliers')[1]
return {'value': result}
if picking_code == 'outgoing':
result['default_location_dest_id'] = obj_data.get_object_reference(cr, uid, 'stock','stock_location_customers')[1]
return {'value': result}
else:
return {'value': result}
def name_get(self, cr, uid, ids, context=None):
"""Overides orm name_get method to display 'Warehouse_name: PickingType_name' """
if not isinstance(ids, list):
ids = [ids]
res = []
if not ids:
return res
for record in self.browse(cr, uid, ids, context=context):
name = record.name
if record.warehouse_id:
name = record.warehouse_id.name + ': ' +name
res.append((record.id, name))
return res
def _default_warehouse(self, cr, uid, context=None):
user = self.pool.get('res.users').browse(cr, uid, uid, context)
res = self.pool.get('stock.warehouse').search(cr, uid, [('company_id', '=', user.company_id.id)], limit=1, context=context)
return res and res[0] or False
_columns = {
'name': fields.char('name', translate=True, required=True),
'pack': fields.boolean('Pack', help='This picking type needs packing interface'),
'auto_force_assign': fields.boolean('Automatic Availability', help='This picking type does\'t need to check for the availability in stock'),
'pack': fields.boolean('Prefill Pack Operations', help='This picking type needs packing interface'),
'auto_force_assign': fields.boolean('Automatic Availability', help='This picking type does\'t need to check for the availability in source location.'),
'color': fields.integer('Color Index'),
'delivery': fields.boolean('Print delivery'),
'sequence_id': fields.many2one('ir.sequence', 'Sequence', required=True),
'default_location_src_id': fields.many2one('stock.location', 'Default Source Location'),
'default_location_dest_id': fields.many2one('stock.location', 'Default Destination Location'),
'code_id': fields.many2one('stock.picking.code', 'Picking type code', required=True),
'code_id': fields.selection([('incoming', 'Suppliers'), ('outgoing', 'Customers'), ('internal', 'Internal')], 'Picking type code', required=True),
'return_picking_type_id': fields.many2one('stock.picking.type', 'Picking Type for Returns'),
'warehouse_id': fields.many2one('stock.warehouse', 'Warehouse'),
# Statistics for the kanban view
'weekly_picking': fields.function(_get_picking_data,
@ -2427,4 +2458,7 @@ class stock_picking_type(osv.osv):
type='string', multi='_get_picking_history'),
}
_defaults = {
'warehouse_id': _default_warehouse,
}

View File

@ -77,12 +77,6 @@
<field name="location_id" ref="stock_location_company"/>
</record>
<!--
Picking types, sequences and codes
-->
<!-- Sequences for picking types -->
<record id="seq_picking_type_in" model="ir.sequence">
@ -106,24 +100,12 @@
<field name="company_id" eval="False"/>
</record>
<record id="picking_code_in" model="stock.picking.code">
<field name="name">Suppliers</field>
</record>
<record id="picking_code_out" model="stock.picking.code">
<field name="name">Customers</field>
</record>
<record id="picking_code_internal" model="stock.picking.code">
<field name="name">Internal</field>
</record>
<record id="picking_type_in" model="stock.picking.type">
<field name="name">Receptions</field>
<field name="sequence_id" ref="seq_picking_type_in"/>
<field name="default_location_src_id" ref="stock_location_suppliers"/>
<field name="default_location_dest_id" ref="stock_location_stock"/>
<field name="code_id" ref="picking_code_in"/>
<field name="code_id">incoming</field>
<field name="auto_force_assign">True</field>
</record>
@ -132,13 +114,13 @@
<field name="sequence_id" ref="seq_picking_type_out"/>
<field name="default_location_src_id" ref="stock_location_stock"/>
<field name="default_location_dest_id" ref="stock_location_customers"/>
<field name="code_id" ref="picking_code_out"/>
<field name="code_id">outgoing</field>
</record>
<record id="picking_type_internal" model="stock.picking.type">
<field name="name">Internal Transfers</field>
<field name="sequence_id" ref="seq_picking_type_internal"/>
<field name="code_id" ref="picking_code_internal"/>
<field name="code_id">internal</field>
</record>
@ -228,9 +210,7 @@ watch your stock valuation, and track production lots upstream and downstream (b
-->
<record id="warehouse0" model="stock.warehouse">
<field model="res.company" name="name" search="[]" use="name"/>
<field name="lot_input_id" ref="stock_location_stock"/>
<field name="lot_stock_id" ref="stock_location_stock"/>
<field name="lot_output_id" ref="stock_location_output"/>
</record>
<record id="sequence_mrp_op_type" model="ir.sequence.type">

View File

@ -251,20 +251,16 @@
</record>
<record id="stock_warehouse_shop0" model="stock.warehouse">
<field name="lot_output_id" ref="stock.stock_location_output"/>
<field name="name">Chicago Warehouse</field>
<field name="lot_stock_id" ref="stock_location_shop0"/>
<field name="partner_id" ref="res_partner_address_41"/>
<field name="company_id" ref="res_company_1"/>
<field name="lot_input_id" ref="stock_location_shop0"/>
</record>
<record id="stock_warehouse_shop1" model="stock.warehouse">
<field name="lot_output_id" ref="stock.stock_location_output"/>
<field name="name">Birmingham Warehouse</field>
<field name="lot_stock_id" ref="stock_location_shop1"/>
<field name="partner_id" ref="res_partner_address_40"/>
<field name="company_id" ref="res_company_1"/>
<field name="lot_input_id" ref="stock_location_shop1"/>
</record>
</data>

View File

@ -22,9 +22,7 @@
-
!record {model: stock.warehouse, id: warehouse_icecream}:
name: Ice Cream Shop
lot_input_id: location_refrigerator
lot_stock_id: location_refrigerator
lot_output_id: location_delivery_counter
-
!record {model: product.product, id: product_icecream}:
default_code: 001

View File

@ -487,9 +487,7 @@
<h1><field name="name"/></h1>
<group>
<group>
<field name="lot_input_id" groups="stock.group_locations"/>
<field name="lot_stock_id" groups="stock.group_locations"/>
<field name="lot_output_id" groups="stock.group_locations"/>
</group>
<group>
<field name="company_id" groups="base.group_multi_company" widget="selection"/>
@ -506,9 +504,7 @@
<field name="arch" type="xml">
<tree string="Warehouse">
<field name="name"/>
<field name="lot_input_id" groups="stock.group_locations"/>
<field name="lot_stock_id" groups="stock.group_locations"/>
<field name="lot_output_id" groups="stock.group_locations"/>
<field name="partner_id"/>
</tree>
</field>
@ -588,7 +584,6 @@
<group>
<field name="partner_id"/>
<field name="backorder_id" readonly="1" attrs="{'invisible': [('backorder_id','=',False)]}"/>
<field name="picking_type_id"/>
</group>
<group>
<field name="date"/>
@ -611,6 +606,7 @@
<field name="result_package_id" groups="stock.group_tracking_lot"/>
</tree>
</field>
<separator string="Expected Quantities"/>
<field name="move_lines" context="{'address_in_id': partner_id, 'form_view_ref':'view_move_picking_form', 'tree_view_ref':'view_move_picking_tree', 'default_picking_type_id': picking_type_id}"/>
<field name="note" placeholder="Add an internal note..." class="oe_inline"/>
</page>
@ -618,6 +614,7 @@
<group>
<group>
<field name="move_type"/>
<field name="picking_type_id"/>
</group>
<group>
<field name="company_id" groups="base.group_multi_company" widget="selection"/>
@ -730,7 +727,6 @@
'search_default_picking_type_id': [active_id],
'default_picking_type_id': active_id,
'contact_display': 'partner_address',
'search_default_available': 1
}
</field>
<field name="search_view_id" ref="view_picking_internal_search"/>
@ -1175,6 +1171,7 @@
<search string="Picking Type">
<field name="name" string="Picking Type"/>
<field name="sequence_id"/>
<field name="warehouse_id"/>
</search>
</field>
</record>
@ -1194,18 +1191,27 @@
<field name="model">stock.picking.type</field>
<field name="arch" type="xml">
<form string="Picking Types" version="7.0">
<group>
<field name="name"/>
<field name="code_id"/>
<field name="sequence_id"/>
<field name="pack"/>
<field name="auto_force_assign"/>
</group>
<group>
<field name="default_location_src_id"/>
<field name="default_location_dest_id"/>
<field name="return_picking_type_id"/>
</group>
<sheet>
<group>
<group>
<field name="name"/>
<field name="sequence_id"/>
<field name="warehouse_id"/>
<field name="pack"/>
</group>
<group>
<field name="code_id" on_change="onchange_picking_code(code_id)"/>
<field name="return_picking_type_id"/>
<field name="auto_force_assign"/>
<field name="delivery"/>
</group>
</group>
<separator string="Locations"/>
<group>
<field name="default_location_src_id" attrs="{'required': [('code_id', '=', 'internal')]}"/>
<field name="default_location_dest_id" attrs="{'required': [('code_id', 'in', ('internal', 'incoming'))]}"/>
</group>
</sheet>
</form>
</field>
</record>
@ -1562,7 +1568,7 @@
<!-- Procurements are located in Warehouse menu hierarchy, MRP users should come to Stock application to use it. -->
<menuitem id="menu_stock_sched" name="Schedulers" parent="stock.menu_stock_root" sequence="4" groups="stock.group_stock_manager"/>
<menuitem action="action_procurement_compute" id="menu_procurement_compute" parent="menu_stock_sched" groups="stock.group_stock_manager"/>
<menuitem action="action_procurement_compute" id="menu_procurement_compute" parent="menu_stock_sched" groups="base.group_no_one"/>
<menuitem action="procurement.action_compute_schedulers" id="menu_stock_proc_schedulers" parent="menu_stock_sched" sequence="20" groups="stock.group_stock_manager"/>
<menuitem action="procurement.procurement_exceptions" id="menu_stock_procurement_action" parent="menu_stock_sched" sequence="50" groups="stock.group_stock_manager"/>
<menuitem id="menu_stock_procurement" name="Automatic Procurements" parent="stock.menu_stock_configuration" sequence="5"/>
@ -1645,7 +1651,7 @@
<field name="loc_case"/>
</group>
</group>
<group name="Weights" position="after">
<group name="Weights" position="before">
<group name="store" groups="stock.group_locations" string="Counter-Part Locations Properties">
<field name="property_stock_procurement" attrs="{'readonly':[('type','=','service')]}" domain="[('usage','=','procurement')]"/>
<field name="property_stock_production" attrs="{'readonly':[('type','=','service')]}" domain="[('usage','=','production')]"/>

View File

@ -1,9 +1,15 @@
-
I create a move of 5 products from stock to customer
I first create a new product
-
!record {model: product.product, id: move_product}:
name: move prod
type: product
-
In order to test the negative quants, I create a move of 5 products from stock to customer
-
!record {model: stock.move, id: move_test0}:
name: Move Products
product_id: product.product_product_3
product_id: move_product
product_uom_qty: 5
product_uom: product.product_uom_unit
product_uos_qty: 5
@ -16,63 +22,38 @@
!python {model: stock.move}: |
self.action_confirm(cr, uid, [ref('move_test0')], context=context)
-
I check that the quantity on hand is 18 and virtual is 13
I check that the quantity on hand is 0 and virtual is -5
-
!python {model: product.product}: |
context['location'] = False
product = self.browse(cr, uid, ref('product.product_product_3'), context=context)
assert product.qty_available==18, 'Expecting 18 products in stock, got %.2f!' % (product.qty_available,)
assert product.virtual_available==13.0, 'Expecting 13 products in virtual stock, got %.2f!' % (product.virtual_available,)
product = self.browse(cr, uid, ref('move_product'), context=context)
assert product.qty_available == 0, 'Expecting 0 products in stock, got %.2f!' % (product.qty_available,)
assert product.virtual_available == -5.0, 'Expecting -5 products in virtual stock, got %.2f!' % (product.virtual_available,)
-
I validate the move
-
!python {model: stock.move}: |
self.action_done(cr, uid, [ref('move_test0')], context=context)
-
I check that the quantity on hand is 13 and 5 products are at customer location
I check that the quantity on hand is -5 and 5 products are at customer location
-
!python {model: product.product}: |
context['location'] = False
product = self.browse(cr, uid, ref('product.product_product_3'), context=context)
assert product.qty_available==13, 'Expecting 13 products in stock, got %.2f!' % (product.qty_available,)
product = self.browse(cr, uid, ref('move_product'), context=context)
assert product.qty_available == -5, 'Expecting -5 products in stock, got %.2f!' % (product.qty_available,)
context['location'] = ref('stock.stock_location_customers')
product = self.browse(cr, uid, ref('product.product_product_3'), context=context)
assert product.qty_available==5, 'Expecting 5 products in customer location, got %.2f!' % (product.qty_available,)
product = self.browse(cr, uid, ref('move_product'), context=context)
assert product.qty_available == 5, 'Expecting 5 products in customer location, got %.2f!' % (product.qty_available,)
-
In order to test negative quants, I will deliver 14 products to the customer
-
!record {model: stock.move, id: move_test1}:
name: Move 14 Products
product_id: product.product_product_3
product_uom_qty: 14
product_uom: product.product_uom_unit
product_uos_qty: 14
product_uos: product.product_uom_unit
location_id: stock.stock_location_stock
location_dest_id: stock.stock_location_customers
-
I confirm and validate the move
-
!python {model: stock.move}: |
self.action_confirm(cr, uid, [ref('move_test1')], context=context)
self.action_done(cr, uid, [ref('move_test1')], context=context)
-
I check that the quantity on hand is -2
-
!python {model: product.product}: |
context['location'] = False
product = self.browse(cr, uid, ref('product.product_product_3'), context=context)
assert product.qty_available == -1, 'Expecting -1 products in stock, got %.2f!' % (product.qty_available,)
-
To compensate negative quants, I will receive 5 products from the supplier
To compensate negative quants, I will receive 15 products from the supplier
-
!record {model: stock.move, id: move_test2}:
name: Move 15 Products
product_id: product.product_product_3
product_uom_qty: 5
product_id: move_product
product_uom_qty: 15
product_uom: product.product_uom_unit
product_uos_qty: 5
product_uos_qty: 15
product_uos: product.product_uom_unit
location_id: stock.stock_location_suppliers
location_dest_id: stock.stock_location_stock
@ -83,9 +64,51 @@
self.action_confirm(cr, uid, [ref('move_test2')], context=context)
self.action_done(cr, uid, [ref('move_test2')], context=context)
-
I check that the quantity on hand is 4
I check that the quantity on hand is 10
-
!python {model: product.product}: |
context['location'] = False
product = self.browse(cr, uid, ref('product.product_product_3'), context=context)
assert product.qty_available == 4, 'Expecting 4 products in stock, got %.2f!' % (product.qty_available,)
product = self.browse(cr, uid, ref('move_product'), context=context)
assert product.qty_available == 10, 'Expecting 10 products in stock, got %.2f!' % (product.qty_available,)
assert product.virtual_available == 10.0, 'Expecting 10 products in virtual stock, got %.2f!' % (product.virtual_available,)
-
I create a move of 2 products from stock to customer
-
!record {model: stock.move, id: move_test1}:
name: Move Products
product_id: move_product
product_uom_qty: 2
product_uom: product.product_uom_unit
product_uos_qty: 2
product_uos: product.product_uom_unit
location_id: stock.stock_location_stock
location_dest_id: stock.stock_location_customers
-
I confirm the move to be processed in the future
-
!python {model: stock.move}: |
self.action_confirm(cr, uid, [ref('move_test1')], context=context)
-
I check that the quantity on hand is 10 and virtual is 8
-
!python {model: product.product}: |
context['location'] = False
product = self.browse(cr, uid, ref('move_product'), context=context)
assert product.qty_available == 10, 'Expecting 10 products in stock, got %.2f!' % (product.qty_available,)
assert product.virtual_available == 8.0, 'Expecting 8 products in virtual stock, got %.2f!' % (product.virtual_available,)
-
I validate the move
-
!python {model: stock.move}: |
self.action_done(cr, uid, [ref('move_test1')], context=context)
-
I check that the quantity on hand is 8 and 7 products are at customer location
-
!python {model: product.product}: |
context['location'] = False
product = self.browse(cr, uid, ref('move_product'), context=context)
assert product.qty_available == 8, 'Expecting 10 products in stock, got %.2f!' % (product.qty_available,)
context['location'] = ref('stock.stock_location_customers')
product = self.browse(cr, uid, ref('move_product'), context=context)
assert product.qty_available == 7, 'Expecting 7 products in customer location, got %.2f!' % (product.qty_available,)

View File

@ -48,9 +48,9 @@
package3 = stock_quant_pack.create(cr, uid, {'name': 'Pallet 3'}, context=context)
#Create package for each line and assign it as result_package_id
#create pack operation
stock_pack.write(cr, uid, record.pack_operation_ids[0].id, {'package_id': package1, 'result_package_id': package1, 'product_qty': 120})
new_pack1 = stock_pack.create(cr, uid, {'product_id': ref('product1'), 'product_uom_id': ref('product.product_uom_unit'), 'picking_id': ref('pick1'), 'lot_id': lot_a, 'package_id': package2, 'result_package_id': package2, 'product_qty': 120}, context=context)
new_pack2 = stock_pack.create(cr, uid, {'product_id': ref('product1'), 'product_uom_id': ref('product.product_uom_unit'), 'picking_id': ref('pick1'), 'package_id': package3, 'result_package_id': package3, 'product_qty': 60}, context=context)
stock_pack.write(cr, uid, record.pack_operation_ids[0].id, {'result_package_id': package1, 'product_qty': 120})
new_pack1 = stock_pack.create(cr, uid, {'product_id': ref('product1'), 'product_uom_id': ref('product.product_uom_unit'), 'picking_id': ref('pick1'), 'lot_id': lot_a, 'result_package_id': package2, 'product_qty': 120}, context=context)
new_pack2 = stock_pack.create(cr, uid, {'product_id': ref('product1'), 'product_uom_id': ref('product.product_uom_unit'), 'picking_id': ref('pick1'), 'result_package_id': package3, 'product_qty': 60}, context=context)
-
Use button rereserve and check the qtyremaining on the moves are correct (=original quantities)
-
@ -72,7 +72,7 @@
assert len(reco_id) == 3, "The number of quants created is not correct"
for rec in self.browse(cr, uid, reco_id, context=context):
if rec.package_id.name == 'Pallet 1':
assert rec.qty == 120, "Should have 120 pîeces on pallet 1"
assert rec.qty == 120, "Should have 120 pieces on pallet 1"
elif rec.package_id.name == 'Pallet 2':
assert rec.qty == 120, "Should have 120 pieces on pallet 2"
elif rec.package_id.name == 'Pallet 3':
@ -113,17 +113,16 @@
self.do_prepare_partial(cr, uid, [ref('delivery_order1')], context=context)
delivery_id = self.browse(cr, uid, ref('delivery_order1'), context=context)
for rec in delivery_id.pack_operation_ids:
if rec.package_id.name == 'Pallet 1' or rec.product_qty == 120:
stock_pack.write(cr, uid, rec.id, {'product_qty': 120}, context=context)
if rec.package_id.name == 'Pallet 2' or rec.lot_id.name == 'Lot A' :
if rec.package_id.name == 'Pallet 1' and rec.product_qty == 120:
stock_pack.write(cr, uid, rec.id, {'product_id': False}, context=context)
if rec.package_id.name == 'Pallet 2' and rec.lot_id.name == 'Lot A' :
stock_pack.write(cr, uid, rec.id, {'product_qty': 20}, context=context)
if rec.package_id.name == 'Pallet 3' or rec.product_qty == 60:
if rec.package_id.name == 'Pallet 3' and rec.product_qty == 60:
stock_pack.write(cr, uid, rec.id, {'product_qty': 10}, context=context)
-
Process this picking
-
!python {model: stock.picking}: |
self.rereserve(cr, uid, [ref('delivery_order1')], context=context)
self.do_partial(cr, uid, [ref('delivery_order1')], context=context)
-
Check the quants that you have 120 pieces pallet 1 in customers, 100 pieces pallet 2 in stock and 20 with customers and 50 in stock, 10 in customers from pallet 3
@ -133,12 +132,18 @@
for rec in self.browse(cr, uid, reco_id, context=context):
if rec.package_id.name == 'Pallet 1' and rec.location_id.id == ref('stock_location_customers'):
assert rec.qty == 120, "Should have 120 pieces on pallet 1"
if rec.package_id.name == 'Pallet 2' and rec.location_id.id == ref('stock_location_stock'):
assert rec.qty == 20, "Should have 20 pieces in stock on pallet 2"
if rec.package_id.name == 'Pallet 3' and rec.location_id.id == ref('stock_location_stock'):
assert rec.qty == 10, "Should have 10 pieces in stock on pallet 3"
elif rec.package_id.name == 'Pallet 2' and rec.location_id.id == ref('stock_location_stock'):
assert rec.qty == 100, "Should have 100 pieces in stock on pallet 2"
elif rec.lot_id.name == 'Lot A' and rec.location_id.id == ref('stock_location_customers'):
assert (rec.qty == 20 and not rec.package_id), "Should have 20 pieces in customer location from pallet 2"
elif rec.package_id.name == 'Pallet 3' and rec.location_id.id == ref('stock_location_stock'):
assert rec.qty == 50, "Should have 50 pieces in stock on pallet 3"
elif not rec.package_id and not rec.lot_id and rec.location_id.id == ref('stock_location_customers'):
assert rec.qty == 10, "Should have 10 pieces in customer location from pallet 3"
else:
assert False, "Unrecognized quant"
-
Check a backorder was created and on that backorder, prepare partial and add an op with 20 pieces (20 that cannot be assigned) with lot B
Check a backorder was created and on that backorder, prepare partial
-
!python {model: stock.picking}: |
picking = self.browse(cr, uid, ref('delivery_order1'), context=context)
@ -147,11 +152,8 @@
backorder_id = self.browse(cr, uid, backorder, context=context)
self.action_confirm(cr, uid, backorder, context=context)
self.action_assign(cr, uid, backorder)
self.force_assign(cr, uid, backorder, context=context)
self.do_prepare_partial(cr, uid, backorder, context=context)
#create lot B
lot_b = self.pool.get('stock.production.lot').create(cr, uid, {'name': 'Lot B', 'product_id': ref('product1')}, context=context)
stock_pack = self.pool.get('stock.pack.operation').create(cr, uid, {'picking_id': backorder_id[0].id, 'lot_id': lot_b, 'product_qty': 20})
stock_pack = self.pool.get('stock.pack.operation').create(cr, uid, {'picking_id': backorder_id[0].id, 'product_qty': 20})
-
Process this backorder
-
@ -162,10 +164,13 @@
self.rereserve(cr, uid, [backorder_id[0].id], context=context)
self.do_partial(cr, uid, [backorder_id[0].id], context=context)
-
Check you have a negative quant because there were 20 too many that were transferred with lot B
Check there are still 0 pieces in stock
-
!python {model: stock.quant}: |
reco_id = self.search(cr ,uid , [('product_id','=',ref('product1'))], context=context)
reco_id = self.search(cr ,uid , [('product_id','=',ref('product1')), ('location_id', '=', ref('stock_location_stock'))], context=context)
total_qty = 0
for rec in self.browse(cr, uid, reco_id, context=context):
if rec.lot_id.name == 'Lot B':
assert rec.qty != -20, ""
total_qty += rec.qty
product = self.pool.get("product.product").browse(cr, uid, ref('product1'))
assert total_qty == 0, "Total quantity in stock should be 0 as the backorder took everything out of stock"
assert product.qty_available == 0, "Quantity available should be 0 too"

View File

@ -25,11 +25,16 @@
-
!python {model: stock.picking}: |
self.action_confirm(cr, uid, [ref('pick_output')])
-
I run the scheduler.
-
!python {model: procurement.order}: |
self.run_scheduler(cr, uid)
-
Check a picking was created from stock to output.
-
!python {model: stock.move }: |
picking = self.pool.get("stock.picking").browse(cr, uid, ref("pick_output"))
move_id = self.search(cr, uid, [('product_id', '=', ref('product.product_product_3')),('location_id', '=', ref('stock.stock_location_stock')),
('location_dest_id', '=', ref('stock.stock_location_output'), ('move_dest_id', '=', picking.move_lines[0].id))])
assert len(move_id) == 1, "It should have created a picking from Stock to Output with the original picking as destination"
('location_dest_id', '=', ref('stock.stock_location_output')), ('move_dest_id', '=', picking.move_lines[0].id)])
assert len(move_id) == 1, "It should have created a picking from Stock to Output with the original picking as destination"

View File

@ -1,22 +1,3 @@
-
I confirm outgoing shipment of 130 kgm Ice-cream.
-
!python {model: stock.picking}: |
self.action_confirm(cr, uid, [ref("outgoing_shipment")])
-
I check shipment details after confirmed.
-
!python {model: stock.picking}: |
shipment = self.browse(cr, uid, ref("outgoing_shipment"))
assert shipment.state == "confirmed", "Shipment should be confirmed."
for move_line in shipment.move_lines:
assert move_line.state == "confirmed", "Move should be confirmed."
-
Now I check virtual stock of Ice-cream after confirmed outgoing shipment.
-
!python {model: product.product}: |
product = self.browse(cr, uid, ref('product_icecream'), context=context)
product.virtual_available == -30, "Vitual stock is not updated."
-
I confirm incomming shipment of 50 kgm Ice-cream.
-
@ -50,33 +31,24 @@
assert move_line.state == 'draft', "Move line of backorder should be draft."
context.update({'active_model': 'stock.picking', 'active_id': backorder_id[0], 'active_ids': backorder_id})
self.action_confirm(cr, uid, backorder_id, context=context)
self.do_partial(cr, uid, backorder_id, context=context)
-
I receive another 10kgm Ice-cream.
I receive the remaining 10kgm Ice-cream from the backorder.
-
!python {model: stock.picking}: |
pick = self.browse(cr, uid, ref("incomming_shipment"))
backorder_id = self.search(cr, uid, [('backorder_id', '=', ref("incomming_shipment"))],context=context)
backorder = self.browse(cr, uid, backorder_id)[0]
self.pool.get('stock.pack.operation').create(cr, uid, {
'picking_id': pick.id,
'picking_id': backorder.id,
'product_id': ref('product_icecream'),
'product_uom_id': ref('product.product_uom_kgm'),
'product_qty': 10
})
context.update({'active_model': 'stock.picking', 'active_id': ref('incomming_shipment'), 'active_ids': [ref('incomming_shipment')]})
pick.do_partial(context=context)
backorder.do_partial(context=context)
-
I check incomming shipment after received.
I check incomming shipment after reception.
-
!python {model: stock.picking}: |
shipment = self.browse(cr, uid, self.search(cr, uid, [('backorder_id', '=', ref("incomming_shipment"))]))[0]
assert shipment.state == 'done', "shipment should be close after received."
for move_line in shipment.move_lines:
assert move_line.product_qty == 10, "Qty does not correspond."
assert move_line.product_id.virtual_available == 20, "Virtual stock does not correspond."
assert move_line.state == 'done', "Move line should be closed."
-
Return picking
-
!python {model: stock.return.picking }: |
# TODO: Should still work out according to the previous steps of shipment.yml
pass

View File

@ -109,8 +109,6 @@ class stock_move_scrap(osv.osv_memory):
res.update({'product_id': move.product_id.id})
if 'product_uom' in fields:
res.update({'product_uom': move.product_uom.id})
if 'product_qty' in fields:
res.update({'product_qty': move.product_qty})
if 'location_id' in fields:
if scrap_location_id:
res.update({'location_id': scrap_location_id[0]})

View File

@ -80,16 +80,7 @@ class stock_return_picking(osv.osv_memory):
res.update({'product_return_moves': result1})
return res
def create_returns(self, cr, uid, ids, context=None):
"""
Creates return picking.
@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.
"""
def _create_returns(self, cr, uid, ids, context=None):
if context is None:
context = {}
record_id = context and context.get('active_id', False) or False
@ -136,9 +127,22 @@ class stock_return_picking(osv.osv_memory):
pick_obj.action_confirm(cr, uid, [new_picking], context=context)
pick_obj.force_assign(cr, uid, [new_picking], context)
return new_picking, pick_type_id
def create_returns(self, cr, uid, ids, context=None):
"""
Creates return picking.
@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.
"""
new_picking_id, pick_type_id = self._create_returns(cr, uid, ids, context=context)
ctx = {'default_picking_type_id': pick_type_id}
return {
'domain': "[('id', 'in', [" + str(new_picking) + "])]",
'domain': "[('id', 'in', [" + str(new_picking_id) + "])]",
'name': _('Returned Picking'),
'view_type': 'form',
'view_mode': 'tree,form',

View File

@ -169,7 +169,7 @@ class product_product(osv.osv):
_columns = {
'valuation':fields.property(type='selection', selection=[('manual_periodic', 'Periodical (manual)'),
('real_time','Real Time (automated)'),], string = 'Inventory Valuation',
help="If real-time valuation is enabled for a product, the system will automatically write journal entries corresponding to stock moves." \
help="If real-time valuation is enabled for a product, the system will automatically write journal entries corresponding to stock moves, with product price as specified by the 'Costing Method'" \
"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),
}
@ -184,9 +184,9 @@ class product_template(osv.osv):
_inherit = 'product.template'
_columns = {
'cost_method': fields.property(type='selection', selection=[('standard', 'Standard Price'), ('average', 'Average Price'), ('real', 'Real Price')],
help="""Standard Price: The cost price is manually updated at the end of a specific period (usually every year)
Average Price: The cost price is recomputed at each incoming shipment
Real Price: The cost price is calculated as the real price of each outgoing product""",
help="""Standard Price: The cost price is manually updated at the end of a specific period (usually every year).
Average Price: The cost price is recomputed at each incoming shipment and used for the product valuation.
Real Price: The cost price displayed is the price of the last outgoing product (will be use in case of inventory loss for example).""",
string="Costing Method", required=True),
'property_stock_account_input': fields.property(
type='many2one',

View File

@ -75,7 +75,7 @@
<field name="priority">26</field>
<field name="arch" type="xml">
<xpath expr="//group[@name='properties']" position="before">
<group groups="stock.group_inventory_valuation">
<group>
<separator string="Inventory Valuation" colspan="4"/>
<group colspan="2" col="2">
<field name="valuation" attrs="{'readonly':[('type', '=', 'service')]}"/>

View File

@ -70,7 +70,6 @@ class stock_quant(osv.osv):
return line.cost * line.qty
return super(stock_quant, self)._get_inventory_value(cr, uid, line, prodbrow, context=context)
# FP Note: this is where we should post accounting entries for adjustment
def _price_update(self, cr, uid, quant, newprice, context=None):
super(stock_quant, self)._price_update(cr, uid, quant, newprice, context=context)
@ -198,4 +197,28 @@ class stock_quant(osv.osv):
return move_obj.create(cr, uid, {'journal_id': journal_id,
'line_id': move_lines,
'ref': move.picking_id and move.picking_id.name}, context=context)
class stock_move(osv.osv):
_inherit = "stock.move"
def action_done(self, cr, uid, ids, context=None):
super(stock_move, self).action_done(cr, uid, ids, context=context)
self.product_price_update(cr, uid, ids, context=context)
def product_price_update(self, cr, uid, ids, context=None):
'''
This method adapts the price on the product when necessary (if the cost_method is 'real'), so that a return or an inventory loss is made using the last value used for an outgoing valuation.
'''
product_obj = self.pool.get('product.product')
for move in self.browse(cr, uid, ids, context=context):
if move.product_id.cost_method == 'real' and move.location_dest_id.usage != 'internal':
if any([q.qty <= 0 for q in move.quant_ids]):
#if there is a negative quant, the standard price shouldn't be updated
continue
#get the average price of the move
#Note: here we can't use the quant.cost directly as we may have moved out 2 units (1 unit to 5€ and 1 unit to 7€) and in case of a product return of 1 unit, we can't know which of the 2 cost has to be used (5€ or 7€?). So at that time, thanks to the average valuation price we are storing we will svaluate it at 6€
average_valuation_price = 0.0
for q in move.quant_ids:
average_valuation_price += q.qty * q.cost
average_valuation_price = average_valuation_price / move.product_qty
product_obj.write(cr, uid, move.product_id.id, {'standard_price': average_valuation_price}, context=context)
self.write(cr, uid, move.id, {'price_unit': average_valuation_price}, context=context)

View File

@ -14,7 +14,7 @@
<record id="picking_type_dropship" model="stock.picking.type">
<field name="name">dropship</field>
<field name="sequence_id" ref="seq_picking_type_dropship"/>
<field name="code_id" ref="stock.picking_code_in"/>
<field name="code_id">incoming</field>
</record>

View File

@ -29,28 +29,20 @@ class stock_location_route(osv.osv):
_description = "Inventory Routes"
_order = 'sequence'
def _default_warehouse(self, cr, uid, context=None):
user = self.pool.get('res.users').browse(cr, uid, uid, context)
res = self.pool.get('stock.warehouse').search(cr, uid, [('company_id', '=', user.company_id.id)], limit=1, context=context)
return res and res[0] or False
_columns = {
'name': fields.char('Route Name', required=True),
'sequence': fields.integer('Sequence'),
'pull_ids': fields.one2many('procurement.rule', 'route_id', 'Pull Rules'),
'push_ids': fields.one2many('stock.location.path', 'route_id', 'Push Rules'),
'warehouse_id': fields.many2one('stock.warehouse', 'Warehouse'),
}
_defaults = {
'sequence': lambda self,cr,uid,ctx: 0,
'warehouse_id': _default_warehouse,
}
class stock_warehouse(osv.osv):
_inherit = 'stock.warehouse'
_columns = {
'route_id': fields.many2one('stock.location.route', 'Default Logistic Route', help='Default route through the warehouse', required=True),
'route_ids': fields.one2many('stock.location.route', 'warehouse_id', 'All Routes'),
'route_id': fields.many2one('stock.location.route', 'Default Routes', help='Default route through the warehouse', required=True),
}
@ -176,13 +168,10 @@ class procurement_order(osv.osv):
def _search_suitable_rule(self, cr, uid, procurement, 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'''
route_ids = [x.id for x in procurement.route_ids]
route_ids = [x.id for x in procurement.route_ids] + [x.id for x in procurement.product_id.route_ids]
res = self.pool.get('procurement.rule').search(cr, uid, domain + [('route_id', 'in', route_ids)], order = 'route_sequence, sequence', context=context)
if not res:
route_ids = [x.id for x in procurement.product_id.route_ids]
res = self.pool.get('procurement.rule').search(cr, uid, domain + [('route_id', 'in', route_ids)], order = 'route_sequence, sequence', context=context)
if not res:
res = self.pool.get('procurement.rule').search(cr, uid, domain, order='sequence', context=context)
res = self.pool.get('procurement.rule').search(cr, uid, domain, order='sequence', context=context)
return res

View File

@ -22,7 +22,7 @@
-->
<record id="route_warehouse0_mts" model='stock.location.route'>
<field name="name">Ship only</field>
<field name="name">Sale: Ship only</field>
<field name="sequence">20</field>
</record>
@ -79,7 +79,7 @@
</record>
<record id="route_warehouse0_pack" model='stock.location.route'>
<field name="name">Pack + Ship</field>
<field name="name">Sale: Pack + Ship</field>
<field name="sequence">15</field>
</record>
@ -128,7 +128,7 @@
<!-- Pick + pack + ship -->
<record id="route_warehouse0_pickpack" model='stock.location.route'>
<field name="name">Pick + Pack + Ship</field>
<field name="name">Sale: Pick + Pack + Ship</field>
<field name="sequence">20</field>
</record>

View File

@ -127,7 +127,7 @@
<field name="arch" type="xml">
<group name="inventory" position="after">
<group string="Routes">
<field name="route_ids" colspan="4" nolabel="1" />
<field name="route_ids" colspan="4" nolabel="1" widget="many2many_tags"/>
</group>
</group>
</field>
@ -142,7 +142,7 @@
<field name="arch" type="xml">
<xpath expr="//sheet" position="inside">
<group string="Strategy" colspan="4">
<field name="route_ids" colspan="4" nolabel="1" />
<field name="route_ids" colspan="4" nolabel="1" widget="many2many_tags"/>
</group>
</xpath>
</field>
@ -156,14 +156,6 @@
<xpath expr="//field[@name='partner_id']" position="after">
<field name="route_id"/>
</xpath>
<xpath expr="//group[last()]" position="after">
<separator string="All Associated Routes"/>
<field name="route_ids" nolabel="1" colspan="4">
<tree string="All Routes">
<field name="name"/>
</tree>
</field>
</xpath>
</field>
</record>
@ -174,7 +166,6 @@
<tree string="Routes">
<field name="sequence" widget="handle" />
<field name="name"/>
<field name="warehouse_id"/>
</tree>
</field>
</record>
@ -187,7 +178,6 @@
<form string="Route">
<field name="name" />
<field name="sequence" groups="base.group_no_one"/>
<field name="warehouse_id"/>
<group string="Push Rules" colspan="4" >
<field name="push_ids" colspan="4" nolabel="1"/>
</group>
@ -199,7 +189,7 @@
</record>
<record id="action_routes_form" model="ir.actions.act_window">
<field name="name">Logistic Routes</field>
<field name="name">Routes</field>
<field name="res_model">stock.location.route</field>
<field name="type">ir.actions.act_window</field>
<field name="view_type">form</field>
@ -207,9 +197,9 @@
<field name="view_id" ref="stock_location_route_tree" />
<field name="help" type="html">
<p class="oe_view_nocontent_create">
Click to add a logistic route.
Click to add a route.
</p>
<p>You can define here the main logistic routes that run through
<p>You can define here the main routes that run through
your warehouses and that define the flows of your products. These
routes can be assigned to a product, a product category or be fixed
on procurement or sales order. </p>

View File

@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
# 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 <http://www.gnu.org/licenses/>.
#
##############################################################################
import stock_multi_warehouse

View File

@ -0,0 +1,46 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
# 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 <http://www.gnu.org/licenses/>.
#
##############################################################################
{
'name': 'Multi-warehouse',
'version': '1.0',
'category': 'Warehousing',
'description': """
This module supplements the Warehouse application with demo data for multiple warehouses
========================================================================================
It creates 3 warehouses
""",
'author': 'OpenERP SA',
'images': [],
'depends': ['stock_complex_routes'],
'data': ['stock_multi_warehouse.yml'],
'demo': [
],
'installable': True,
'test': [
],
'auto_install': False,
}
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -0,0 +1,498 @@
-
Create Panama in Location structure
-
!record {model: stock.location, id: location_panama}:
name: Panama
location_id: stock.stock_location_locations
-
Create Location structure of Warehouse Panama Santiago District
-
!record {model: stock.location, id: location_panama_santiago}:
name: Panama Santiago District Warehouse
location_id: location_panama
-
Input
-
!record {model: stock.location, id: location_panama_santiago_input}:
name: Input
location_id: location_panama_santiago
-
Output
-
!record {model: stock.location, id: location_panama_santiago_output}:
name: Output
location_id: location_panama_santiago
-
Stock
-
!record {model: stock.location, id: location_panama_santiago_stock}:
name: Stock
location_id: location_panama_santiago
-
!record {model: stock.location, id: location_panama_santiago_stock_area1}:
name: Area1
location_id: location_panama_santiago_stock
-
!record {model: stock.location, id: location_panama_santiago_stock_area1_bin1}:
name: Bin1
location_id: location_panama_santiago_stock_area1
-
Create warehouse Panama Santiago District
-
!record {model: stock.warehouse, id: wh_panama_santiago}:
name: Panama Santiago District Warehouse
lot_stock_id: location_panama_santiago
-
Create picking type in for this warehouse
-
!record {model: stock.picking.type, id: picking_type_santiago_in}:
name: Reception
code_id: incoming
sequence_id: stock.seq_picking_type_in
warehouse_id: wh_panama_santiago
default_location_dest_id: location_panama_santiago_input
-
Create picking type out for this warehouse
-
!record {model: stock.picking.type, id: picking_type_santiago_out}:
name: Deliveries
code_id: outgoing
sequence_id: stock.seq_picking_type_out
warehouse_id: wh_panama_santiago
default_location_src_id: location_panama_santiago_output
-
Create picking type internal for this warehouse
-
!record {model: stock.picking.type, id: picking_type_santiago_internal}:
name: Deliveries
code_id: internal
sequence_id: stock.seq_picking_type_internal
warehouse_id: wh_panama_santiago
default_location_src_id: location_panama_santiago_input
default_location_dest_id: location_panama_santiago_stock
-
Create Location structure of Warehouse Panama Main
-
!record {model: stock.location, id: location_panama_main}:
name: Panama Main Warehouse
location_id: location_panama
-
Input
-
!record {model: stock.location, id: location_panama_main_input}:
name: Input
location_id: location_panama_main
-
Output
-
!record {model: stock.location, id: location_panama_main_output}:
name: Output
location_id: location_panama_main
-
Stock
-
!record {model: stock.location, id: location_panama_main_stock}:
name: Stock
location_id: location_panama_main
-
!record {model: stock.location, id: location_panama_main_stock_area2}:
name: Area2
location_id: location_panama_main_stock
-
Create warehouse Panama main
-
!record {model: stock.warehouse, id: wh_panama_main}:
name: Panama Main Warehouse
lot_stock_id: location_panama_main
-
Create picking type in for warehouse panama main
-
!record {model: stock.picking.type, id: picking_type_main_in}:
name: Reception
code_id: incoming
sequence_id: stock.seq_picking_type_in
warehouse_id: wh_panama_main
default_location_dest_id: location_panama_main_input
-
Create picking type out for warehouse panama main
-
!record {model: stock.picking.type, id: picking_type_main_out}:
name: Deliveries
code_id: outgoing
sequence_id: stock.seq_picking_type_out
warehouse_id: wh_panama_main
default_location_src_id: location_panama_main_output
-
Create picking type internal for warehouse panama main
-
!record {model: stock.picking.type, id: picking_type_main_internal}:
name: Deliveries
code_id: internal
sequence_id: stock.seq_picking_type_internal
warehouse_id: wh_panama_main
default_location_src_id: location_panama_main_input
default_location_dest_id: location_panama_main_stock
-
Create Location structure of Warehouse Dubai
-
!record {model: stock.location, id: location_panama_main}:
name: Panama Main Warehouse
location_id: location_panama
-
Create United Arab Emirates in Location structure
-
!record {model: stock.location, id: location_uae}:
name: United Arab Emirates
location_id: stock.stock_location_locations
-
Create Location structure of Dubai Warehouse
-
!record {model: stock.location, id: location_dubai}:
name: Dubai Warehouse
location_id: location_uae
child_ids:
- name: Input
- name: Output
- name: Stock
child_ids:
- name: Area3
-
Create Dubai warehouse
-
!record {model: stock.warehouse, id: wh_dubai}:
name: Dubai Warehouse
lot_stock_id: location_dubai
-
Create Ship Route for main panama warehouse
-
!record {model: stock.location.route, id: route_wh_panama_main_push}:
name: Ship main panama
pull_ids:
- invoice_state: none
location_id: location_panama_main_output
location_src_id: location_panama_main_stock
name: Panama Main Stock -> Panama Main Output
procure_method: make_to_stock
picking_type_id: picking_type_main_internal
action: move
- invoice_state: none
location_id: stock.stock_location_customers
location_src_id: location_panama_main_output
name: Panama Main Output -> Customer
picking_type_id: picking_type_main_out
procure_method: make_to_order
action: move
-
Create Ship Route for panama santiago warehouse
-
!record {model: stock.location.route, id: route_wh_panama_santiago_push}:
name: Ship panama santiago
pull_ids:
- invoice_state: none
location_id: location_panama_santiago_output
location_src_id: location_panama_santiago_stock
name: Santiago Stock -> Santiago Output
procure_method: make_to_stock
picking_type_id: picking_type_santiago_internal
action: move
- invoice_state: none
location_id: stock.stock_location_customers
location_src_id: location_panama_santiago_output
name: Santiago Output -> Customer
picking_type_id: picking_type_santiago_out
procure_method: make_to_order
action: move
-
Create Products
-
!record {model: product.product, id: product_A}:
name: Product A
type: consu
categ_id: product.product_category_1
list_price: 100.0
standard_price: 70.0
uom_id: product.product_uom_unit
uom_po_id: product.product_uom_unit
-
!record {model: product.product, id: product_B}:
name: Product B
type: consu
categ_id: product.product_category_1
list_price: 150.0
standard_price: 150.0
uom_id: product.product_uom_unit
uom_po_id: product.product_uom_unit
-
!record {model: product.product, id: product_C}:
name: Product C
type: consu
categ_id: product.product_category_1
list_price: 200.0
standard_price: 150.0
uom_id: product.product_uom_unit
uom_po_id: product.product_uom_unit
-
!record {model: product.product, id: product_D}:
name: Product D
type: consu
categ_id: product.product_category_1
list_price: 300.0
standard_price: 280.0
uom_id: product.product_uom_unit
uom_po_id: product.product_uom_unit
-
!record {model: product.product, id: product_E}:
name: Product E
type: product
categ_id: product.product_category_1
list_price: 10.0
standard_price: 10.0
uom_id: product.product_uom_unit
uom_po_id: product.product_uom_unit
-
!record {model: product.product, id: product_F}:
name: Product F
type: product
categ_id: product.product_category_1
list_price: 20.0
standard_price: 20.0
uom_id: product.product_uom_unit
uom_po_id: product.product_uom_unit
-
!record {model: product.product, id: product_G}:
name: Product G
type: product
categ_id: product.product_category_1
list_price: 30.0
standard_price: 30.0
uom_id: product.product_uom_unit
uom_po_id: product.product_uom_unit
-
!record {model: product.product, id: product_H}:
name: Product H
type: product
categ_id: product.product_category_1
list_price: 40.0
standard_price: 40.0
uom_id: product.product_uom_unit
uom_po_id: product.product_uom_unit
-
Create Lot
-
!record {model: stock.production.lot, id: lot001}:
name: Lot001
product_id: product_C
-
!record {model: stock.production.lot, id: lot002}:
name: Lot002
product_id: product_C
-
!record {model: stock.production.lot, id: lot003}:
name: Lot003
product_id: product_C
-
!record {model: stock.production.lot, id: lot004}:
name: Lot004
product_id: product_C
-
Fill inventory
-
I create an inventory for Location Stock1
-
!record {model: stock.inventory, id: inventory_stock1}:
name: Inventory Stock 1
location_id: location_panama_santiago_stock
-
I create the wizard to confirm the inventory
-
!record {model: stock.fill.inventory, id: inventory_wizard}:
set_stock_zero: False
-
I fill Stock1 inventory
-
!python {model: stock.inventory.line}: |
context = {'active_ids': [ref('inventory_stock1')]}
self.pool.get('stock.fill.inventory').fill_inventory(cr, uid, [ref('inventory_wizard')], context=context)
-
I add 2 inventory line for product A and B and say i have 6 and 12 products in stock
-
!record {model: stock.inventory.line, id: inventory_stock1line_productA}:
inventory_id: inventory_stock1
product_id: product_A
product_qty: 6
location_id: location_panama_santiago_stock
-
!record {model: stock.inventory.line, id: inventory_stock1line_productB}:
inventory_id: inventory_stock1
product_id: product_B
product_qty: 12
location_id: location_panama_santiago_stock
-
I confirm Stock1 inventory
-
!python {model: stock.inventory}: |
self.action_done(cr, uid, [ref('inventory_stock1')], context=context)
-
I create an inventory for Location Stock2
-
!record {model: stock.inventory, id: inventory_stock2}:
name: Inventory Stock 2
location_id: location_panama_main_stock
-
I fill Stock2 inventory
-
!python {model: stock.inventory.line}: |
context = {'active_ids': [ref('inventory_stock2')]}
self.pool.get('stock.fill.inventory').fill_inventory(cr, uid, [ref('inventory_wizard')], context=context)
-
I add 4 inventory lines for product A, B, C, C and say i have 8, 120, 5, 15 products in stock
-
!record {model: stock.inventory.line, id: inventory_stock2line_productA}:
inventory_id: inventory_stock2
product_id: product_A
product_qty: 8
location_id: location_panama_main_stock
-
!record {model: stock.inventory.line, id: inventory_stock2line_productB}:
inventory_id: inventory_stock2
product_id: product_B
product_qty: 120
location_id: location_panama_main_stock
-
!record {model: stock.inventory.line, id: inventory_stock2line_productC_1}:
inventory_id: inventory_stock2
product_id: product_C
product_qty: 5
location_id: location_panama_main_stock
prod_lot_id: lot003
-
!record {model: stock.inventory.line, id: inventory_stock2line_productC_2}:
inventory_id: inventory_stock2
product_id: product_C
product_qty: 15
location_id: location_panama_main_stock
prod_lot_id: lot004
-
I confirm Stock2 inventory
-
!python {model: stock.inventory}: |
self.action_done(cr, uid, [ref('inventory_stock2')], context=context)
-
I create an inventory for Location Area1
-
!record {model: stock.inventory, id: inventory_stock1_area1}:
name: Inventory Stock1 area 1
location_id: location_panama_santiago_stock_area1
-
I fill Area1 inventory
-
!python {model: stock.inventory.line}: |
context = {'active_ids': [ref('inventory_stock1_area1')]}
self.pool.get('stock.fill.inventory').fill_inventory(cr, uid, [ref('inventory_wizard')], context=context)
-
I add 2 inventory lines for product D, D and say i have 4, 5 products in stock
-
!record {model: stock.inventory.line, id: inventory_stock1line_productD_1}:
inventory_id: inventory_stock1_area1
product_id: product_D
product_qty: 4
location_id: location_panama_santiago_stock_area1
-
!record {model: stock.inventory.line, id: inventory_stock1line_productD_2}:
inventory_id: inventory_stock1_area1
product_id: product_D
product_qty: 5
location_id: location_panama_santiago_stock_area1
-
I confirm Area1 inventory
-
!python {model: stock.inventory}: |
self.action_done(cr, uid, [ref('inventory_stock1_area1')], context=context)
-
I create an inventory for Location Area 2
-
!record {model: stock.inventory, id: inventory_stock2_area2}:
name: Inventory Stock2 area 2
location_id: location_panama_main_stock_area2
-
I fill Area2 inventory
-
!python {model: stock.inventory.line}: |
context = {'active_ids': [ref('inventory_stock2_area2')]}
self.pool.get('stock.fill.inventory').fill_inventory(cr, uid, [ref('inventory_wizard')], context=context)
-
I add 2 inventory lines for product D, D and say i have 2, 3 products in stock
-
!record {model: stock.inventory.line, id: inventory_stock2line_productD_1}:
inventory_id: inventory_stock2_area2
product_id: product_D
product_qty: 2
location_id: location_panama_main_stock_area2
-
!record {model: stock.inventory.line, id: inventory_stock2line_productD_2}:
inventory_id: inventory_stock2_area2
product_id: product_D
product_qty: 3
location_id: location_panama_main_stock_area2
-
I confirm Area2 inventory
-
!python {model: stock.inventory}: |
self.action_done(cr, uid, [ref('inventory_stock2_area2')], context=context)
-
I create an inventory for Location Bin1
-
!record {model: stock.inventory, id: inventory_stock1_area1_bin1}:
name: Inventory Stock1 area1 bin 1
location_id: location_panama_santiago_stock_area1_bin1
-
I fill Bin1 inventory
-
!python {model: stock.inventory.line}: |
context = {'active_ids': [ref('inventory_stock1_area1_bin1')]}
self.pool.get('stock.fill.inventory').fill_inventory(cr, uid, [ref('inventory_wizard')], context=context)
-
I add 2 inventory lines for product C, C and say i have 3, 14 products in stock
-
!record {model: stock.inventory.line, id: inventory_stock1line_productC_1}:
inventory_id: inventory_stock1_area1_bin1
product_id: product_C
product_qty: 3
location_id: location_panama_santiago_stock_area1_bin1
prod_lot_id: lot001
-
!record {model: stock.inventory.line, id: inventory_stock1line_productC_2}:
inventory_id: inventory_stock1_area1_bin1
product_id: product_C
product_qty: 14
location_id: location_panama_santiago_stock_area1_bin1
prod_lot_id: lot002
-
I confirm Bin1 inventory
-
!python {model: stock.inventory}: |
self.action_done(cr, uid, [ref('inventory_stock1_area1_bin1')], context=context)