[MERGE] merge with parent
bzr revid: rma@tinyerp.com-20130904055303-v6sbe6bknw6m1lus
This commit is contained in:
@@ -123,10 +123,9 @@
|
||||
-
|
||||
Reception is ready for process so now done the reception.
|
||||
-
|
||||
!python {model: stock.partial.picking}: |
|
||||
pick_ids = self.pool.get('purchase.order').browse(cr, uid, ref("purchase_order_001")).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_id = self.pool.get('purchase.order').browse(cr, uid, ref("purchase_order_001")).picking_ids[0]
|
||||
picking_id.do_partial(context=context)
|
||||
-
|
||||
I check the Stock Interim account (Received) is credited successfully.
|
||||
-
|
||||
@@ -196,30 +195,30 @@
|
||||
product_qty: 1.0
|
||||
product_uom: product.product_uom_unit
|
||||
location_dest_id: stock.stock_location_customers
|
||||
invoice_state: 2binvoiced
|
||||
move_type: direct
|
||||
type: out
|
||||
picking_type_id: stock.picking_type_out
|
||||
-
|
||||
I need to check the availability of the product, So I make my picking order for processing later.
|
||||
-
|
||||
!python {model: stock.picking}: |
|
||||
self.draft_force_assign(cr, uid, [ref("stock_picking_out001")], {"lang": "en_US", "search_default_available":
|
||||
1, "tz": False, "active_model": "ir.ui.menu", "contact_display": "partner",
|
||||
"active_ids": [ref("stock.menu_action_picking_tree")], "active_id": ref("stock.menu_action_picking_tree"),
|
||||
})
|
||||
self.action_confirm(cr, uid, [ref('stock_picking_out001')], context=context)
|
||||
|
||||
-
|
||||
I check the product availability, Product is available in the stock and ready to be sent.
|
||||
-
|
||||
!python {model: stock.picking}: |
|
||||
self.action_assign(cr, uid, [ref("stock_picking_out001")], {"lang": "en_US", "search_default_available":
|
||||
1, "tz": False, "active_model": "ir.ui.menu", "contact_display": "partner",
|
||||
"active_ids": [ref("stock.menu_action_picking_tree")], "active_id": ref("stock.menu_action_picking_tree"),
|
||||
})
|
||||
picking = self.browse(cr, uid, ref("stock_picking_out001"))
|
||||
assert picking.state == "confirmed", "Picking should be confirmed."
|
||||
for move_line in picking.move_lines:
|
||||
assert move_line.state == "confirmed", "Move should be confirmed."
|
||||
|
||||
-
|
||||
I process the delivery.
|
||||
-
|
||||
!python {model: stock.partial.picking}: |
|
||||
partial_id = self.create(cr, uid, {}, context={'active_model':'stock.picking','active_ids':[ref('stock_picking_out001')]})
|
||||
self.do_partial(cr, uid, [partial_id])
|
||||
!python {model: stock.picking}: |
|
||||
picking = self.pool.get('stock.picking').browse(cr, uid, ref("stock_picking_out001"))
|
||||
picking.do_partial(context=context)
|
||||
-
|
||||
I check Stock Interim account (Delivery) is debited successfully.
|
||||
-
|
||||
|
||||
@@ -131,10 +131,9 @@
|
||||
-
|
||||
Reception is ready for process so now done the reception.
|
||||
-
|
||||
!python {model: stock.partial.picking}: |
|
||||
pick_ids = self.pool.get('purchase.order').browse(cr, uid, ref("purchase_order_001_fifo")).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_id = self.pool.get('purchase.order').browse(cr, uid, ref("purchase_order_001_fifo")).picking_ids[0]
|
||||
picking_id.do_partial(context=context)
|
||||
-
|
||||
I check the Stock Interim account (Received) is credit successfully.
|
||||
-
|
||||
@@ -198,30 +197,28 @@
|
||||
product_id: product_fifo_anglo_saxon
|
||||
product_qty: 1.0
|
||||
location_dest_id: stock.stock_location_customers
|
||||
invoice_state: 2binvoiced
|
||||
move_type: direct
|
||||
type: out
|
||||
picking_type_id: stock.picking_type_out
|
||||
-
|
||||
I need to check the availability of the product, So I make my picking order for processing later.
|
||||
-
|
||||
!python {model: stock.picking}: |
|
||||
self.draft_force_assign(cr, uid, [ref("stock_picking_out001_fifo")], {"lang": "en_US", "search_default_available":
|
||||
1, "tz": False, "active_model": "ir.ui.menu", "contact_display": "partner",
|
||||
"active_ids": [ref("stock.menu_action_picking_tree")], "active_id": ref("stock.menu_action_picking_tree"),
|
||||
})
|
||||
self.action_confirm(cr, uid, [ref('stock_picking_out001_fifo')], context=context)
|
||||
-
|
||||
I check the product availability, Product is available in the stock and ready to be sent.
|
||||
-
|
||||
!python {model: stock.picking}: |
|
||||
self.action_assign(cr, uid, [ref("stock_picking_out001_fifo")], {"lang": "en_US", "search_default_available":
|
||||
1, "tz": False, "active_model": "ir.ui.menu", "contact_display": "partner",
|
||||
"active_ids": [ref("stock.menu_action_picking_tree")], "active_id": ref("stock.menu_action_picking_tree"),
|
||||
})
|
||||
picking = self.browse(cr, uid, ref("stock_picking_out001_fifo"))
|
||||
assert picking.state == "confirmed", "Picking should be confirmed."
|
||||
for move_line in picking.move_lines:
|
||||
assert move_line.state == "confirmed", "Move should be confirmed."
|
||||
-
|
||||
I process the delivery.
|
||||
-
|
||||
!python {model: stock.partial.picking}: |
|
||||
partial_id = self.create(cr, uid, {}, context={'active_model':'stock.picking','active_ids':[ref('stock_picking_out001_fifo')]})
|
||||
self.do_partial(cr, uid, [partial_id])
|
||||
!python {model: stock.picking}: |
|
||||
picking = self.pool.get('stock.picking').browse(cr, uid, ref("stock_picking_out001_fifo"))
|
||||
picking.do_partial(context=context)
|
||||
-
|
||||
I check Stock Interim account (Delivery) is debited successfully.
|
||||
-
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,17 +706,20 @@ 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),
|
||||
'type': 'out',
|
||||
'picking_type_id': picking_type.id,
|
||||
'company_id': order.company_id.id,
|
||||
'move_type': 'direct',
|
||||
'note': order.note or "",
|
||||
}, 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':
|
||||
|
||||
@@ -213,7 +213,7 @@ class add_product(osv.osv_memory):
|
||||
'name':'%s (Added)' %order_id.name,
|
||||
'move_lines':[],
|
||||
'state':'draft',
|
||||
'type':'out',
|
||||
'picking_type_id': self.pool.get('ir.model.data').get_object(cr, uid, 'stock', 'picking_type_out', context=context).id,
|
||||
'date':date_cur
|
||||
})
|
||||
stock_move_obj.create(cr, uid, {
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
!python {model: procurement.order}: |
|
||||
self.run_scheduler(cr, uid)
|
||||
-
|
||||
I check that procurement order is done
|
||||
I check that procurement order is in exception, as at first there isn't any suitable rule
|
||||
-
|
||||
!python {model: procurement.order}: |
|
||||
proc_order = self.browse(cr, uid, ref('procurement_order0'), context=context)
|
||||
assert proc_order.state == 'done'
|
||||
assert proc_order.state == 'exception'
|
||||
|
||||
@@ -14,8 +14,7 @@
|
||||
!record {model: product.product, id: product_product_hrmanger0}:
|
||||
categ_id: product.product_category_6
|
||||
mes_type: fixed
|
||||
name: HR Manger
|
||||
procure_method: make_to_stock
|
||||
name: HR Manager
|
||||
standard_price: 1.0
|
||||
supply_method: buy
|
||||
type: service
|
||||
|
||||
@@ -81,7 +81,6 @@ Dashboard / Reports for Purchase Management will include:
|
||||
'test/ui/duplicate_order.yml',
|
||||
'test/ui/delete_order.yml',
|
||||
'test/average_price.yml',
|
||||
'test/lifo_price.yml',
|
||||
],
|
||||
'demo': [
|
||||
'purchase_order_demo.yml',
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
+23
-11
@@ -150,6 +150,10 @@ class purchase_order(osv.osv):
|
||||
limit=1)
|
||||
return res and res[0] or False
|
||||
|
||||
def _get_picking_in(self, cr, uid, context=None):
|
||||
obj_data = self.pool.get('ir.model.data')
|
||||
return obj_data.get_object_reference(cr, uid, 'stock','picking_type_in')[1]
|
||||
|
||||
STATE_SELECTION = [
|
||||
('draft', 'Draft PO'),
|
||||
('sent', 'RFQ Sent'),
|
||||
@@ -184,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)]}),
|
||||
@@ -230,6 +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", 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,
|
||||
@@ -241,7 +247,8 @@ class purchase_order(osv.osv):
|
||||
'pricelist_id': lambda self, cr, uid, context: context.get('partner_id', False) and self.pool.get('res.partner').browse(cr, uid, context['partner_id']).property_product_pricelist_purchase.id,
|
||||
'company_id': lambda self,cr,uid,c: self.pool.get('res.company')._company_default_get(cr, uid, 'purchase.order', context=c),
|
||||
'journal_id': _get_journal,
|
||||
'currency_id': lambda self, cr, uid, context: self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.currency_id.id
|
||||
'currency_id': lambda self, cr, uid, context: self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.currency_id.id,
|
||||
'picking_type_id': _get_picking_in,
|
||||
}
|
||||
_sql_constraints = [
|
||||
('name_uniq', 'unique(name, company_id)', 'Order Reference must be unique per Company!'),
|
||||
@@ -295,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')
|
||||
@@ -380,6 +391,8 @@ class purchase_order(osv.osv):
|
||||
'''
|
||||
This function returns an action that display existing picking orders of given purchase order ids.
|
||||
'''
|
||||
if context is None:
|
||||
context = {}
|
||||
mod_obj = self.pool.get('ir.model.data')
|
||||
pick_ids = []
|
||||
for po in self.browse(cr, uid, ids, context=context):
|
||||
@@ -847,7 +860,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',
|
||||
@@ -1233,7 +1246,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:
|
||||
@@ -1269,7 +1281,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,
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
@@ -196,8 +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" 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>
|
||||
@@ -240,7 +245,7 @@
|
||||
<page string="Deliveries & 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"/>
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
@@ -13,15 +13,12 @@
|
||||
</record>
|
||||
|
||||
<record id="stock_picking_in_inherit_purchase" model="ir.ui.view">
|
||||
<field name="name">Incoming Picking Inherited</field>
|
||||
<field name="name">Picking Inherited</field>
|
||||
<field name="model">stock.picking</field>
|
||||
<field name="inherit_id" ref="stock.view_picking_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='date']" position="before">
|
||||
<field name="purchase_id" domain="[('invoice_method','=','picking')]" 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 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>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
@@ -55,10 +55,9 @@
|
||||
-
|
||||
Process the reception of purchase order 1
|
||||
-
|
||||
!python {model: stock.partial.picking}: |
|
||||
!python {model: stock.picking}: |
|
||||
pick_ids = self.pool.get('purchase.order').browse(cr, uid, ref("purchase_order_average1")).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])
|
||||
self.do_partial(cr, uid, [pick_ids[0].id])
|
||||
-
|
||||
Check the standard price of the product (average icecream).
|
||||
-
|
||||
@@ -69,10 +68,9 @@
|
||||
-
|
||||
Process the reception of purchase order 2
|
||||
-
|
||||
!python {model: stock.partial.picking}: |
|
||||
!python {model: stock.picking}: |
|
||||
pick_ids = self.pool.get('purchase.order').browse(cr, uid, ref("purchase_order_average2")).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])
|
||||
self.do_partial(cr, uid, [pick_ids[0].id])
|
||||
-
|
||||
Check the standard price
|
||||
-
|
||||
@@ -82,7 +80,7 @@
|
||||
Create picking to send some goods
|
||||
-
|
||||
!record {model: stock.picking, id: outgoing_average_shipment}:
|
||||
type: out
|
||||
picking_type_id: stock.picking_type_out
|
||||
-
|
||||
Create move for picking
|
||||
-
|
||||
@@ -91,18 +89,12 @@
|
||||
product_id: product_average_icecream
|
||||
product_uom: product.product_uom_kgm
|
||||
product_qty: 20.0
|
||||
type: out
|
||||
-
|
||||
I assign this outgoing shipment
|
||||
I assign this outgoing shipment and process the delivery
|
||||
-
|
||||
!python {model: stock.picking}: |
|
||||
self.action_assign(cr, uid, [ref("outgoing_average_shipment")])
|
||||
-
|
||||
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_average_shipment")], 'default_type':'out'})
|
||||
self.do_partial(cr, uid, [partial_id])
|
||||
self.do_partial(cr, uid, [ref("outgoing_average_shipment")])
|
||||
-
|
||||
Check the standard price (60 * 10 + 30 * 80) / 40 = 75.0 did not change
|
||||
-
|
||||
@@ -129,10 +121,9 @@
|
||||
-
|
||||
Process the reception of purchase order 3 in grams
|
||||
-
|
||||
!python {model: stock.partial.picking}: |
|
||||
!python {model: stock.picking}: |
|
||||
pick_ids = self.pool.get('purchase.order').browse(cr, uid, ref("purchase_order_average3")).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])
|
||||
self.do_partial(cr, uid, [pick_ids[0].id])
|
||||
-
|
||||
Check price is (75.0*20 + 500) / 20.5 = 97.56097561
|
||||
-
|
||||
|
||||
@@ -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,)
|
||||
|
||||
@@ -11,9 +11,9 @@
|
||||
-
|
||||
Now I create an invoice for order on reception.
|
||||
-
|
||||
!python {model: stock.picking}: |
|
||||
pick_ids = self.search(cr, uid, [('purchase_id','=',ref('purchase_order_2'))])
|
||||
self.action_invoice_create(cr, uid, pick_ids, ref('account.expenses_journal'))
|
||||
!python {model: purchase.order}: |
|
||||
pick_ids = [self.browse(cr, uid, ref("purchase_order_2")).id]
|
||||
self.action_invoice_create(cr, uid, pick_ids)
|
||||
-
|
||||
I check that the invoice of order.
|
||||
-
|
||||
|
||||
@@ -45,10 +45,9 @@
|
||||
-
|
||||
Reception is ready for process so now done the reception.
|
||||
-
|
||||
!python {model: stock.partial.picking}: |
|
||||
pick_ids = self.pool.get('purchase.order').browse(cr, uid, ref("purchase_order_1")).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_ids = self.pool.get('purchase.order').browse(cr, uid, ref("purchase_order_1")).picking_ids[0]
|
||||
pick_ids.do_partial(context=context)
|
||||
-
|
||||
I check that purchase order is shipped.
|
||||
-
|
||||
|
||||
@@ -6,13 +6,8 @@
|
||||
!record {model: procurement.order, id: procurement_order_testcase0}:
|
||||
location_id: stock.stock_location_stock
|
||||
name: Test scheduler for RFQ
|
||||
procure_method: make_to_order
|
||||
product_id: product.product_product_45
|
||||
product_qty: 15.0
|
||||
-
|
||||
I confirm on procurement order.
|
||||
-
|
||||
!workflow {model: procurement.order, action: button_confirm, ref: procurement_order_testcase0}
|
||||
-
|
||||
I run the scheduler.
|
||||
-
|
||||
|
||||
@@ -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,
|
||||
|
||||
+2
-11
@@ -658,7 +658,7 @@ class sale_order(osv.osv):
|
||||
for order in self.browse(cr, uid, ids, context=context):
|
||||
proc_ids = []
|
||||
group_id = self.pool.get("procurement.group").create(cr, uid, {
|
||||
'name': order.name, 'partner_id': order.partner_shipping_id.id
|
||||
'name': order.name, 'partner_id': order.partner_shipping_id.id, 'move_type': order.picking_policy
|
||||
}, context=context)
|
||||
order.write({'procurement_group_id': group_id}, context=context)
|
||||
for line in order.order_line:
|
||||
@@ -1072,14 +1072,5 @@ class procurement_order(osv.osv):
|
||||
_inherit = 'procurement.order'
|
||||
_columns = {
|
||||
'sale_line_id': fields.many2one('sale.order.line', string='Sale Order Line'),
|
||||
'invoice_state': fields.selection(
|
||||
[
|
||||
("invoiced", "Invoiced"),
|
||||
("2binvoiced", "To Be Invoiced"),
|
||||
("none", "Not Applicable")
|
||||
], "Invoice Control", required=True),
|
||||
}
|
||||
_defaults = {
|
||||
'invoice_state': 'none',
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -20,9 +20,7 @@
|
||||
##############################################################################
|
||||
|
||||
import sale_stock
|
||||
import stock
|
||||
import report
|
||||
import company
|
||||
import res_config
|
||||
import wizard
|
||||
|
||||
|
||||
@@ -51,7 +51,6 @@ You can choose flexible invoicing methods:
|
||||
'security/sale_stock_security.xml',
|
||||
'security/ir.model.access.csv',
|
||||
'company_view.xml',
|
||||
'wizard/stock_invoice_onshipping_view.xml',
|
||||
'sale_stock_view.xml',
|
||||
'sale_stock_workflow.xml',
|
||||
'stock_view.xml',
|
||||
|
||||
@@ -61,7 +61,14 @@ class sale_order(osv.osv):
|
||||
if move.procurement_id and move.procurement_id.sale_line_id:
|
||||
res.add(move.procurement_id.sale_line_id.order_id.id)
|
||||
return list(res)
|
||||
|
||||
|
||||
def _get_orders_procurements(self, cr, uid, ids, context=None):
|
||||
res = set()
|
||||
for proc in self.pool.get('procurement.order').browse(cr, uid, ids, context=context):
|
||||
if proc.sale_line_id:
|
||||
res.add(proc.sale_line_id.order_id.id)
|
||||
return list(res)
|
||||
|
||||
def _get_picking_ids(self, cr, uid, ids, name, args, context=None):
|
||||
res = {}
|
||||
for sale in self.browse(cr, uid, ids, context=context):
|
||||
@@ -107,7 +114,10 @@ class sale_order(osv.osv):
|
||||
('prepaid', 'Before Delivery'),
|
||||
], 'Create Invoice', required=True, readonly=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]},
|
||||
help="""On demand: A draft invoice can be created from the sales order when needed. \nOn delivery order: A draft invoice can be created from the delivery order when the products have been delivered. \nBefore delivery: A draft invoice is created from the sales order and must be paid before the products can be delivered."""),
|
||||
'shipped': fields.function(_get_shipped, string='Delivered', type='boolean', store={'stock.move': (_get_orders, ['state'], 10)}),
|
||||
'shipped': fields.function(_get_shipped, string='Delivered', type='boolean', store={
|
||||
'stock.move': (_get_orders, ['state'], 10),
|
||||
'procurement.order': (_get_orders_procurements, ['state'], 10)
|
||||
}),
|
||||
'warehouse_id': fields.many2one('stock.warehouse', 'Warehouse', required=True),
|
||||
'picking_ids': fields.function(_get_picking_ids, method=True, type='one2many', relation='stock.picking', string='Picking associated to this sale'),
|
||||
}
|
||||
@@ -158,11 +168,12 @@ class sale_order(osv.osv):
|
||||
# if order_policy<>picking: super()
|
||||
# else: call invoice_on_picking_method()
|
||||
def action_invoice_create(self, cr, uid, ids, grouped=False, states=['confirmed', 'done', 'exception'], date_invoice = False, context=None):
|
||||
picking_obj = self.pool.get('stock.picking')
|
||||
move_obj = self.pool.get("stock.move")
|
||||
res = super(sale_order,self).action_invoice_create(cr, uid, ids, grouped=grouped, states=states, date_invoice = date_invoice, context=context)
|
||||
for order in self.browse(cr, uid, ids, context=context):
|
||||
if order.order_policy == 'picking':
|
||||
picking_obj.write(cr, uid, map(lambda x: x.id, order.picking_ids), {'invoice_state': 'invoiced'})
|
||||
for picking in order.picking_ids:
|
||||
move_obj.write(cr, uid, [x.id for x in picking.move_lines], {'invoice_state': 'invoiced'}, context=context)
|
||||
return res
|
||||
|
||||
def action_cancel(self, cr, uid, ids, context=None):
|
||||
@@ -223,7 +234,7 @@ class sale_order(osv.osv):
|
||||
self.pool.get('sale.order.line').write(cr, uid, write_done_ids, {'state': 'done'})
|
||||
if write_cancel_ids:
|
||||
self.pool.get('sale.order.line').write(cr, uid, write_cancel_ids, {'state': 'exception'})
|
||||
|
||||
|
||||
if mode == 'finished':
|
||||
return finished
|
||||
elif mode == 'canceled':
|
||||
@@ -265,7 +276,7 @@ class sale_order(osv.osv):
|
||||
|
||||
class sale_order_line(osv.osv):
|
||||
_inherit = 'sale.order.line'
|
||||
|
||||
|
||||
def _number_packages(self, cr, uid, ids, field_name, arg, context=None):
|
||||
res = {}
|
||||
for line in self.browse(cr, uid, ids, context=context):
|
||||
|
||||
@@ -104,21 +104,7 @@
|
||||
groups="sale.group_delivery_invoice_address"
|
||||
/>
|
||||
|
||||
<record id="view_picking_inherit_form2" model="ir.ui.view">
|
||||
<field name="name">stock.picking.form.inherit</field>
|
||||
<field name="model">stock.picking</field>
|
||||
<field name="inherit_id" ref="stock.view_picking_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//button[@name='do_prepare_partial']" position="after">
|
||||
<button name="%(action_stock_invoice_onshipping)d" string="Create Invoice" attrs="{'invisible': ['|',('state','<>','done'),('invoice_state','<>','2binvoiced')]}" type="action" class="oe_highlight" groups="base.group_user"/>
|
||||
<button name="%(action_stock_invoice_onshipping)d" string="Refund Invoice" attrs="{'invisible': ['|',('state','<>','done'),('invoice_state','<>','invoiced')]}" type="action" class="oe_highlight" groups="base.group_user" context="{'inv_type': 'out_refund'}"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='move_type']" position="after">
|
||||
<field name="invoice_state" groups="account.group_account_invoice"/>
|
||||
</xpath>
|
||||
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_picking_internal_search_inherit" model="ir.ui.view">
|
||||
<field name="name">stock.picking.search.inherit</field>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -31,20 +31,14 @@
|
||||
from openerp.tools import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT
|
||||
order = self.browse(cr, uid, ref("sale.sale_order_6"))
|
||||
for order_line in order.order_line:
|
||||
procurement = order_line.procurement_id
|
||||
sale_order_date = self.date_to_datetime(cr, uid, order.date_order, context)
|
||||
date_planned = datetime.strptime(sale_order_date, DEFAULT_SERVER_DATETIME_FORMAT) + relativedelta(days=order_line.delay or 0.0)
|
||||
procurement = order_line.procurement_ids[0]
|
||||
date_planned = datetime.strptime(order.date_order, DEFAULT_SERVER_DATETIME_FORMAT) + relativedelta(days=order_line.delay or 0.0)
|
||||
date_planned = (date_planned - timedelta(days=order.company_id.security_lead)).strftime(DEFAULT_SERVER_DATETIME_FORMAT)
|
||||
assert procurement.date_planned == date_planned, "Scheduled date is not correspond."
|
||||
assert procurement.product_id.id == order_line.product_id.id, "Product is not correspond."
|
||||
assert procurement.product_qty == order_line.product_uom_qty, "Qty is not correspond."
|
||||
assert procurement.product_uom.id == order_line.product_uom.id, "UOM is not correspond."
|
||||
assert procurement.procure_method == order_line.type, "Procurement method is not correspond."
|
||||
-
|
||||
I run the scheduler.
|
||||
-
|
||||
!python {model: procurement.order}: |
|
||||
self.run_scheduler(cr, uid)
|
||||
assert procurement.move_ids[0].procure_method == order_line.type, "Procurement method is not correspond."
|
||||
-
|
||||
I check the details of delivery order after confirmed quotation.
|
||||
-
|
||||
@@ -57,18 +51,16 @@
|
||||
for picking in sale_order.picking_ids:
|
||||
assert picking.state == "auto" or "confirmed", "Delivery order should be in 'Waitting Availability' state."
|
||||
assert picking.origin == sale_order.name,"Origin of Delivery order is not correspond with sequence number of sale order."
|
||||
assert picking.type == 'out',"Shipment should be Outgoing."
|
||||
assert picking.picking_type_id == self.pool.get('ir.model.data').get_object(cr, uid, 'stock', 'picking_type_out', context=context),"Shipment should be Outgoing."
|
||||
assert picking.move_type == sale_order.picking_policy,"Delivery Method is not corresponding with delivery method of sale order."
|
||||
assert picking.partner_id.id == sale_order.partner_shipping_id.id,"Shipping Address is not correspond with sale order."
|
||||
assert picking.note == sale_order.note,"Note is not correspond with sale order."
|
||||
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.sale_line_id
|
||||
sale_order_date = self.date_to_datetime(cr, uid, sale_order.date_order, context)
|
||||
date_planned = datetime.strptime(sale_order_date, DEFAULT_SERVER_DATETIME_FORMAT) + relativedelta(days=order_line.delay or 0.0)
|
||||
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)
|
||||
date_planned = (date_planned - timedelta(days=sale_order.company_id.security_lead)).strftime(DEFAULT_SERVER_DATETIME_FORMAT)
|
||||
assert datetime.strptime(move.date_expected, DEFAULT_SERVER_DATETIME_FORMAT) == datetime.strptime(date_planned, DEFAULT_SERVER_DATETIME_FORMAT), "Excepted Date is not correspond with Planned Date."
|
||||
assert move.product_id.id == order_line.product_id.id,"Product is not correspond."
|
||||
@@ -79,18 +71,21 @@
|
||||
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.
|
||||
-
|
||||
!python {model: stock.partial.picking}: |
|
||||
!python {model: stock.picking}: |
|
||||
order = self.pool.get('sale.order').browse(cr, uid, ref("sale.sale_order_6"))
|
||||
for pick in order.picking_ids:
|
||||
data = pick.force_assign()
|
||||
if data == True:
|
||||
partial_id = self.create(cr, uid, {}, context={'active_model': 'stock.picking','active_ids': [pick.id]})
|
||||
self.do_partial(cr, uid, [partial_id])
|
||||
pick.do_partial(context=context)
|
||||
-
|
||||
I run the scheduler.
|
||||
-
|
||||
!python {model: procurement.order}: |
|
||||
self.run_scheduler(cr, uid)
|
||||
-
|
||||
I check sale order to verify shipment.
|
||||
-
|
||||
@@ -123,7 +118,7 @@
|
||||
assert invoice.reference == order.client_order_ref or order.name,"Reference is not correspond."
|
||||
assert invoice.partner_id.id == order.partner_invoice_id.id,"Customer does not correspond."
|
||||
assert invoice.currency_id.id == order.pricelist_id.currency_id.id, "Currency is not correspond."
|
||||
assert invoice.comment == (order.note or ''),"Note is not correspond."
|
||||
assert (invoice.comment or '') == (order.note or ''),"Note is not correspond."
|
||||
assert invoice.journal_id.id in journal_ids,"Sales Journal is not link on Invoice."
|
||||
assert invoice.payment_term.id == order.payment_term.id, "Payment term is not correspond."
|
||||
for so_line in order.order_line:
|
||||
|
||||
@@ -1,12 +1,27 @@
|
||||
-
|
||||
In order to test the Prepaid Order Policy, I create a product
|
||||
-
|
||||
!record {model: product.product, id: product_prepaid1}:
|
||||
name: 'OpenERP Documentation Book'
|
||||
list_price: 60.60
|
||||
-
|
||||
Now i create a sale order that uses my new product
|
||||
-
|
||||
!record {model: sale.order, id: sale_order_prepaid1}:
|
||||
partner_id: base.res_partner_2
|
||||
order_policy: prepaid
|
||||
order_line:
|
||||
- product_id: sale_stock.product_prepaid1
|
||||
product_uom_qty: 10
|
||||
-
|
||||
Now I confirm the Quotation with "Pay before delivery" policy.
|
||||
-
|
||||
!workflow {model: sale.order, action: order_confirm, ref: sale.sale_order_4}
|
||||
!workflow {model: sale.order, action: order_confirm, ref: sale_order_prepaid1}
|
||||
-
|
||||
I check that delivery order should not created before invoice is paid.
|
||||
-
|
||||
!python {model: sale.order}: |
|
||||
sale_order = self.browse(cr, uid, ref("sale.sale_order_4"))
|
||||
sale_order = self.browse(cr, uid, ref("sale_order_prepaid1"))
|
||||
assert len(sale_order.picking_ids) == False, "Delivery order should not created before invoice."
|
||||
assert sale_order.invoice_ids, "Invoice should be created."
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
</data>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_order_line_tree_inherit" model="ir.ui.view">
|
||||
<field name="name">sale.order.line.tree.sale.stock.location</field>
|
||||
<field name="inherit_id" ref="sale.view_order_line_tree"/>
|
||||
@@ -29,7 +30,10 @@
|
||||
<field name="inherit_id" ref="sale.view_order_form"/>
|
||||
<field name="model">sale.order</field>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//tree/field[@name='price_unit']" position="before">
|
||||
<xpath expr="//field[@name='order_line']/form/group/group/field[@name='price_unit']" position="before">
|
||||
<field name="route_id"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='order_line']/tree/field[@name='price_unit']" position="before">
|
||||
<field name="route_id"/>
|
||||
</xpath>
|
||||
</field>
|
||||
|
||||
@@ -91,7 +91,9 @@ Dashboard / Reports for Warehouse Management will include:
|
||||
'test': [
|
||||
'test/inventory.yml',
|
||||
'test/move.yml',
|
||||
# 'test/shipment.yml',
|
||||
'test/procrule.yml',
|
||||
'test/shipment.yml',
|
||||
'test/packing.yml',
|
||||
],
|
||||
'installable': True,
|
||||
'application': True,
|
||||
|
||||
@@ -40,7 +40,7 @@ class procurement_rule(osv.osv):
|
||||
'location_id': fields.many2one('stock.location', 'Destination Location'),
|
||||
'location_src_id': fields.many2one('stock.location', 'Source Location',
|
||||
help="Source location is action=move"),
|
||||
'picking_type_id': fields.many2one('stock.picking.type', 'Picking Type', required=True,
|
||||
'picking_type_id': fields.many2one('stock.picking.type', 'Picking Type',
|
||||
help="Picking Type determines the way the picking should be shown in the view, reports, ...")
|
||||
}
|
||||
|
||||
|
||||
@@ -208,6 +208,7 @@ class product_product(osv.osv):
|
||||
'track_outgoing': fields.boolean('Track Outgoing Lots', help="Forces to specify a Serial Number for all moves containing this product and going to a Customer Location"),
|
||||
'location_id': fields.dummy(string='Location', relation='stock.location', type='many2one'),
|
||||
'warehouse_id': fields.dummy(string='Warehouse', relation='stock.warehouse', type='many2one'),
|
||||
'orderpoint_ids': fields.one2many('stock.warehouse.orderpoint', 'product_id', 'Minimum Stock Rules'),
|
||||
}
|
||||
|
||||
def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
|
||||
@@ -258,6 +259,7 @@ class product_template(osv.osv):
|
||||
_name = 'product.template'
|
||||
_inherit = 'product.template'
|
||||
_columns = {
|
||||
'type': fields.selection([('product', 'Stockable Product'), ('consu', 'Consumable'), ('service', 'Service')], 'Product Type', required=True, help="Consumable: Will not imply stock management for this product. \nStockable product: Will imply stock management for this product."),
|
||||
'property_stock_procurement': fields.property(
|
||||
type='many2one',
|
||||
relation='stock.location',
|
||||
|
||||
@@ -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."""),
|
||||
@@ -62,8 +67,10 @@ This installs the module product_expiry."""),
|
||||
'group_stock_tracking_lot': fields.boolean("Track serial number on logistic units (pallets)",
|
||||
implied_group='stock.group_tracking_lot',
|
||||
help="""When you select a serial number on product moves, you can get the upstream or downstream traceability of that product."""),
|
||||
'group_stock_tracking_owner': fields.boolean("Manage owner on stock",
|
||||
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',
|
||||
|
||||
@@ -38,6 +38,10 @@
|
||||
<field name="group_stock_tracking_lot" class="oe_inline"/>
|
||||
<label for="group_stock_tracking_lot"/>
|
||||
</div>
|
||||
<div>
|
||||
<field name="group_stock_tracking_owner" class="oe_inline"/>
|
||||
<label for="group_stock_tracking_owner"/>
|
||||
</div>
|
||||
</div>
|
||||
</group>
|
||||
<separator string="Accounting"/>
|
||||
@@ -58,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"/>
|
||||
|
||||
@@ -29,6 +29,11 @@
|
||||
<field name="category_id" ref="base.module_category_hidden"/>
|
||||
</record>
|
||||
|
||||
<record id="group_tracking_owner" model="res.groups">
|
||||
<field name="name">Manage Different Stock Owners</field>
|
||||
<field name="category_id" ref="base.module_category_hidden"/>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
<data noupdate="1">
|
||||
<!-- multi -->
|
||||
|
||||
+299
-95
@@ -157,7 +157,7 @@ class stock_quant(osv.osv):
|
||||
'reservation_id': fields.many2one('stock.move', 'Reserved for Move', help="Is this quant reserved for a stock.move?"),
|
||||
'lot_id': fields.many2one('stock.production.lot', 'Lot'),
|
||||
'cost': fields.float('Unit Cost'),
|
||||
'partner_id': fields.related('lot_id', 'partner_id', type='many2one', relation="res.partner", string="Owner", store=True), # TODO implement store={}
|
||||
'owner_id': fields.many2one('res.partner', 'Owner', help="This is the owner of the quant"),
|
||||
|
||||
'create_date': fields.datetime('Creation Date'),
|
||||
'in_date': fields.datetime('Incoming Date'),
|
||||
@@ -201,7 +201,6 @@ class stock_quant(osv.osv):
|
||||
else:
|
||||
self._quant_split(cr, uid, quant, qty, context=context)
|
||||
# FP Note: improve this using preferred locations
|
||||
location_to = move.location_dest_id
|
||||
location_to = self.check_preferred_location(cr, uid, move, context=context)
|
||||
self.write(cr, uid, [quant.id], {
|
||||
'location_id': location_to.id,
|
||||
@@ -232,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
|
||||
|
||||
#
|
||||
@@ -239,10 +239,10 @@ class stock_quant(osv.osv):
|
||||
# Create a negative quant in the source location if it's an internal location
|
||||
# Reconcile a positive quant with a negative is possible
|
||||
#
|
||||
def _quant_create(self, cr, uid, qty, move, context=None):
|
||||
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,
|
||||
@@ -251,9 +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,
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
@@ -318,8 +318,22 @@ class stock_quant(osv.osv):
|
||||
def _price_update(self, cr, uid, quant, newprice, context=None):
|
||||
self.write(cr, uid, [quant.id], {'cost': newprice}, context=context)
|
||||
|
||||
def write(self, cr, uid, ids, vals, context=None):
|
||||
#We want to trigger the move with nothing on reserved_quant_ids for the store of the remaining quantity
|
||||
if 'reservation_id' in vals:
|
||||
reservation_ids = self.browse(cr, uid, ids, context=context)
|
||||
moves_to_warn = set()
|
||||
for reser in reservation_ids:
|
||||
if reser.reservation_id:
|
||||
moves_to_warn.add(reser.reservation_id.id)
|
||||
self.pool.get('stock.move').write(cr, uid, list(moves_to_warn), {'reserved_quant_ids': []}, context=context)
|
||||
return super(stock_quant, self).write(cr, uid, ids, vals, context=context)
|
||||
|
||||
def quants_unreserve(self, cr, uid, move, context=None):
|
||||
cr.execute('update stock_quant set reservation_id=NULL where reservation_id=%s', (move.id,))
|
||||
#cr.execute('update stock_quant set reservation_id=NULL where reservation_id=%s', (move.id,))
|
||||
#need write for related store of remaining qty
|
||||
related_quants = [x.id for x in move.reserved_quant_ids]
|
||||
self.write(cr, uid, related_quants, {'reservation_id': False}, context=context)
|
||||
return True
|
||||
|
||||
#
|
||||
@@ -516,7 +530,8 @@ class stock_picking(osv.osv):
|
||||
picking_obj = self.browse(cr, uid, id, context=context)
|
||||
if ('name' not in default) or (picking_obj.name == '/'):
|
||||
default['name'] = '/'
|
||||
default['backorder_id'] = False
|
||||
if not default.get('backorder_id'):
|
||||
default['backorder_id'] = False
|
||||
return super(stock_picking, self).copy(cr, uid, id, default, context)
|
||||
|
||||
def action_confirm(self, cr, uid, ids, context=None):
|
||||
@@ -610,16 +625,20 @@ class stock_picking(osv.osv):
|
||||
backorder_moves = picking.move_lines
|
||||
backorder_move_ids = [x.id for x in backorder_moves if x.state not in ('done','cancel')]
|
||||
|
||||
backorder_id = self.copy(cr, uid, picking.id, {
|
||||
'name': '/',
|
||||
'move_lines': [],
|
||||
'pack_operation_ids': [],
|
||||
'backorder_id': picking.id,
|
||||
})
|
||||
back_order_name = self.browse(cr, uid, backorder_id, context=context).name
|
||||
self.message_post(cr, uid, picking.id, body=_("Back order <em>%s</em> <b>created</b>.") % (back_order_name), context=context)
|
||||
self.pool.get('stock.move').write(cr, uid, backorder_move_ids, {'picking_id': backorder_id}, context=context)
|
||||
return backorder_id
|
||||
if backorder_move_ids:
|
||||
backorder_id = self.copy(cr, uid, picking.id, {
|
||||
'name': '/',
|
||||
'move_lines': [],
|
||||
'pack_operation_ids': [],
|
||||
'backorder_id': picking.id,
|
||||
})
|
||||
back_order_name = self.browse(cr, uid, backorder_id, context=context).name
|
||||
self.message_post(cr, uid, picking.id, body=_("Back order <em>%s</em> <b>created</b>.") % (back_order_name), context=context)
|
||||
move_obj = self.pool.get("stock.move")
|
||||
move_obj.write(cr, uid, backorder_move_ids, {'picking_id': backorder_id}, context=context)
|
||||
self.pool.get("stock.picking").action_confirm(cr, uid, [picking.id], context=context)
|
||||
return backorder_id
|
||||
return False
|
||||
|
||||
def do_prepare_partial(self, cr, uid, picking_ids, context=None):
|
||||
context = context or {}
|
||||
@@ -636,9 +655,11 @@ class stock_picking(osv.osv):
|
||||
'product_qty': qty,
|
||||
'quant_id': quant.id,
|
||||
'product_id': quant.product_id.id,
|
||||
'lot_id': quant.lot_id.id,
|
||||
'lot_id': quant.lot_id and quant.lot_id.id or False,
|
||||
'product_uom_id': quant.product_id.uom_id.id,
|
||||
'owner_id': quant.owner_id and quant.owner_id.id or False,
|
||||
'cost': quant.cost,
|
||||
'package_id': quant.package_id and quant.package_id.id or False,
|
||||
}, context=context)
|
||||
if remaining_qty > 0:
|
||||
pack_operation_obj.create(cr, uid, {
|
||||
@@ -649,7 +670,7 @@ class stock_picking(osv.osv):
|
||||
'cost': move.product_id.standard_price,
|
||||
}, context=context)
|
||||
|
||||
def _do_partial_product_move(self, cr, uid, picking, product, qty, quant=False, context=None):
|
||||
def _do_partial_product_move(self, cr, uid, picking, product, qty, pack_id = False, lot_id = False, quant=False, context=None):
|
||||
moves = []
|
||||
stock_move_obj = self.pool.get('stock.move')
|
||||
for move in picking.move_lines:
|
||||
@@ -684,44 +705,150 @@ class stock_picking(osv.osv):
|
||||
moves.append(move)
|
||||
return moves
|
||||
|
||||
def do_rereserve(self, cr, uid, picking_ids, context=None):
|
||||
'''
|
||||
Needed for parameter create
|
||||
'''
|
||||
self.rereserve(cr, uid, picking_ids, context=context)
|
||||
|
||||
|
||||
def rereserve(self, cr, uid, picking_ids, create=False, context=None):
|
||||
"""
|
||||
This will unreserve all products and reserve the quants from the operations
|
||||
:return: tuple of dictionary with quantities of quant operation and product that can not be matched between ops and moves
|
||||
and dictionary with remaining values on moves
|
||||
|
||||
"""
|
||||
quant_obj = self.pool.get("stock.quant")
|
||||
move_obj = self.pool.get("stock.move")
|
||||
op_obj = self.pool.get("stock.pack.operation")
|
||||
res = {}
|
||||
res2 = {} #what is left from moves
|
||||
for picking in self.browse(cr, uid, picking_ids, context=context):
|
||||
# unreserve everything and initialize res2
|
||||
for move in picking.move_lines:
|
||||
quant_obj.quants_unreserve(cr, uid, move, context=context)
|
||||
res2[move.id] = move.product_qty
|
||||
# Resort pack_operation_ids
|
||||
|
||||
orderedpackops = picking.pack_operation_ids
|
||||
#Sort packing operations such that packing operations with most specific information
|
||||
orderedpackops.sort(key = lambda x: (x.package_id and -1 or 0) + (x.lot_id and -1 or 0))
|
||||
|
||||
for ops in orderedpackops:
|
||||
#Find moves that correspond
|
||||
if ops.product_id:
|
||||
#TODO: Should have order such that things with lots and packings are searched first
|
||||
move_ids = move_obj.search(cr, uid, [('picking_id','=',picking.id), ('product_id', '=', ops.product_id.id), ('remaining_qty', '>', 0.0)], context=context)
|
||||
qty_to_do = ops.product_qty
|
||||
while qty_to_do > 0 and move_ids:
|
||||
move = move_obj.browse(cr, uid, move_ids.pop(), context=context)
|
||||
if move.remaining_qty > qty_to_do:
|
||||
qty = qty_to_do
|
||||
qty_to_do = 0
|
||||
else:
|
||||
qty = move.remaining_qty
|
||||
qty_to_do -= move.remaining_qty
|
||||
|
||||
if create and move.location_id.usage != 'internal':
|
||||
# Create quants
|
||||
vals = {
|
||||
'product_id': move.product_id.id,
|
||||
'location_id': move.location_id.id,
|
||||
'qty': qty,
|
||||
#'cost': price_unit,
|
||||
'history_ids': [(4, move.id)],
|
||||
'in_date': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
|
||||
'company_id': move.company_id.id,
|
||||
'lot_id': ops.lot_id and ops.lot_id.id or False,
|
||||
'owner_id': ops.owner_id and ops.owner_id.id or False,
|
||||
'reservation_id': move.id, #Reserve at once
|
||||
'package_id': ops.result_package_id and ops.result_package_id.id or False,
|
||||
}
|
||||
quant_id = quant_obj.create(cr, uid, vals, context=context)
|
||||
else:
|
||||
#Quants get
|
||||
prefered_order = "reservation_id IS NOT NULL"
|
||||
domain = op_obj._get_domain(cr, uid, ops, context=context)
|
||||
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:
|
||||
quant_obj.write(cr, uid, [x[0].id for x in quants], {'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
|
||||
elif ops.package_id:
|
||||
quants = ops.package_id.quant_ids #_get_package_lines
|
||||
for quant in quants:
|
||||
move_ids = move_obj.search(cr, uid, [('picking_id', '=', picking.id), ('product_id', '=', quant.product_id.id), ('remaining_qty', '>', 0.0)])
|
||||
qty_to_do = quant.qty
|
||||
while qty_to_do > 0 and move_ids:
|
||||
move = move_obj.browse(cr, uid, move_ids.pop(), context=context)
|
||||
if move.remaining_qty > qty_to_do:
|
||||
qty = qty_to_do
|
||||
qty_to_do = 0.0
|
||||
else:
|
||||
qty = move.remaining_qty
|
||||
qty_to_do -= move.remaining_qty
|
||||
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:
|
||||
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)
|
||||
|
||||
|
||||
def do_partial(self, cr, uid, picking_ids, context=None):
|
||||
"""
|
||||
If no pack operation, we close the whole move
|
||||
Otherwise, do the pack operations
|
||||
"""
|
||||
#TODO: this variable should be in argument
|
||||
quant_obj = self.pool.get('stock.quant')
|
||||
quant_package_obj = self.pool.get('stock.quant.package')
|
||||
stock_move_obj = self.pool.get('stock.move')
|
||||
for picking in self.browse(cr, uid, picking_ids, context=context):
|
||||
if not picking.pack_operation_ids:
|
||||
self.action_done(cr, uid, [picking.id], context=context)
|
||||
continue
|
||||
for op in picking.pack_operation_ids:
|
||||
#TODO: op.package_id can not work as quants_get is not defined on quant package => gives traceback
|
||||
if op.package_id:
|
||||
for quant in quant_package_obj.quants_get(cr, uid, op.package_id, context=context):
|
||||
self._do_partial_product_move(cr, uid, picking, quant.product_id, quant.qty, quant, context=context)
|
||||
op.package_id.write(cr, uid, {
|
||||
'parent_id': op.result_package_id.id
|
||||
}, context=context)
|
||||
elif op.product_id:
|
||||
moves = self._do_partial_product_move(cr, uid, picking, op.product_id, op.product_qty, op.quant_id, context=context)
|
||||
|
||||
quants = []
|
||||
for m in moves:
|
||||
for quant in m.quant_ids:
|
||||
quants.append(quant.id)
|
||||
quant_obj.write(cr, uid, quants, {
|
||||
'package_id': op.result_package_id.id
|
||||
|
||||
}, context=context)
|
||||
|
||||
else:
|
||||
#First thing that needs to happen is rereserving the quants
|
||||
res = self.rereserve(cr, uid, [picking.id], create = True, context = context) #This time, quants need to be created
|
||||
orig_moves = picking.move_lines
|
||||
#Add moves that operations need extra
|
||||
extra_moves = []
|
||||
for ops in res[0].keys():
|
||||
for prod in res[0][ops].keys():
|
||||
product = self.pool.get('product.product').browse(cr, uid, prod, context=context)
|
||||
qty = res[0][ops][prod]
|
||||
if qty > 0:
|
||||
#TODO: Maybe should try to reserve quants?
|
||||
quant = False
|
||||
move_id = stock_move_obj.create(cr, uid, {
|
||||
'name': product.name,
|
||||
'product_id': product.id,
|
||||
'product_uom_qty': qty,
|
||||
'product_uom': product.uom_id.id,
|
||||
'location_id': picking.location_id.id,
|
||||
'location_dest_id': picking.location_dest_id.id,
|
||||
'picking_id': picking.id,
|
||||
'reserved_quant_ids': quant and [(4, quant.id)] or [],
|
||||
'picking_type_id': picking.picking_type_id.id
|
||||
}, context=context)
|
||||
extra_moves.append(move_id)
|
||||
res2 = res[1]
|
||||
for move in res2.keys():
|
||||
if res2[move] > 0:
|
||||
mov = stock_move_obj.browse(cr, uid, 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)
|
||||
return True
|
||||
|
||||
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 = {}
|
||||
@@ -818,7 +945,7 @@ class stock_production_lot(osv.osv):
|
||||
'product_id': fields.many2one('product.product', 'Product', required=True, domain=[('type', '<>', 'service')]),
|
||||
'quant_ids': fields.one2many('stock.quant', 'lot_id', 'Quants'),
|
||||
'create_date': fields.datetime('Creation Date'),
|
||||
'partner_id': fields.many2one('res.partner', 'Owner'),
|
||||
# 'partner_id': fields.many2one('res.partner', 'Owner'),
|
||||
}
|
||||
_defaults = {
|
||||
'name': lambda x, y, z, c: x.pool.get('ir.sequence').get(y, z, 'stock.lot.serial'),
|
||||
@@ -831,8 +958,8 @@ class stock_production_lot(osv.osv):
|
||||
res = []
|
||||
for lot in self.browse(cr, uid, ids, context=context):
|
||||
name = lot.name
|
||||
if lot.partner_id:
|
||||
name += ' (' + lot.partner_id.name + ')'
|
||||
# if lot.partner_id:
|
||||
# name += ' (' + lot.partner_id.name + ')'
|
||||
res.append((lot.id, name))
|
||||
return res
|
||||
|
||||
@@ -848,6 +975,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):
|
||||
@@ -884,20 +1015,29 @@ class stock_move(osv.osv):
|
||||
res[m.id] = uom_obj._compute_qty_obj(cr, uid, m.product_uom, m.product_uom_qty, m.product_id.uom_id)
|
||||
return res
|
||||
|
||||
# def _get_remaining_qty(self, cr, uid, ids, field_name, args, context=None):
|
||||
# #TODO: this function assumes that there aren't several stock move in the same picking with the same product. what should we do in that case?
|
||||
# #TODO take care of the quant on stock moves too
|
||||
# res = dict.fromkeys(ids, False)
|
||||
# for move in self.browse(cr, uid, ids, context=context):
|
||||
# res[move.id] = move.product_qty
|
||||
# if move.picking_id:
|
||||
# for op in move.picking_id.pack_operation_ids:
|
||||
# if op.product_id == move.product_id or (op.quant_id and op.quant_id.product_id == move.product_id):
|
||||
# res[move.id] -= op.product_qty
|
||||
# if op.package_id:
|
||||
# #find the product qty recursively
|
||||
# res[move.id] -= self.pool.get('stock.quant.package')._get_product_total_qty(cr, uid, op.package_id, move.product_id.id, context=context)
|
||||
# return res
|
||||
|
||||
def _get_remaining_qty(self, cr, uid, ids, field_name, args, context=None):
|
||||
#TODO: this function assumes that there aren't several stock move in the same picking with the same product. what should we do in that case?
|
||||
#TODO take care of the quant on stock moves too
|
||||
res = dict.fromkeys(ids, False)
|
||||
res = {}
|
||||
for move in self.browse(cr, uid, ids, context=context):
|
||||
res[move.id] = move.product_qty
|
||||
if move.picking_id:
|
||||
for op in move.picking_id.pack_operation_ids:
|
||||
if op.product_id == move.product_id or (op.quant_id and op.quant_id.product_id == move.product_id):
|
||||
res[move.id] -= op.product_qty
|
||||
if op.package_id:
|
||||
#find the product qty recursively
|
||||
res[move.id] -= self.pool.get('stock.quant.package')._get_product_total_qty(cr, uid, op.package_id, move.product_id.id, context=context)
|
||||
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)
|
||||
@@ -923,6 +1063,14 @@ class stock_move(osv.osv):
|
||||
res[move.id] = min(move.product_qty, availability)
|
||||
return res
|
||||
|
||||
|
||||
def _get_move(self, cr, uid, ids, context=None):
|
||||
res = set()
|
||||
for quant in self.browse(cr, uid, ids, context=context):
|
||||
if quant.reservation_id:
|
||||
res.add(quant.reservation_id.id)
|
||||
return list(res)
|
||||
|
||||
_columns = {
|
||||
'name': fields.char('Description', required=True, select=True),
|
||||
'priority': fields.selection([('0', 'Not urgent'), ('1', 'Urgent')], 'Priority'),
|
||||
@@ -988,7 +1136,10 @@ class stock_move(osv.osv):
|
||||
|
||||
'quant_ids': fields.many2many('stock.quant', 'stock_quant_move_rel', 'move_id', 'quant_id', 'Quants'),
|
||||
'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)]}),
|
||||
'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),
|
||||
'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'),
|
||||
'rule_id': fields.many2one('procurement.rule', 'Procurement Rule'),
|
||||
@@ -999,7 +1150,10 @@ class stock_move(osv.osv):
|
||||
'origin_returned_move_id': fields.many2one('stock.move', 'Origin return move', help='move that created the return move'),
|
||||
'returned_move_ids': fields.one2many('stock.move', 'origin_returned_move_id', 'All returned moves', help='Optional: all returned moves created from this move'),
|
||||
'availability': fields.function(_get_product_availability, type='float', string='Availability'),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
def copy(self, cr, uid, id, default=None, context=None):
|
||||
if default is None:
|
||||
@@ -1290,6 +1444,7 @@ class stock_move(osv.osv):
|
||||
for m2 in move.move_orig_ids:
|
||||
for q in m2.quant_ids:
|
||||
dp.append(str(q.id))
|
||||
qty -= q.qty
|
||||
domain = ['|', ('reservation_id', '=', False), ('reservation_id', '=', move.id)]
|
||||
quants = quant_obj.quants_get(cr, uid, move.location_id, move.product_id, qty, domain=domain, prefered_order = dp and ('id not in ('+','.join(dp)+')') or False, context=context)
|
||||
#Will only reserve physical quants, no negative
|
||||
@@ -1310,6 +1465,8 @@ class stock_move(osv.osv):
|
||||
"""
|
||||
context = context or {}
|
||||
for move in self.browse(cr, uid, ids, context=context):
|
||||
if move.reserved_quant_ids:
|
||||
self.pool.get("stock.quant").quants_unreserve(cr, uid, move, context=context)
|
||||
if move.move_dest_id:
|
||||
if move.propagate:
|
||||
self.action_cancel(cr, uid, [move.move_dest_id.id], context=context)
|
||||
@@ -1335,7 +1492,6 @@ class stock_move(osv.osv):
|
||||
"""
|
||||
context = context or {}
|
||||
quant_obj = self.pool.get("stock.quant")
|
||||
picking_obj = self.pool.get('stock.picking')
|
||||
|
||||
todo = [move.id for move in self.browse(cr, uid, ids, context=context) if move.state == "draft"]
|
||||
if todo:
|
||||
@@ -1345,14 +1501,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)]
|
||||
quants = quant_obj.quants_get(cr, uid, move.location_id, move.product_id, qty, domain=domain, prefered_order = 'reservation_id<>'+str(move.id), context=context)
|
||||
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)
|
||||
@@ -1478,11 +1639,11 @@ class stock_move(osv.osv):
|
||||
self.action_done(cr, uid, res, context=context)
|
||||
return res
|
||||
|
||||
|
||||
|
||||
def split(self, cr, uid, move, qty, context=None):
|
||||
""" Partially (or not) moves a stock.move.
|
||||
@param partial_datas: Dictionary containing details of partial picking
|
||||
like partner_id, delivery_date, delivery
|
||||
moves with product_id, product_qty, uom
|
||||
"""
|
||||
Splits qty from move move into a new move
|
||||
"""
|
||||
if move.product_qty==qty:
|
||||
return move.id
|
||||
@@ -1509,8 +1670,11 @@ class stock_move(osv.osv):
|
||||
self.write(cr, uid, [move.id], {
|
||||
'product_uom_qty': move.product_uom_qty - uom_qty,
|
||||
'product_uos_qty': move.product_uos_qty - uos_qty,
|
||||
'reserved_quant_ids': [(6,0,[])]
|
||||
# 'reserved_quant_ids': [(6,0,[])] SHOULD NOT CHANGE as it has been reserved already
|
||||
}, context=context)
|
||||
|
||||
if move.move_dest_id and move.propagate:
|
||||
self.split(cr, uid, move.move_dest_id, qty, context=context)
|
||||
return new_move
|
||||
|
||||
class stock_inventory(osv.osv):
|
||||
@@ -1580,6 +1744,7 @@ class stock_inventory(osv.osv):
|
||||
for inv in self.browse(cr, uid, ids, context=context):
|
||||
if not inv.move_ids:
|
||||
self.action_check(cr, uid, [inv.id], context=context)
|
||||
inv.refresh()
|
||||
move_obj.action_done(cr, uid, [x.id for x in inv.move_ids], context=context)
|
||||
self.write(cr, uid, [inv.id], {'state': 'done', 'date_done': time.strftime('%Y-%m-%d %H:%M:%S')}, context=context)
|
||||
return True
|
||||
@@ -1757,25 +1922,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,
|
||||
}
|
||||
|
||||
|
||||
@@ -1788,7 +1945,7 @@ report_sxw.report_sxw('report.stock.quant.package.barcode', 'stock.quant.package
|
||||
|
||||
class stock_package(osv.osv):
|
||||
"""
|
||||
These are the packages, containing quants and/or others packages
|
||||
These are the packages, containing quants and/or other packages
|
||||
"""
|
||||
_name = "stock.quant.package"
|
||||
_description = "Physical Packages"
|
||||
@@ -1935,6 +2092,7 @@ class stock_pack_operation(osv.osv):
|
||||
'lot_id': fields.many2one('stock.production.lot', 'Lot/Serial Number'),
|
||||
'result_package_id': fields.many2one('stock.quant.package', 'Container Package', help="If set, the operations are packed into this package", required=False, ondelete='cascade'),
|
||||
'date': fields.datetime('Date', required=True),
|
||||
'owner_id': fields.many2one('res.partner', 'Owner', help="Owner of the quants"),
|
||||
#'update_cost': fields.boolean('Need cost update'),
|
||||
'cost': fields.float("Cost", help="Unit Cost for this product line"),
|
||||
'currency': fields.many2one('res.currency', string="Currency", help="Currency in which Unit cost is expressed", ondelete='CASCADE'),
|
||||
@@ -1944,6 +2102,22 @@ class stock_pack_operation(osv.osv):
|
||||
'date': fields.date.context_today,
|
||||
}
|
||||
|
||||
|
||||
def _get_domain(self, cr, uid, ops, context=None):
|
||||
'''
|
||||
Gives domain for different
|
||||
'''
|
||||
res = []
|
||||
if ops.package_id:
|
||||
res.append(('package_id', '=', ops.package_id.id), )
|
||||
if ops.lot_id:
|
||||
res.append(('lot_id', '=', ops.lot_id.id), )
|
||||
if ops.owner_id:
|
||||
res.append(('owner_id', '=', ops.owner_id.id), )
|
||||
else:
|
||||
res.append(('owner_id', '=', False), )
|
||||
return res
|
||||
|
||||
#TODO: this function can be refactored
|
||||
def _search_and_increment(self, cr, uid, picking_id, key, context=None):
|
||||
'''Search for an operation on an existing key in a picking, if it exists increment the qty (+1) otherwise create it
|
||||
@@ -2099,26 +2273,12 @@ class stock_warehouse_orderpoint(osv.osv):
|
||||
class product_template(osv.osv):
|
||||
_inherit = "product.template"
|
||||
_columns = {
|
||||
'type': fields.selection([('product', 'Stockable Product'), ('consu', 'Consumable'), ('service', 'Service')], 'Product Type', required=True, help="Consumable: Will not imply stock management for this product. \nStockable product: Will imply stock management for this product."),
|
||||
'supply_method': fields.selection([('produce', 'Manufacture'), ('buy', 'Buy'), ('wait', 'None')], 'Supply Method', required=True, help="Manufacture: When procuring the product, a manufacturing order or a task will be generated, depending on the product type. \nBuy: When procuring the product, a purchase order will be generated."),
|
||||
}
|
||||
_defaults = {
|
||||
'supply_method': 'buy',
|
||||
}
|
||||
|
||||
class product_product(osv.osv):
|
||||
_inherit = "product.product"
|
||||
_columns = {
|
||||
'orderpoint_ids': fields.one2many('stock.warehouse.orderpoint', 'product_id', 'Minimum Stock Rules'),
|
||||
}
|
||||
|
||||
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"
|
||||
@@ -2210,17 +2370,58 @@ 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
|
||||
reads = self.browse(cr, uid, ids, context=context)
|
||||
for record in reads:
|
||||
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'),
|
||||
'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 stock'),
|
||||
'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,
|
||||
@@ -2249,4 +2450,7 @@ class stock_picking_type(osv.osv):
|
||||
type='string', multi='_get_picking_history'),
|
||||
|
||||
}
|
||||
_defaults = {
|
||||
'warehouse_id': _default_warehouse,
|
||||
}
|
||||
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
+67
-24
@@ -493,19 +493,19 @@
|
||||
<field name="model">stock.warehouse</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Warehouse" version="7.0">
|
||||
<sheet>
|
||||
<label for="name" class="oe_edit_only"/>
|
||||
<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"/>
|
||||
<field name="partner_id"/>
|
||||
</group>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
@@ -515,9 +515,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>
|
||||
@@ -583,6 +581,7 @@
|
||||
<button name="force_assign" states="confirmed" string="Force Availability" type="object" class="oe_highlight" groups="base.group_user"/>
|
||||
<button name="do_partial" states="assigned" string="Transfer Done" groups="stock.group_stock_user" type="object" class="oe_highlight"/>
|
||||
<button name="do_split" string="Postpone Partial Transfer" groups="stock.group_stock_user" type="object" attrs="{'invisible': ['|',('pack_operation_exist', '=', False),('state','!=','assigned')]}"/>
|
||||
<button name="do_rereserve" string="ReReserve from Operations" groups="stock.group_stock_user" type="object" attrs="{'invisible': ['|',('pack_operation_exist', '=', False),('state','!=','assigned')]}"/>
|
||||
<button name="do_prepare_partial" string="Partial Transfer" groups="stock.group_stock_user" type="object" class="oe_highlight" attrs="{'invisible': ['|',('pack_operation_exist', '=', True),('state','!=','assigned')]}"/>
|
||||
<button name="%(act_stock_return_picking)d" string="Reverse Transfer" states="done" type="action" groups="base.group_user"/>
|
||||
<button name="action_cancel" states="assigned,confirmed,draft" string="Cancel Transfer" groups="base.group_user" type="object"/>
|
||||
@@ -611,11 +610,12 @@
|
||||
<field name="pack_operation_ids" attrs="{'invisible': [('pack_operation_exist', '=', False)]}">
|
||||
<tree editable="top">
|
||||
<field name="product_id"/>
|
||||
<field name="product_uom_id"/>
|
||||
<field name="lot_id"/>
|
||||
<field name="package_id"/>
|
||||
<field name="product_uom_id" groups="product.group_uom"/>
|
||||
<field name="lot_id" groups="stock.group_production_lot"/>
|
||||
<field name="package_id" groups="stock.group_tracking_lot"/>
|
||||
<field name="owner_id" groups="stock.group_tracking_owner"/>
|
||||
<field name="product_qty"/>
|
||||
<field name="result_package_id"/>
|
||||
<field name="result_package_id" groups="stock.group_tracking_lot"/>
|
||||
</tree>
|
||||
</field>
|
||||
<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}"/>
|
||||
@@ -803,7 +803,7 @@
|
||||
<field name="scrapped" invisible="1"/>
|
||||
<field name="location_dest_id" groups="stock.group_locations"/>
|
||||
<field name="state"/>
|
||||
<field name="availability"/>
|
||||
<field name="remaining_qty" attrs="{'invisible': [('state', '=', 'done')]}"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
@@ -1182,6 +1182,7 @@
|
||||
<search string="Picking Type">
|
||||
<field name="name" string="Picking Type"/>
|
||||
<field name="sequence_id"/>
|
||||
<field name="warehouse_id"/>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
@@ -1201,18 +1202,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>
|
||||
@@ -1450,10 +1460,41 @@
|
||||
<field name="location_src_id"/>
|
||||
<field name="location_id"/>
|
||||
<field name="action"/>
|
||||
<field name="picking_type_id"/>
|
||||
<field name="picking_type_id" attrs="{'required': [('action', '!=', 'manufacture')]}"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
<record model="ir.ui.view" id="view_procurement_rule_form">
|
||||
<field name="name">stock.procurement.form</field>
|
||||
<field name="model">procurement.rule</field>
|
||||
<field eval="10" name="priority"/>
|
||||
|
||||
<field name="arch" type="xml">
|
||||
<form string="Procurement Rules" version="7.0">
|
||||
<sheet>
|
||||
<div class="oe_title">
|
||||
<h1>
|
||||
<label string="Name"/>
|
||||
<field name="name" class="oe_inline"/>
|
||||
</h1>
|
||||
</div>
|
||||
<group>
|
||||
<group>
|
||||
<field name="location_src_id"/>
|
||||
<field name="location_id"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="action"/>
|
||||
<field name="picking_type_id" attrs="{'required': [('action', '!=', 'manufacture')]}"/>
|
||||
<field name="group_id"/>
|
||||
</group>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
|
||||
@@ -1510,6 +1551,7 @@
|
||||
<field name="reservation_id"/>
|
||||
<field name="propagated_from_id"/>
|
||||
<field name="history_ids"/>
|
||||
<field name="owner_id"/>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
@@ -1525,7 +1567,7 @@
|
||||
<field name="qty"/>
|
||||
<field name="location_id"/>
|
||||
<field name="lot_id"/>
|
||||
<field name="package_id" invisible="1"/>
|
||||
<field name="package_id"/>
|
||||
<field name="packaging_type_id" invisible="1"/>
|
||||
<field name="in_date"/>
|
||||
<field name="reservation_id" invisible='1'/>
|
||||
@@ -1537,6 +1579,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="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"/>
|
||||
@@ -1619,7 +1662,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')]"/>
|
||||
|
||||
@@ -1,83 +1,106 @@
|
||||
-
|
||||
I create an inventory of 10 products
|
||||
In order to test the inventory, I create a new product
|
||||
-
|
||||
!record {model: product.product, id: inventory_product}:
|
||||
name: inventory prod
|
||||
type: product
|
||||
-
|
||||
I create an inventory for this product only
|
||||
-
|
||||
!record {model: stock.inventory, id: inventory_test0}:
|
||||
name: Test
|
||||
inventory_line_id:
|
||||
- product_id: product.product_product_3
|
||||
product_qty: 10
|
||||
location_id: stock.stock_location_14
|
||||
product_id: inventory_product
|
||||
|
||||
-
|
||||
I post this inventory
|
||||
I create the wizard to confirm the inventory
|
||||
-
|
||||
!record {model: stock.fill.inventory, id: inventory_test0_wizard}:
|
||||
set_stock_zero: False
|
||||
-
|
||||
I fill this inventory
|
||||
-
|
||||
!python {model: stock.inventory.line}: |
|
||||
context = {'active_ids': [ref('inventory_test0')]}
|
||||
self.pool.get('stock.fill.inventory').fill_inventory(cr, uid, [ref('inventory_test0_wizard')], context=context)
|
||||
-
|
||||
I check that my inventory has no line, as the product is a new one.
|
||||
-
|
||||
!python {model: stock.inventory}: |
|
||||
inv = self.browse(cr, uid, ref('inventory_test0'), context=context)
|
||||
assert len(inv.line_ids) == 0, "Wrong number of inventory lines."
|
||||
-
|
||||
I add an inventory line and say i have 10 products in stock
|
||||
-
|
||||
!record {model: stock.inventory.line, id: inventory_testline0}:
|
||||
inventory_id: stock.inventory_test0
|
||||
product_id: inventory_product
|
||||
product_qty: 10
|
||||
location_id: stock.stock_location_14
|
||||
-
|
||||
I confirm this inventory
|
||||
-
|
||||
!python {model: stock.inventory}: |
|
||||
self.action_confirm(cr, uid, [ref('inventory_test0')], context=context)
|
||||
self.action_done(cr, uid, [ref('inventory_test0')], context=context)
|
||||
-
|
||||
I check that this inventory has a stock.move and a quant
|
||||
-
|
||||
!python {model: stock.inventory}: |
|
||||
inv = self.browse(cr, uid, ref('inventory_test0'), context=context)
|
||||
assert len(inv.move_ids) == 1, "No move created for the inventory."
|
||||
assert len(inv.move_ids) >= 1, "No move created for the inventory."
|
||||
assert len(inv.move_ids[0].quant_ids) >= 1, "No quant created for this inventory"
|
||||
-
|
||||
I check that the quantity on hand is 10 on the location and it's parent.
|
||||
I check that the quantity on hand is 10 on the location and its parent.
|
||||
-
|
||||
!python {model: product.product}: |
|
||||
context['location'] = ref('stock.stock_location_14')
|
||||
product = self.browse(cr, uid, ref('product.product_product_3'), context=context)
|
||||
assert product.qty_available==10, 'Expecting 10 products, got %.2f on location stock_location_14!' % (product.qty_available,)
|
||||
product = self.browse(cr, uid, ref('inventory_product'), context=context)
|
||||
assert product.qty_available == 10, 'Expecting 10 products, got %.2f on location stock_location_14!' % (product.qty_available,)
|
||||
context['location'] = ref('stock.stock_location_stock')
|
||||
product = self.browse(cr, uid, ref('product.product_product_3'), context=context)
|
||||
assert product.qty_available==10, 'Expecting 10 products, got %.2f on location stock_location_stock!' % (product.qty_available,)
|
||||
product = self.browse(cr, uid, ref('inventory_product'), context=context)
|
||||
assert product.qty_available == 10, 'Expecting 10 products, got %.2f on location stock_location_stock!' % (product.qty_available,)
|
||||
-
|
||||
I check that the quantity on hand is 0 on a brother location
|
||||
-
|
||||
!python {model: product.product}: |
|
||||
context['location'] = ref('stock.stock_location_components')
|
||||
product = self.browse(cr, uid, ref('product.product_product_3'), context=context)
|
||||
assert product.qty_available==0, 'Expecting 0 products, got %.2f on location stock_location_components!' % (product.qty_available,)
|
||||
product = self.browse(cr, uid, ref('inventory_product'), context=context)
|
||||
assert product.qty_available == 0, 'Expecting 0 products, got %.2f on location stock_location_components!' % (product.qty_available,)
|
||||
-
|
||||
I create an inventory of 20 products
|
||||
I create another inventory
|
||||
-
|
||||
!record {model: stock.inventory, id: inventory_test1}:
|
||||
name: Test
|
||||
inventory_line_id:
|
||||
- product_id: product.product_product_3
|
||||
product_qty: 20
|
||||
location_id: stock.stock_location_14
|
||||
name: second test inventory
|
||||
product_id: inventory_product
|
||||
-
|
||||
I post this new inventory
|
||||
I fill this new inventory
|
||||
-
|
||||
!python {model: stock.inventory}: |
|
||||
context = {'active_ids': [ref('inventory_test1')]}
|
||||
self.pool.get('stock.fill.inventory').fill_inventory(cr, uid, [ref('inventory_test0_wizard')], context=context)
|
||||
-
|
||||
I check that my inventory has one line, with a quantity of 10.
|
||||
-
|
||||
!python {model: stock.inventory}: |
|
||||
inv = self.browse(cr, uid, ref('inventory_test1'), context=context)
|
||||
assert inv.line_ids and len(inv.line_ids) == 1, "Wrong number of inventory lines."
|
||||
assert inv.line_ids[0].product_qty == 10, "Wrong quantity in inventory line."
|
||||
assert inv.line_ids[0].product_id.id == ref('inventory_product'), "Wrong product in inventory line."
|
||||
-
|
||||
I modify the inventory line and set the quantity to 20 product on this new inventory
|
||||
-
|
||||
!python {model: stock.inventory}: |
|
||||
inv = self.browse(cr, uid, ref('inventory_test1'), context=context)
|
||||
self.pool.get('stock.inventory.line').write(cr, uid, inv.line_ids[0].id, {'product_qty': 20})
|
||||
-
|
||||
I confirm this inventory
|
||||
-
|
||||
!python {model: stock.inventory}: |
|
||||
self.action_confirm(cr, uid, [ref('inventory_test1')], context=context)
|
||||
self.action_done(cr, uid, [ref('inventory_test1')], context=context)
|
||||
|
||||
-
|
||||
I check that the quantity on hand is 20 on the location and it's parent.
|
||||
I check that the quantity on hand is 20 on the location and its parent.
|
||||
-
|
||||
!python {model: product.product}: |
|
||||
context['location'] = ref('stock.stock_location_14')
|
||||
product = self.browse(cr, uid, ref('product.product_product_3'), context=context)
|
||||
assert product.qty_available==20, 'Expecting 20 products, got %.2f on location stock_location_14!' % (product.qty_available,)
|
||||
-
|
||||
!record {model: stock.inventory, id: inventory_test2}:
|
||||
name: Test
|
||||
inventory_line_id:
|
||||
- product_id: product.product_product_3
|
||||
product_qty: 18
|
||||
location_id: stock.stock_location_14
|
||||
-
|
||||
I post this new inventory
|
||||
-
|
||||
!python {model: stock.inventory}: |
|
||||
self.action_confirm(cr, uid, [ref('inventory_test2')], context=context)
|
||||
self.action_done(cr, uid, [ref('inventory_test2')], context=context)
|
||||
-
|
||||
I check that the quantity on hand is 18 on the location and it's parent.
|
||||
-
|
||||
!python {model: product.product}: |
|
||||
context['location'] = ref('stock.stock_location_14')
|
||||
product = self.browse(cr, uid, ref('product.product_product_3'), context=context)
|
||||
assert product.qty_available==18, 'Expecting 18 products, got %.2f on location stock_location_14!' % (product.qty_available,)
|
||||
product = self.browse(cr, uid, ref('inventory_product'), context=context)
|
||||
assert product.qty_available == 20, 'Expecting 20 products, got %.2f on location stock_location_14!' % (product.qty_available,)
|
||||
|
||||
|
||||
+66
-43
@@ -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,)
|
||||
|
||||
+170
-11
@@ -1,18 +1,177 @@
|
||||
-
|
||||
Create a new stockable product
|
||||
-
|
||||
Create a new stockable product
|
||||
-
|
||||
!record {model: product.product, id: packingtest}:
|
||||
name: nice product
|
||||
!record {model: product.product, id: product1}:
|
||||
name: Nice product
|
||||
type: product
|
||||
categ_id: product.product_category_1
|
||||
list_price: 100.0
|
||||
standard_price: 70.0
|
||||
seller_ids:
|
||||
- delay: 1
|
||||
name: base.res_partner_2
|
||||
min_qty: 2.0
|
||||
qty: 5.0
|
||||
uom_id: product.product_uom_unit
|
||||
uom_po_id: product.product_uom_unit
|
||||
-
|
||||
Create an incoming picking for this product of 300 PCE from suppliers to stock
|
||||
-
|
||||
!record{model: stock.picking}: |
|
||||
!record {model: stock.picking, id: pick1}:
|
||||
name: Incoming picking
|
||||
partner_id: base.res_partner_2
|
||||
picking_type_id: picking_type_in
|
||||
move_lines:
|
||||
- product_id: product1
|
||||
product_uom_qty: 300.00
|
||||
location_id: stock_location_suppliers
|
||||
location_dest_id: stock_location_stock
|
||||
-
|
||||
Create move linked to picking
|
||||
-
|
||||
!record{model:stock.move, id:}
|
||||
-
|
||||
Confirm picking
|
||||
Confirm and assign picking and prepare partial
|
||||
-
|
||||
!python {model: stock.picking}: |
|
||||
self.action_confirm(cr, uid, ref())
|
||||
self.action_confirm(cr, uid, [ref('pick1')], context=context)
|
||||
self.do_prepare_partial(cr, uid, [ref('pick1')], context=context)
|
||||
-
|
||||
Put 120 pieces on Pallet 1 (package), 120 pieces on Pallet 2 with lot A and 60 pieces on Pallet 3
|
||||
-
|
||||
!python {model: stock.picking}: |
|
||||
#Change quantity of first to 120 and create 2 others quant operations
|
||||
record = self.browse(cr, uid, ref('pick1'), context=context)
|
||||
stock_pack = self.pool.get('stock.pack.operation')
|
||||
stock_quant_pack = self.pool.get('stock.quant.package')
|
||||
#create lot A
|
||||
lot_a = self.pool.get('stock.production.lot').create(cr, uid, {'name': 'Lot A', 'product_id': ref('product1')}, context=context)
|
||||
#create package
|
||||
package1 = stock_quant_pack.create(cr, uid, {'name': 'Pallet 1'}, context=context)
|
||||
package2 = stock_quant_pack.create(cr, uid, {'name': 'Pallet 2'}, context=context)
|
||||
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, {'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)
|
||||
-
|
||||
!python {model: stock.picking}: |
|
||||
self.rereserve(cr, uid, [ref('pick1')], context=context)
|
||||
picking = self.browse(cr, uid, ref('pick1'), context=context)
|
||||
move_reco = picking.move_lines[0]
|
||||
assert move_reco.remaining_qty == 300.0, "Remaining quantities should not change upon receiving"
|
||||
-
|
||||
Transfer the reception
|
||||
-
|
||||
!python {model: stock.picking}: |
|
||||
self.do_partial(cr, uid, [ref('pick1')], context=context)
|
||||
-
|
||||
Check the system created 3 quants one with 120 pieces on pallet 1, one with 120 pieces on pallet 2 with lot A and 60 pieces on pallet 3
|
||||
-
|
||||
!python {model: stock.quant}: |
|
||||
reco_id = self.search(cr ,uid , [('product_id','=',ref('product1'))], context=context)
|
||||
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 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':
|
||||
assert rec.qty == 60, "Should have 60 pieces on pallet 3"
|
||||
-
|
||||
Check there is no backorder or extra moves created
|
||||
-
|
||||
!python {model: stock.picking}: |
|
||||
picking = self.browse(cr, uid, ref('pick1'), context=context)
|
||||
backorder = self.search(cr, uid, [('backorder_id', '=', ref('pick1'))])
|
||||
assert not backorder, ""
|
||||
#Check extra moves created
|
||||
assert len(picking.move_lines) == 1, ""
|
||||
-
|
||||
Make a delivery order of 300 pieces to the customer
|
||||
-
|
||||
!record {model: stock.picking, id: delivery_order1}:
|
||||
name: outgoing picking
|
||||
partner_id: base.res_partner_4
|
||||
picking_type_id: stock.picking_type_out
|
||||
move_lines:
|
||||
- product_id: product1
|
||||
product_uom_qty: 300.00
|
||||
location_id: stock_location_stock
|
||||
location_dest_id: stock_location_customers
|
||||
-
|
||||
Assign and confirm
|
||||
-
|
||||
!python {model: stock.picking}: |
|
||||
self.action_confirm(cr, uid, [ref('delivery_order1')], context=context)
|
||||
self.action_assign(cr, uid, [ref('delivery_order1')])
|
||||
self.force_assign(cr, uid, [ref('delivery_order1')], context=context)
|
||||
-
|
||||
Instead of doing the 300 pieces, you decide to take pallet 1 (do not mention product in operation here) and 20 pieces from lot A and 10 pieces from pallet 3
|
||||
-
|
||||
!python {model: stock.picking}: |
|
||||
stock_pack = self.pool.get('stock.pack.operation')
|
||||
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' 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' 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
|
||||
-
|
||||
!python {model: stock.quant}: |
|
||||
reco_id = self.search(cr ,uid , [('product_id','=',ref('product1'))], context=context)
|
||||
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"
|
||||
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
|
||||
-
|
||||
!python {model: stock.picking}: |
|
||||
picking = self.browse(cr, uid, ref('delivery_order1'), context=context)
|
||||
backorder = self.search(cr, uid, [('backorder_id.id', '=', picking.id)], context=context)
|
||||
assert backorder, "Backorder should have been created"
|
||||
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})
|
||||
-
|
||||
Process this backorder
|
||||
-
|
||||
!python {model: stock.picking}: |
|
||||
picking = self.browse(cr, uid, ref('delivery_order1'), context=context)
|
||||
backorder = self.search(cr, uid, [('backorder_id.id', '=', picking.id)], context=context)
|
||||
backorder_id = self.browse(cr, uid, backorder, context=context)
|
||||
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
|
||||
-
|
||||
!python {model: stock.quant}: |
|
||||
reco_id = self.search(cr ,uid , [('product_id','=',ref('product1'))], context=context)
|
||||
for rec in self.browse(cr, uid, reco_id, context=context):
|
||||
if rec.lot_id.name == 'Lot B':
|
||||
assert rec.qty != -20, ""
|
||||
|
||||
@@ -1,22 +1,40 @@
|
||||
-
|
||||
Create new global procurement rule from Stock -> Output
|
||||
-
|
||||
!record {model: procurement.rule, ref:}
|
||||
location_id: stock
|
||||
location_dest_id: output
|
||||
!record {model: procurement.rule, id: global_proc_rule}:
|
||||
name: Stock -> output
|
||||
action: move
|
||||
picking_type_id: stock.picking_type_out
|
||||
location_src_id: stock.stock_location_stock
|
||||
location_id: stock.stock_location_output
|
||||
-
|
||||
Create Delivery Order from Output -> Customer
|
||||
-
|
||||
!record {model: stock.picking, ref:}
|
||||
!record {model: stock.picking, id: pick_output}:
|
||||
name: Delivery order for procurement
|
||||
partner_id: base.res_partner_2
|
||||
picking_type_id: stock.picking_type_out
|
||||
move_lines:
|
||||
- product_id: product.product_product_3
|
||||
product_uom_qty: 10.00
|
||||
location_id: stock.stock_location_output
|
||||
location_dest_id: stock.stock_location_customers
|
||||
procure_method: make_to_order
|
||||
-
|
||||
Confirm delivery order
|
||||
Confirm delivery order.
|
||||
-
|
||||
|
||||
!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 procurement was created in output (as there is the global procurement rule) related to this delivery order
|
||||
Check a picking was created from stock to output.
|
||||
-
|
||||
|
||||
-
|
||||
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"
|
||||
|
||||
@@ -1,105 +1,55 @@
|
||||
-
|
||||
I confirm outgoing shipment of 130 kgm Ice-cream.
|
||||
-
|
||||
!workflow {model: stock.picking, action: button_confirm, 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 vitual 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.
|
||||
-
|
||||
!workflow {model: stock.picking, action: button_confirm, ref: incomming_shipment}
|
||||
!python {model: stock.picking}: |
|
||||
self.action_confirm(cr, uid, [ref("incomming_shipment")])
|
||||
-
|
||||
I receive 40kgm Ice-cream so I make backorder of incomming shipment for 40 kgm.
|
||||
I receive 40kgm Ice-cream so It will make backorder of incomming shipment for 10 kgm.
|
||||
-
|
||||
!python {model: stock.partial.picking}: |
|
||||
!python {model: stock.picking}: |
|
||||
pick = self.browse(cr, uid, ref("incomming_shipment"), context=context)
|
||||
self.pool.get('stock.pack.operation').create(cr, uid, {
|
||||
'picking_id': pick.id,
|
||||
'product_id': ref('product_icecream'),
|
||||
'product_uom_id': ref('product.product_uom_kgm'),
|
||||
'product_qty': 40
|
||||
})
|
||||
context.update({'active_model': 'stock.picking', 'active_id': ref('incomming_shipment'), 'active_ids': [ref('incomming_shipment')]})
|
||||
pick.do_partial(context=context)
|
||||
-
|
||||
!record {model: stock.partial.picking, id: partial_incomming}:
|
||||
move_ids:
|
||||
- quantity: 40
|
||||
product_id: product_icecream
|
||||
product_uom: product.product_uom_kgm
|
||||
move_id: incomming_shipment_icecream
|
||||
location_id: location_convenience_shop
|
||||
location_dest_id: location_refrigerator
|
||||
-
|
||||
!python {model: stock.partial.picking }: |
|
||||
self.do_partial(cr, uid, [ref('partial_incomming')], context=context)
|
||||
-
|
||||
I check backorder shipment after received partial shipment.
|
||||
I check backorder shipment after received partial shipment and check remaining shipment.
|
||||
-
|
||||
!python {model: stock.picking}: |
|
||||
shipment = self.browse(cr, uid, ref("incomming_shipment"))
|
||||
backorder = shipment.backorder_id
|
||||
assert backorder, "Backorder should be created after partial shipment."
|
||||
assert backorder.state == 'done', "Backorder should be close after received."
|
||||
for move_line in shipment.move_lines:
|
||||
assert move_line.product_qty == 40, "Qty in shipment does not correspond."
|
||||
assert move_line.state == 'done', "Move line of shipment should be closed."
|
||||
backorder_id = self.search(cr, uid, [('backorder_id', '=', ref("incomming_shipment"))],context=context)
|
||||
backorder = self.browse(cr, uid, backorder_id)[0]
|
||||
for move_line in backorder.move_lines:
|
||||
assert move_line.product_qty == 40, "Qty in backorder does not correspond."
|
||||
assert move_line.state == 'done', "Move line of backorder should be closed."
|
||||
assert move_line.product_qty == 10, "Qty in backorder does not correspond."
|
||||
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.
|
||||
-
|
||||
!record {model: stock.partial.picking, id: partial_incomming}:
|
||||
move_ids:
|
||||
- quantity: 10
|
||||
product_id: product_icecream
|
||||
product_uom: product.product_uom_kgm
|
||||
move_id: incomming_shipment_icecream
|
||||
location_id: location_convenience_shop
|
||||
location_dest_id: location_refrigerator
|
||||
-
|
||||
!python {model: stock.partial.picking }: |
|
||||
self.do_partial(cr, uid, [ref('partial_incomming')], context=context)
|
||||
|
||||
-
|
||||
I check incomming shipment after received.
|
||||
I receive the remaining 10kgm Ice-cream from the backorder.
|
||||
-
|
||||
!python {model: stock.picking}: |
|
||||
shipment = 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': backorder.id,
|
||||
'product_id': ref('product_icecream'),
|
||||
'product_uom_id': ref('product.product_uom_kgm'),
|
||||
'product_qty': 10
|
||||
})
|
||||
backorder.do_partial(context=context)
|
||||
-
|
||||
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."
|
||||
|
||||
-
|
||||
I return last incomming shipment for 10 kgm Ice-cream.
|
||||
-
|
||||
!python {model: stock.return.picking }: |
|
||||
# this work without giving the id of the picking to return, magically, thanks to the context
|
||||
self.create_returns(cr, uid, [ref('return_incomming')], context=context)
|
||||
-
|
||||
I cancel incomming shipment after return it.
|
||||
-
|
||||
!python {model: stock.picking}: |
|
||||
# the cancel is not on the return, but on the incomming shipment (which now has a quantity of 10, thanks to the
|
||||
# backorder). This situation is a little weird as we returned a move that we finally cancelled... As result, only
|
||||
# 30Kg from the original 50Kg will be counted in the stock (50 - 10 (cancelled quantity) - 10 (returned quantity))
|
||||
self.action_cancel(cr, uid, [ref("incomming_shipment")], context=context)
|
||||
-
|
||||
I make invoice of backorder of incomming shipment.
|
||||
-
|
||||
!python {model: stock.invoice.onshipping}: |
|
||||
shipment = self.pool.get('stock.picking').browse(cr, uid, ref("incomming_shipment"))
|
||||
context.update({'active_model': 'stock.picking', 'active_id': shipment.backorder_id.id, 'active_ids': [shipment.backorder_id.id]})
|
||||
-
|
||||
I check available stock after received incomming shipping. (removed invoicing here)
|
||||
-
|
||||
!python {model: product.product}: |
|
||||
product = self.browse(cr, uid, ref('product_icecream'), context=context)
|
||||
assert product.qty_available == 140, "Stock does not correspond."
|
||||
assert product.virtual_available == 0, "Vitual stock does not correspond."
|
||||
@@ -31,6 +31,7 @@
|
||||
target="new"
|
||||
key2="client_action_multi"
|
||||
id="action_procurement_compute"/>
|
||||
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
|
||||
@@ -21,5 +21,6 @@
|
||||
|
||||
import product
|
||||
import stock_account
|
||||
import stock
|
||||
import wizard
|
||||
|
||||
|
||||
@@ -49,6 +49,7 @@ Dashboard / Reports for Warehouse Management will include:
|
||||
'data': [
|
||||
'stock_account_data.xml',
|
||||
'wizard/stock_change_standard_price_view.xml',
|
||||
'wizard/stock_invoice_onshipping_view.xml',
|
||||
'product_data.xml',
|
||||
'product_view.xml',
|
||||
'stock_account_view.xml',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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')]}"/>
|
||||
|
||||
@@ -22,6 +22,66 @@
|
||||
from openerp.osv import fields, osv
|
||||
from openerp.tools.translate import _
|
||||
|
||||
#----------------------------------------------------------
|
||||
# Procurement Rule
|
||||
#----------------------------------------------------------
|
||||
class procurement_rule(osv.osv):
|
||||
_inherit = 'procurement.rule'
|
||||
_columns= {
|
||||
'invoice_state': fields.selection([
|
||||
("invoiced", "Invoiced"),
|
||||
("2binvoiced", "To Be Invoiced"),
|
||||
("none", "Not Applicable")], "Invoice Status",
|
||||
required=False),
|
||||
}
|
||||
|
||||
#----------------------------------------------------------
|
||||
# Procurement Order
|
||||
#----------------------------------------------------------
|
||||
|
||||
|
||||
class procurement_order(osv.osv):
|
||||
_inherit = "procurement.order"
|
||||
_columns = {
|
||||
'invoice_state': fields.selection(
|
||||
[("invoiced", "Invoiced"),
|
||||
("2binvoiced", "To Be Invoiced"),
|
||||
("none", "Not Applicable")
|
||||
], "Invoice Control", required=True),
|
||||
}
|
||||
|
||||
def _run_move_create(self, cr, uid, procurement, context=None):
|
||||
res = super(procurement_order, self)._run_move_create(cr, uid, procurement, context=context)
|
||||
res.update({'invoice_state': (procurement.rule_id.invoice_state in ('none', False) and procurement.invoice_state or procurement.rule_id.invoice_state) or 'none'})
|
||||
return res
|
||||
|
||||
_defaults = {
|
||||
'invoice_state': 'none'
|
||||
}
|
||||
|
||||
|
||||
#----------------------------------------------------------
|
||||
# Move
|
||||
#----------------------------------------------------------
|
||||
|
||||
class stock_move(osv.osv):
|
||||
_inherit = "stock.move"
|
||||
_columns = {
|
||||
'invoice_state': fields.selection([("invoiced", "Invoiced"),
|
||||
("2binvoiced", "To Be Invoiced"),
|
||||
("none", "Not Applicable")], "Invoice Control",
|
||||
select=True, required=True, track_visibility='onchange',
|
||||
states={'draft': [('readonly', False)]}),
|
||||
}
|
||||
_defaults= {
|
||||
'invoice_state': lambda *args, **argv: 'none'
|
||||
}
|
||||
|
||||
|
||||
|
||||
#----------------------------------------------------------
|
||||
# Picking
|
||||
#----------------------------------------------------------
|
||||
|
||||
class stock_picking(osv.osv):
|
||||
_inherit = 'stock.picking'
|
||||
@@ -30,22 +90,13 @@ class stock_picking(osv.osv):
|
||||
for pick in self.browse(cr, uid, ids, context=context):
|
||||
result[pick.id] = 'none'
|
||||
for move in pick.move_lines:
|
||||
if move.procurement_id:
|
||||
if move.procurement_id.invoice_state=='invoiced':
|
||||
result[pick.id] = 'invoiced'
|
||||
elif move.procurement_id.invoice_state=='2binvoiced':
|
||||
result[pick.id] = '2binvoiced'
|
||||
break
|
||||
if move.invoice_state=='invoiced':
|
||||
result[pick.id] = 'invoiced'
|
||||
elif move.invoice_state=='2binvoiced':
|
||||
result[pick.id] = '2binvoiced'
|
||||
break
|
||||
return result
|
||||
|
||||
def __get_picking_procurement(self, cr, uid, ids, context={}):
|
||||
result = {}
|
||||
for proc in self.pool.get('procurement.order').browse(cr, uid, ids, context=context):
|
||||
for move in proc.move_ids:
|
||||
if move.picking_id:
|
||||
result[move.picking_id.id] = True
|
||||
return result.keys()
|
||||
|
||||
def __get_picking_move(self, cr, uid, ids, context={}):
|
||||
res = []
|
||||
for move in self.pool.get('stock.move').browse(cr, uid, ids, context=context):
|
||||
@@ -58,14 +109,17 @@ class stock_picking(osv.osv):
|
||||
("invoiced", "Invoiced"),
|
||||
("2binvoiced", "To Be Invoiced"),
|
||||
("none", "Not Applicable")
|
||||
], string="Invoice Control", required=False,
|
||||
], string="Invoice Control", required=True,
|
||||
|
||||
store={
|
||||
'procurement.order': (__get_picking_procurement, ['invoice_state'], 10),
|
||||
'stock.picking': (lambda self, cr, uid, ids, c={}: ids, ['state'], 10),
|
||||
'stock.move': (__get_picking_move, ['picking_id'], 10),
|
||||
},
|
||||
),
|
||||
}
|
||||
_defaults = {
|
||||
'invoice_state': lambda *args, **argv: 'none'
|
||||
}
|
||||
|
||||
def action_invoice_create(self, cr, uid, ids, journal_id=False, group=False, type='out_invoice', context=None):
|
||||
""" Creates invoice based on the invoice state selected for picking.
|
||||
@@ -35,6 +35,7 @@ import logging
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
|
||||
#----------------------------------------------------------
|
||||
# Stock Location
|
||||
#----------------------------------------------------------
|
||||
@@ -69,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)
|
||||
@@ -197,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)
|
||||
|
||||
@@ -16,5 +16,41 @@
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
<record id="view_picking_inherit_form2" model="ir.ui.view">
|
||||
<field name="name">stock.picking.form.inherit</field>
|
||||
<field name="model">stock.picking</field>
|
||||
<field name="inherit_id" ref="stock.view_picking_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//button[@name='do_prepare_partial']" position="after">
|
||||
<button name="%(action_stock_invoice_onshipping)d" string="Create Invoice" attrs="{'invisible': ['|',('state','<>','done'),('invoice_state','<>','2binvoiced')]}" type="action" class="oe_highlight" groups="base.group_user"/>
|
||||
<button name="%(action_stock_invoice_onshipping)d" string="Refund Invoice" attrs="{'invisible': ['|',('state','<>','done'),('invoice_state','<>','invoiced')]}" type="action" class="oe_highlight" groups="base.group_user" context="{'inv_type': 'out_refund'}"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='move_type']" position="after">
|
||||
<field name="invoice_state" groups="account.group_account_invoice"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_move_form_inherit" model="ir.ui.view">
|
||||
<field name="name">stock.move.form.inherit</field>
|
||||
<field name="model">stock.move</field>
|
||||
<field name="inherit_id" ref="stock.view_move_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='picking_type_id']" position="after">
|
||||
<field name="invoice_state" groups="account.group_account_invoice"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="view_procurement_rule_form_inherit">
|
||||
<field name="name">stock.procurement.rule.inherit.form</field>
|
||||
<field name="model">procurement.rule</field>
|
||||
<field name="inherit_id" ref="stock.view_procurement_rule_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="location_id" position="after">
|
||||
<field name="invoice_state"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
</data>
|
||||
</openerp>
|
||||
|
||||
@@ -20,3 +20,4 @@
|
||||
##############################################################################
|
||||
|
||||
import stock_change_standard_price
|
||||
import stock_invoice_onshipping
|
||||
|
||||
@@ -38,7 +38,12 @@ This adds a route on the sales order and sales order line (mini module)
|
||||
'init_xml': [],
|
||||
'update_xml': ['stock_complex_routes.xml'],
|
||||
'demo_xml': [],
|
||||
'test': [],
|
||||
'test': [
|
||||
'test/crossdock.yml',
|
||||
'test/dropship.yml',
|
||||
'test/procurementexception.yml',
|
||||
'test/lifo_price.yml'
|
||||
],
|
||||
'installable': True,
|
||||
'auto_install': True,
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
-
|
||||
Create new product without any routes
|
||||
-
|
||||
!record {model: product.product, id: cross_shop_product}:
|
||||
name: PCE
|
||||
type: product
|
||||
categ_id: product.product_category_1
|
||||
list_price: 100.0
|
||||
standard_price: 70.0
|
||||
seller_ids:
|
||||
- delay: 1
|
||||
name: base.res_partner_2
|
||||
min_qty: 2.0
|
||||
qty: 5.0
|
||||
type: product
|
||||
uom_id: product.product_uom_unit
|
||||
uom_po_id: product.product_uom_unit
|
||||
-
|
||||
Create a sales order with a line of 100 PCE incoming shipment, with route_id crossdock shipping.
|
||||
-
|
||||
!record {model: sale.order, id: sale_order_crossdock_shpng}:
|
||||
partner_id: base.res_partner_4
|
||||
note: Create Sales order
|
||||
order_line:
|
||||
- product_id: cross_shop_product
|
||||
product_uom_qty: 100.00
|
||||
route_id: stock_location.route_warehouse0_crossdock
|
||||
-
|
||||
Confirm sales order
|
||||
-
|
||||
!workflow {model: sale.order, action: order_confirm, ref: sale_order_crossdock_shpng}
|
||||
-
|
||||
Check a quotation was created to a certain supplier and confirm so it becomes a confirmed purchase order
|
||||
-
|
||||
!python {model: purchase.order}: |
|
||||
po_id = self.search(cr, uid, [('partner_id', '=', ref('base.res_partner_2'))])
|
||||
self.wkf_confirm_order(cr, uid, po_id)
|
||||
@@ -1,53 +1,68 @@
|
||||
-
|
||||
Create new product without any routes
|
||||
Create new product without any routes
|
||||
-
|
||||
!record {model:product.product, id:}
|
||||
!record {model: product.product, id: drop_shop_product}:
|
||||
name: Pen drive
|
||||
type: product
|
||||
categ_id: product.product_category_1
|
||||
list_price: 100.0
|
||||
standard_price: 70.0
|
||||
seller_ids:
|
||||
- delay: 1
|
||||
name: base.res_partner_2
|
||||
min_qty: 2.0
|
||||
qty: 5.0
|
||||
uom_id: product.product_uom_unit
|
||||
uom_po_id: product.product_uom_unit
|
||||
-
|
||||
Create a sales order with a line of 200 PCE incoming shipment, with route_id drop shipping
|
||||
Create a sales order with a line of 200 PCE incoming shipment, with route_id drop shipping.
|
||||
-
|
||||
!record {model: sale.order, id: } :
|
||||
customer:
|
||||
- product_id:
|
||||
product_qty: 200
|
||||
product_uom:
|
||||
supplier_ids:
|
||||
|
||||
|
||||
!record {model: sale.order, id: sale_order_drp_shpng}:
|
||||
partner_id: base.res_partner_2
|
||||
note: Create sale order for drop shipping
|
||||
payment_term: account.account_payment_term
|
||||
order_line:
|
||||
- product_id: drop_shop_product
|
||||
product_uom_qty: 200
|
||||
price_unit: 100.00
|
||||
route_id: route_drop_shipping
|
||||
-
|
||||
Confirm sales order
|
||||
Confirm sales order
|
||||
-
|
||||
!python{model: sale.order}: |
|
||||
self.browse(cr, uid, ref()).action_b
|
||||
!workflow {model: sale.order, action: order_confirm, ref: sale_order_drp_shpng}
|
||||
-
|
||||
Check the sales order created a procurement group which has a procurement of 200 pieces
|
||||
Check the sales order created a procurement group which has a procurement of 200 pieces
|
||||
-
|
||||
!python{model: procurement.group}: |
|
||||
proc_group = self.search(cr, uid, [('sale_id', '=', ref())])
|
||||
assert self.browse(cr, uid, proc_group)[0].procurement_ids[0].product_qty == 200
|
||||
!python {model: procurement.group}: |
|
||||
sale_record = self.pool.get("sale.order").browse(cr, uid, ref('sale_order_drp_shpng'))
|
||||
assert self.browse(cr, uid, sale_record.procurement_group_id.id).procurement_ids[0].product_qty == 200
|
||||
-
|
||||
Check a quotation was created to a certain supplier and confirm so it becomes a confirmed purchase order
|
||||
Check a quotation was created to a certain supplier and confirm so it becomes a confirmed purchase order
|
||||
-
|
||||
!python {model: purchase.order}: |
|
||||
from openerp import netsvc
|
||||
sale_record = self.pool.get("sale.order").browse(cr, uid, ref('sale_order_drp_shpng'))
|
||||
procurement_order = self.pool.get("procurement.group").browse(cr, uid, sale_record.procurement_group_id.id).procurement_ids[0]
|
||||
purchase_id = procurement_order.purchase_id.id
|
||||
wf_service = netsvc.LocalService('workflow')
|
||||
wf_service.trg_validate(uid, 'purchase.order', purchase_id, 'purchase_confirm', cr)
|
||||
-
|
||||
|
||||
Use 'Receive Products' button to immediately view this picking, it should have created a picking with 200 pieces..
|
||||
-
|
||||
!python {model: purchase.order}: |
|
||||
po_id = self.search(cr, uid, [('partner_id', '=', ref('base.res_partner_2'))])
|
||||
self.view_picking(cr, uid, po_id)
|
||||
-
|
||||
Send the 200 pieces.
|
||||
-
|
||||
Check the sales order has one picking with 200 pieces
|
||||
!python {model: stock.picking}: |
|
||||
po_id = self.pool.get('purchase.order').search(cr, uid, [('partner_id', '=', ref('base.res_partner_2'))])
|
||||
picking_id = self.search(cr, uid, [('purchase_id', '=', po_id[0])])
|
||||
self.do_partial(cr, uid, picking_id)
|
||||
-
|
||||
!python{model: sale.order}: |
|
||||
assert len(self.browse(cr, uid, ref()).picking_ids) == 1
|
||||
Check one quant was created in Customers location with 200 pieces and one move in the history_ids
|
||||
-
|
||||
Use 'View Delivery Order' button to immediately view this picking and check shipped of purchase order is False
|
||||
-
|
||||
|
||||
-
|
||||
Send the 200 pieces
|
||||
-
|
||||
!python{model: stock.partial.picking}: |
|
||||
|
||||
|
||||
-
|
||||
Check one quant was created in Customers location with 200 pieces and one move in the move_history_ids
|
||||
-
|
||||
|
||||
-
|
||||
Check the sum of the moves related to this product and the sum of the quants is equal
|
||||
-
|
||||
|
||||
!python {model: stock.quant}: |
|
||||
quant_id = self.search(cr, uid, [('location_id', '=', ref('stock.stock_location_customers')),('qty', '=', 200)])
|
||||
assert len(self.browse(cr, uid, quant_id)[0].history_ids) == 1
|
||||
assert len(quant_id) == 1
|
||||
|
||||
+20
-16
@@ -1,8 +1,12 @@
|
||||
-
|
||||
Set product category removal strategy as LIFO
|
||||
Set product category removal strategy as LIFO
|
||||
-
|
||||
!record {model: product.category, id: product.product_category_1}:
|
||||
removal_strategy: lifo
|
||||
!record {model: product.category, id: product.product_category_001}:
|
||||
name : Lifo Category
|
||||
removal_strategy_ids:
|
||||
- product_categ_id: product.product_category_1
|
||||
method: lifo
|
||||
location_id: stock.stock_location_stock
|
||||
-
|
||||
Set a product as using lifo price
|
||||
-
|
||||
@@ -58,10 +62,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_lifo1")).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}: |
|
||||
order = self.pool.get('purchase.order').browse(cr, uid, ref("purchase_order_lifo1")).picking_ids[0]
|
||||
order.do_partial(context=context)
|
||||
-
|
||||
Check the standard price of the product (lifo icecream)
|
||||
-
|
||||
@@ -74,10 +77,9 @@
|
||||
-
|
||||
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_lifo2")).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}: |
|
||||
order = self.pool.get('purchase.order').browse(cr, uid, ref("purchase_order_lifo2")).picking_ids[0]
|
||||
order.do_partial(context=context)
|
||||
-
|
||||
Check the standard price should not have changed
|
||||
-
|
||||
@@ -87,7 +89,7 @@
|
||||
Let us send some goods
|
||||
-
|
||||
!record {model: stock.picking, id: outgoing_lifo_shipment}:
|
||||
type: out
|
||||
picking_type_id: stock.picking_type_out
|
||||
-
|
||||
Picking needs movement from stock
|
||||
-
|
||||
@@ -95,8 +97,10 @@
|
||||
picking_id: outgoing_lifo_shipment
|
||||
product_id: product_lifo_icecream
|
||||
product_uom: product.product_uom_kgm
|
||||
location_id: stock.stock_location_stock
|
||||
location_dest_id: stock.stock_location_customers
|
||||
product_qty: 20.0
|
||||
type: out
|
||||
picking_type_id: stock.picking_type_out
|
||||
-
|
||||
I assign this outgoing shipment
|
||||
-
|
||||
@@ -105,9 +109,9 @@
|
||||
-
|
||||
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_lifo_shipment")], 'default_type':'out'})
|
||||
self.do_partial(cr, uid, [partial_id])
|
||||
!python {model: stock.picking}: |
|
||||
pick_order = self.pool.get('stock.picking').browse(cr, uid, ref("outgoing_lifo_shipment"))
|
||||
pick_order.do_partial(context=context)
|
||||
-
|
||||
Check standard price became 80 euro
|
||||
-
|
||||
@@ -1,37 +1,63 @@
|
||||
-
|
||||
Create product with no seller_ids
|
||||
-
|
||||
|
||||
-
|
||||
Create a sales order with 1 piece for that product and route crossdock
|
||||
-
|
||||
!record{sale.order}
|
||||
-
|
||||
Confirm sales order
|
||||
-
|
||||
!python {
|
||||
-
|
||||
Check there is a procurement in exception that has the procurement group of the sales order
|
||||
-
|
||||
!python {
|
||||
assert
|
||||
-
|
||||
Adjust the product that it has at least one seller_id
|
||||
-
|
||||
!record{id: model: product.product) or in !python
|
||||
-
|
||||
Run the Scheduler
|
||||
-
|
||||
!python
|
||||
-
|
||||
Check the status changed there is no procurement order in exception any more from that procurement group
|
||||
-
|
||||
!python{model: procurement.order}
|
||||
search procurement from
|
||||
-
|
||||
Check a purchase quotation was created
|
||||
-
|
||||
|
||||
|
||||
|
||||
|
||||
I create a product with no supplier define for it.
|
||||
-
|
||||
!record {model: product.product, id: product_with_no_seller}:
|
||||
name: 'product with no seller'
|
||||
list_price: 20.00
|
||||
standard_price: 15.00
|
||||
categ_id: product.product_category_1
|
||||
-
|
||||
I create a sales order with this product with route crossdock.
|
||||
-
|
||||
!record {model: sale.order, id: sale_order_route_crossdock}:
|
||||
partner_id: base.res_partner_2
|
||||
partner_invoice_id: base.res_partner_address_3
|
||||
partner_shipping_id: base.res_partner_address_3
|
||||
note: crossdock route
|
||||
payment_term: account.account_payment_term
|
||||
order_line:
|
||||
- product_id: product_with_no_seller
|
||||
product_uom_qty: 1
|
||||
route_id: stock_location.route_warehouse0_crossdock
|
||||
-
|
||||
I confirm the sales order.
|
||||
-
|
||||
!workflow {model: sale.order, ref: sale_order_route_crossdock, action: order_confirm}
|
||||
-
|
||||
I check there is a procurement in exception that has the procurement group of the sales order created before.
|
||||
-
|
||||
!python {model: procurement.order}: |
|
||||
self.run_scheduler(cr, uid)
|
||||
sale_id = self.pool.get('sale.order').browse(cr, uid, ref("sale_order_route_crossdock"))
|
||||
proc_id = self.search(cr, uid, [('group_id.name', '=', sale_id.name), ('state', '=', 'exception')])
|
||||
assert proc_id, 'No Procurement!'
|
||||
-
|
||||
I set the at least one supplier on the product.
|
||||
-
|
||||
!record {model: product.product, id: product_with_no_seller}:
|
||||
seller_ids:
|
||||
- delay: 1
|
||||
name: base.res_partner_2
|
||||
min_qty: 2.0
|
||||
-
|
||||
I run the Procurement.
|
||||
-
|
||||
!python {model: procurement.order}: |
|
||||
sale_id = self.pool.get('sale.order').browse(cr, uid, ref("sale_order_route_crossdock"))
|
||||
proc_id = self.search(cr, uid, [('group_id.name', '=', sale_id.name), ('state', '=', 'exception')])
|
||||
self.run(cr, uid, proc_id)
|
||||
-
|
||||
I check the status changed there is no procurement order in exception any more from that procurement group
|
||||
-
|
||||
!python {model: procurement.order}: |
|
||||
sale_id = self.pool.get('sale.order').browse(cr, uid, ref("sale_order_route_crossdock"))
|
||||
proc_id = self.search(cr, uid, [('group_id.name', '=', sale_id.name), ('state', '=', 'exception')])
|
||||
assert not proc_id, 'Procurement should be in running state'
|
||||
-
|
||||
I check a purchase quotation was created.
|
||||
-
|
||||
!python {model: procurement.order}: |
|
||||
sale_id = self.pool.get('sale.order').browse(cr, uid, ref("sale_order_route_crossdock"))
|
||||
proc_ids = self.search(cr, uid, [('group_id.name', '=', sale_id.name)])
|
||||
purchase_id = [proc.purchase_id for proc in self.browse(cr, uid, proc_ids) if proc.purchase_id]
|
||||
assert purchase_id, 'No Purchase Quotation is created'
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
product_uom: product.product_uom_unit
|
||||
location_dest_id: stock.stock_location_customers
|
||||
move_type: direct
|
||||
type: out
|
||||
picking_type_id: stock.picking_type_out
|
||||
-
|
||||
I need to check the availability of the product so I make my picking order for processing later.
|
||||
-
|
||||
|
||||
@@ -28,6 +28,7 @@ class stock_location_route(osv.osv):
|
||||
_name = 'stock.location.route'
|
||||
_description = "Inventory Routes"
|
||||
_order = 'sequence'
|
||||
|
||||
_columns = {
|
||||
'name': fields.char('Route Name', required=True),
|
||||
'sequence': fields.integer('Sequence'),
|
||||
@@ -41,7 +42,7 @@ class stock_location_route(osv.osv):
|
||||
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_id': fields.many2one('stock.location.route', 'Default Routes', help='Default route through the warehouse', required=True),
|
||||
}
|
||||
|
||||
|
||||
@@ -60,7 +61,7 @@ class stock_location_path(osv.osv):
|
||||
("2binvoiced", "To Be Invoiced"),
|
||||
("none", "Not Applicable")], "Invoice Status",
|
||||
required=True,),
|
||||
'picking_type_id': fields.many2one('stock.picking.type', 'Picking Type', help="This is the picking type associated with the different pickings"),
|
||||
'picking_type_id': fields.many2one('stock.picking.type', 'Picking Type', required=True, help="This is the picking type associated with the different pickings"),
|
||||
'auto': fields.selection(
|
||||
[('auto','Automatic Move'), ('manual','Manual Operation'),('transparent','Automatic No Step Added')],
|
||||
'Automatic Move',
|
||||
@@ -124,18 +125,13 @@ class procurement_rule(osv.osv):
|
||||
'delay': fields.integer('Number of Hours'),
|
||||
'procure_method': fields.selection([('make_to_stock','Make to Stock'),('make_to_order','Make to Order')], 'Procure Method', required=True, help="'Make to Stock': When needed, take from the stock or wait until re-supplying. 'Make to Order': When needed, purchase or produce for the procurement request."),
|
||||
'partner_address_id': fields.many2one('res.partner', 'Partner Address'),
|
||||
'invoice_state': fields.selection([
|
||||
("invoiced", "Invoiced"),
|
||||
("2binvoiced", "To Be Invoiced"),
|
||||
("none", "Not Applicable")], "Invoice Status",
|
||||
required=True,),
|
||||
|
||||
'route_sequence': fields.related('route_id', 'sequence', string='Route Sequence', store={'stock.location.route': (_get_rules, ['sequence'], 10)}),
|
||||
'sequence': fields.integer('Sequence'),
|
||||
'propagate': fields.boolean('Propagate cancel and split', help='If checked, when the previous move of the move (which was generated by a next procurement) is cancelled or split, the move generated by this move will too'),
|
||||
}
|
||||
_defaults = {
|
||||
'procure_method': 'make_to_stock',
|
||||
'invoice_state': 'none',
|
||||
'propagate': True,
|
||||
'delay': 0,
|
||||
}
|
||||
@@ -146,7 +142,7 @@ class procurement_rule(osv.osv):
|
||||
class procurement_order(osv.osv):
|
||||
_inherit = 'procurement.order'
|
||||
_columns = {
|
||||
'route_ids': fields.many2many('stock.location.route', 'stock_location_route_procurement', 'procurement_id', 'route_id', 'Destination route', help="Preferred route to be followed by the procurement order"),
|
||||
'route_ids': fields.many2many('stock.location.route', 'stock_location_route_procurement', 'procurement_id', 'route_id', 'Followed Route', help="Preferred route to be followed by the procurement order"),
|
||||
}
|
||||
|
||||
def _run_move_create(self, cr, uid, procurement, context=None):
|
||||
@@ -172,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
|
||||
|
||||
|
||||
@@ -215,7 +208,7 @@ class product_category(osv.osv):
|
||||
_inherit = 'product.category'
|
||||
_columns = {
|
||||
'route_ids': fields.many2many('stock.location.route', 'stock_location_route_categ', 'categ_id', 'route_id', 'Routes'),
|
||||
#'removal_strategy_ids': fields.one2many('product.removal', 'product_categ_id', 'Removal Strategies'),
|
||||
'removal_strategy_ids': fields.one2many('product.removal', 'product_categ_id', 'Removal Strategies'),
|
||||
#'putaway_strategy_ids': fields.one2many('product.putaway', 'product_categ_id', 'Put Away Strategies'),
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -38,7 +38,6 @@
|
||||
<field name="location_src_id" ref="stock.stock_location_intermediatelocation0"/>
|
||||
<field name="location_id" ref="stock.stock_location_shop0"/>
|
||||
<field name="partner_address_id" ref="base.main_partner"/>
|
||||
<field name="invoice_state">none</field>
|
||||
<field name="company_id" ref="stock.res_company_1"/>
|
||||
<field name="type_proc">move</field>
|
||||
<field eval="0" name="propagate"/>
|
||||
@@ -53,7 +52,6 @@
|
||||
<field name="location_src_id" ref="stock.stock_location_stock"/>
|
||||
<field name="partner_address_id" ref="stock.res_partner_address_41"/>
|
||||
<field name="type_proc">move</field>
|
||||
<field name="invoice_state">none</field>
|
||||
<field name="company_id" ref="base.main_company"/>
|
||||
<field eval="0" name="propagate"/>
|
||||
<field name="procure_method">make_to_stock</field>
|
||||
|
||||
@@ -2,6 +2,25 @@
|
||||
<openerp>
|
||||
<data>
|
||||
|
||||
<record model="ir.ui.view" id="view_procurement_rule_form_inherit">
|
||||
<field name="name">stock.procurement.rule.inherit.form</field>
|
||||
<field name="model">procurement.rule</field>
|
||||
<field name="inherit_id" ref="stock.view_procurement_rule_form"/>
|
||||
|
||||
<field name="arch" type="xml">
|
||||
<field name="location_id" position="after">
|
||||
<field name="route_id"/>
|
||||
<field name="route_sequence"/>
|
||||
<field name="sequence"/>
|
||||
<field name="delay"/>
|
||||
<field name="propagate"/>
|
||||
</field>
|
||||
<field name="group_id" position="before">
|
||||
<field name="procure_method"/>
|
||||
<field name="partner_address_id"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_location_form_inherit" model="ir.ui.view">
|
||||
<field name="name">stock.location.form</field>
|
||||
@@ -108,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>
|
||||
@@ -123,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>
|
||||
@@ -135,7 +154,7 @@
|
||||
<field name="inherit_id" ref="stock.view_warehouse"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='partner_id']" position="after">
|
||||
<field name="route_id"/>
|
||||
<field name="route_id" widget="many2many_tags"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
@@ -170,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>
|
||||
@@ -178,12 +197,12 @@
|
||||
<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 on a
|
||||
location.</p>
|
||||
routes can be assigned to a product, a product category or be fixed
|
||||
on procurement or sales order. </p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
@@ -191,5 +210,17 @@
|
||||
|
||||
<menuitem action="action_routes_form" id="menu_stock_routes"
|
||||
parent="stock.menu_stock_configuration" sequence="11" />
|
||||
|
||||
<!-- Procurement -->
|
||||
<record id="procurement_form_view_inherit" model="ir.ui.view">
|
||||
<field name="name">procurement.order.form.view.inherit</field>
|
||||
<field name="inherit_id" ref="procurement.procurement_form_view"/>
|
||||
<field name="model">procurement.order</field>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='rule_id']" position="after">
|
||||
<field name="route_ids" widget="many2many_tags"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</data>
|
||||
</openerp>
|
||||
|
||||
@@ -32,9 +32,11 @@
|
||||
- invoice_state: none
|
||||
location_dest_id: stock_location.stock_location_qualitytest0
|
||||
location_from_id: stock.stock_location_stock
|
||||
picking_type_id: stock.picking_type_internal
|
||||
- invoice_state: none
|
||||
location_dest_id: stock.stock_location_components
|
||||
location_from_id: stock_location.stock_location_qualitytest0
|
||||
picking_type_id: stock.picking_type_internal
|
||||
-
|
||||
!record {model: product.product, id: product_product_hpcdwriters01}:
|
||||
list_price: 1000.0
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
|
||||
#
|
||||
@@ -15,8 +15,7 @@
|
||||
# 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/>.
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
import stock_invoice_onshipping
|
||||
import stock_multi_warehouse
|
||||
@@ -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:
|
||||
@@ -0,0 +1,127 @@
|
||||
-
|
||||
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
|
||||
child_ids:
|
||||
- name: Area1
|
||||
child_ids:
|
||||
- name: Bin 1
|
||||
- name: Bin 2
|
||||
- name: Bin 3
|
||||
- name: Bin 4
|
||||
- name: Bin 5
|
||||
- name: Bin 6
|
||||
- name: Bin 7
|
||||
- name: Bin 8
|
||||
- name: Bin 9
|
||||
- name: Bin 10
|
||||
- name: Bin 11
|
||||
- name: Bin 12
|
||||
- name: Bin 13
|
||||
- name: Bin 14
|
||||
- name: Bin 15
|
||||
-
|
||||
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 types 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 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
|
||||
-
|
||||
Create warehouse Panama Santiago District
|
||||
-
|
||||
!record {model: stock.warehouse, id: wh_panama_main}:
|
||||
name: Panama Main Warehouse
|
||||
lot_stock_id: location_panama_main
|
||||
-
|
||||
Create Location structure of Warehouse Dubai
|
||||
-
|
||||
!record {model: stock.location, id: location_panama_main}:
|
||||
name: Panama Main Warehouse
|
||||
location_id: location_panama
|
||||
child_ids:
|
||||
- name: Input
|
||||
- name: Output
|
||||
- name: Stock
|
||||
-
|
||||
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
|
||||
Reference in New Issue
Block a user