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

bzr revid: tpa@tinyerp.com-20130910113951-3ner2h0gmi09apvd
This commit is contained in:
Turkesh Patel (Open ERP)
2013-09-10 17:09:51 +05:30
57 changed files with 982 additions and 572 deletions
@@ -192,7 +192,7 @@
- company_id: base.main_company
location_id: stock.stock_location_stock
product_id: product.product_product_3
product_qty: 1.0
product_uom_qty: 1.0
product_uom: product.product_uom_unit
location_dest_id: stock.stock_location_customers
invoice_state: 2binvoiced
@@ -235,9 +235,7 @@
!python {model: stock.invoice.onshipping}: |
wiz_id = self.create(cr, uid, {'invoice_date': '2013-03-04', 'journal_id': ref('account.sales_journal')},
{'active_ids': [ref("stock_picking_out001")], "active_model": "stock.picking"})
self.create_invoice(cr, uid, [wiz_id], {"lang": "en_US",
"search_default_available": 1, "tz": False, "active_model": "stock.picking",
"contact_display": "partner", "active_ids": [ref("stock_picking_out001")], "active_id": ref("stock_picking_out001")})
self.create_invoice(cr, uid, [wiz_id], {"active_ids": [ref("stock_picking_out001")], "active_id": ref("stock_picking_out001")})
-
I check that the customer invoice is created successfully.
-
+1 -1
View File
@@ -32,7 +32,7 @@ You can define your own carrier and delivery grids for prices. When creating
invoices from picking, OpenERP is able to add and compute the shipping line.
""",
'author': 'OpenERP SA',
'depends': ['sale', 'purchase', 'stock_account'],
'depends': ['sale_stock', 'purchase'],
'data': [
'security/ir.model.access.csv',
'delivery_report.xml',
+11
View File
@@ -28,8 +28,19 @@ class shipping(report_sxw.rml_parse):
super(shipping, self).__init__(cr, uid, name, context=context)
self.localcontext.update({
'time': time,
'get_partner_invoice_info': self.get_partner_invoice_info,
})
def get_partner_invoice_info(self, origin):
"""This method is used to get information of invoiced partner.
:param: origin: origin of picking
:return: tuple contaning partner name and address of invoiced partner
"""
sale_obj = self.pool.get('sale.order')
sale_id = sale_obj.search(self.cr, self.uid, [('name', '=', origin)])[0]
partner_invoice_id = sale_obj.browse(self.cr, self.uid, sale_id).partner_invoice_id
return partner_invoice_id.name, self.pool.get('res.partner')._display_address(self.cr, self.uid, partner_invoice_id)
report_sxw.report_sxw('report.sale.shipping','stock.picking','addons/delivery/report/shipping.rml',parser=shipping)
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+8 -17
View File
@@ -107,14 +107,11 @@
<story>
<pto>
<pto_header>
<blockTable colWidths="370.0,85.0,82.0" repeatRows="1" style="Table3">
<blockTable colWidths="455.0,82.0" repeatRows="1" style="Table3">
<tr>
<td>
<para style="terp_tblheader_Details">Description</para>
</td>
<td>
<para style="terp_tblheader_Details_Centre">Lot</para>
</td>
<td>
<para style="terp_tblheader_Details_Right">Quantity</para>
</td>
@@ -130,12 +127,12 @@
<tr>
<td>
<para style="terp_default_Bold_9">Invoiced to</para>
<para style="terp_default_9">[[ o.sale_id and o.sale_id.partner_invoice_id and o.sale_id.partner_invoice_id.name or '']]</para>
<para style="terp_default_9">[[ o.sale_id and o.sale_id.partner_invoice_id and display_address(o.sale_id.partner_invoice_id) ]]</para>
<para style="terp_default_9">[[ o.origin and get_partner_invoice_info(o.origin)[0] or '' ]]</para>
<para style="terp_default_9">[[ o.origin and get_partner_invoice_info(o.origin)[1] or '' ]]</para>
</td>
<td>
<para style="terp_default_9">[[ o.partner_id and o.partner_id and o.partner_id.name or '' ]]</para>
<para style="terp_default_9">[[ o.partner_id and o.partner_id and display_address(o.partner_id) ]]</para>
<para style="terp_default_9">[[ o.partner_id and o.partner_id.name or '' ]]</para>
<para style="terp_default_9">[[ o.partner_id and display_address(o.partner_id) ]]</para>
</td>
</tr>
</blockTable>
@@ -165,7 +162,7 @@
<blockTable colWidths="126.0,103.0,103.0,103.0,103.0" style="Table2">
<tr>
<td>
<para style="terp_default_Centre_8">[[ o.sale_id and o.sale_id.name ]]</para>
<para style="terp_default_Centre_8">[[ o.origin or '' ]]</para>
</td>
<td>
<para style="terp_default_Centre_8">[[ formatLang(o.date,date_time=True) ]]</para>
@@ -184,14 +181,11 @@
<para style="Standard">
<font color="white"> </font>
</para>
<blockTable colWidths="370.0,85.0,82.0" repeatRows="1" style="Table3">
<blockTable colWidths="455.0,82.0" repeatRows="1" style="Table3">
<tr>
<td>
<para style="terp_tblheader_Details">Description</para>
</td>
<td>
<para style="terp_tblheader_Details_Centre">Lot</para>
</td>
<td>
<para style="terp_tblheader_Details_Right">Quantity</para>
</td>
@@ -199,14 +193,11 @@
</blockTable>
<section>
<para style="terp_default_1">[[repeatIn(o.move_lines,'line')]]</para>
<blockTable colWidths="370.0,85.0,83.0" style="Table4">
<blockTable colWidths="455.0,82.0" style="Table4">
<tr>
<td>
<para style="terp_default_9">[[line.product_id.code ]] [[ line.product_id and line.product_id.name or '']]</para>
</td>
<td>
<para style="terp_default_Centre_9">[[ (line.prodlot_id and (line.prodlot_id.name + (line.prodlot_id.ref and ('/' + line.prodlot_id.ref) or ''))) or ' ' ]]</para>
</td>
<td>
<para style="terp_default_Right_9">[[ formatLang(line.product_qty) ]] [[ line.product_uom and line.product_uom.name ]]</para>
</td>
+13 -18
View File
@@ -30,7 +30,6 @@ class stock_picking(osv.osv):
def _cal_weight(self, cr, uid, ids, name, args, context=None):
res = {}
uom_obj = self.pool.get('product.uom')
for picking in self.browse(cr, uid, ids, context=context):
total_weight = total_weight_net = 0.00
@@ -119,26 +118,22 @@ class stock_picking(osv.osv):
'invoice_line_tax_id': [(6, 0, taxes_ids)],
}
def action_invoice_create(self, cr, uid, ids, journal_id=False,
group=False, type='out_invoice', context=None):
invoice_obj = self.pool.get('account.invoice')
picking_obj = self.pool.get('stock.picking')
def _create_invoice_from_picking(self, cr, uid, picking, vals, context=None):
''' This function simply creates the invoice from the given values. It is overriden in delivery module to add the delivery costs. picking is a browse_record
'''
invoice_line_obj = self.pool.get('account.invoice.line')
result = super(stock_picking, self).action_invoice_create(cr, uid,
ids, journal_id=journal_id, group=group, type=type,
context=context)
for picking in picking_obj.browse(cr, uid, result, context=context):
invoice = invoice_obj.browse(cr, uid, picking.id, context=context)
invoice_line = self._prepare_shipping_invoice_line(cr, uid, picking, invoice, context=context)
if invoice_line:
invoice_line_obj.create(cr, uid, invoice_line)
invoice_obj.button_compute(cr, uid, [invoice.id], context=context)
return result
def _get_default_uom(self,cr,uid,c):
invoice_id = super(stock_picking, self)._create_invoice_from_picking(cr, uid, picking, vals, context=context)
invoice = self.browse(cr, uid, invoice_id, context=context)
invoice_line = self._prepare_shipping_invoice_line(cr, uid, picking, invoice, context=context)
if invoice_line:
invoice_line_obj.create(cr, uid, invoice_line)
return invoice_id
def _get_default_uom(self, cr, uid, c):
uom_categ, uom_categ_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'product', 'product_uom_categ_kgm')
return self.pool.get('product.uom').search(cr, uid, [('category_id', '=', uom_categ_id),('factor','=',1)])[0]
return self.pool.get('product.uom').search(cr, uid, [('category_id', '=', uom_categ_id), ('factor', '=', 1)])[0]
_defaults = {
'weight_uom_id': lambda self,cr,uid,c: self._get_default_uom(cr,uid,c)
'weight_uom_id': lambda self, cr, uid, c: self._get_default_uom(cr, uid, c)
}
@@ -27,7 +27,7 @@
<field name="res_model">product.product</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="context" eval="'{\'default_type\':\'service\',\'default_procure_method\':\'make_to_stock\',\'default_supply_method\':\'buy\',\'default_purchase_ok\':True, \'default_sale_ok\':False, \'default_hr_expense_ok\':True,\'default_categ_id\': ' + str(ref('cat_expense')) +'}'"/>
<field name="context" eval="'{\'default_type\':\'service\',\'default_purchase_ok\':True, \'default_sale_ok\':False, \'default_hr_expense_ok\':True,\'default_categ_id\': ' + str(ref('cat_expense')) +'}'"/>
<field name="domain">[('hr_expense_ok','=',True)]</field>
<field name="view_id" ref="product_expense_installer_tree_view"/>
<field name="help">Define one product for each expense type allowed for an employee (travel by car, hostel, restaurant, etc). If you reimburse the employees at a fixed rate, set a cost and a unit of measure on the product. If you reimburse based on real costs, set the cost at 0.00. The user will set the real price when recording his expense sheet.</field>
+8 -8
View File
@@ -187,7 +187,7 @@ class mrp_bom(osv.osv):
result[bom.id] = []
if bom.bom_lines:
continue
ok = ((name=='child_complete_ids') and (bom.product_id.supply_method=='produce'))
ok = ((name=='child_complete_ids'))
if (bom.type=='phantom' or ok):
sids = bom_obj.search(cr, uid, [('bom_id','=',False),('product_id','=',bom.product_id.id)])
if sids:
@@ -209,11 +209,10 @@ class mrp_bom(osv.osv):
continue
if line.bom_lines or line.type == 'phantom':
continue
if line.product_id.supply_method == 'produce':
if line.product_id.procure_method == 'make_to_stock':
res[line.id] = 'stock'
else:
res[line.id] = 'order'
if line.product_id.procure_method == 'make_to_stock':
res[line.id] = 'stock'
else:
res[line.id] = 'order'
return res
_columns = {
@@ -454,11 +453,12 @@ class mrp_production(osv.osv):
def _moves_assigned(self, cr, uid, ids, name, arg, context=None):
""" Test whether all the consume lines are assigned """
res = True
res = {}
for production in self.browse(cr, uid, ids, context=context):
res[production.id] = True
states = [x.state != 'assigned' for x in production.move_lines if x]
if any(states) or len(states) == 0:
return False
res[production.id] = False
return res
+16
View File
@@ -27,5 +27,21 @@ This application supports complete integration and production scheduling for sto
<field name="number_increment">1</field>
</record>
<!--
Procurement rules and routes
-->
<record id="route_warehouse0_manufacture" model='stock.location.route'>
<field name="name">Manufacture</field>
<field name="sequence">5</field>
</record>
<record id="procurement_rule_supply_stock" model="procurement.rule">
<field name="name">Manufacture to stock</field>
<field name="action">manufacture</field>
<field name="location_id" ref="stock.stock_location_stock"/>
<field name="picking_type_id" ref="stock.picking_type_in"/>
<field name="route_id" ref="route_warehouse0_manufacture"/>
</record>
</data>
</openerp>
+5 -18
View File
@@ -347,7 +347,7 @@
<form string="Bill of Material" version="7.0">
<group>
<group>
<field name="product_id" on_change="onchange_product_id(product_id, name, context)" context="{'default_supply_method':'produce'}" class="oe_inline"/>
<field name="product_id" on_change="onchange_product_id(product_id, name, context)" class="oe_inline"/>
<label for="product_qty" string="Quantity"/>
<div>
<field name="product_qty" class="oe_inline"/>
@@ -380,7 +380,7 @@
<page string="Components">
<field name="bom_lines" widget="one2many_list">
<tree string="Components" editable="bottom">
<field name="product_id" context="{'default_supply_method':'produce'}" on_change="onchange_product_id(product_id, name)"/>
<field name="product_id" on_change="onchange_product_id(product_id, name)"/>
<field name="product_qty"/>
<field name="product_uom" on_change="onchange_uom(product_id, product_uom)" groups="product.group_uom"/>
<field name="name" invisible="1"/>
@@ -517,7 +517,7 @@
<field name="res_model">product.product</field>
<field name="view_type">form</field>
<field name="view_mode">kanban,tree,form</field>
<field name="context">{'search_default_filter_supply_method_produce' : 1}</field>
<field name="context">{}</field>
<field name="search_view_id" ref="product.product_search_form_view"/>
</record>
<!-- BOM menus -->
@@ -634,7 +634,7 @@
</div>
<group>
<group>
<field name="product_id" on_change="product_id_change(product_id)" domain="[('bom_ids','!=',False),('bom_ids.bom_id','=',False)]" class="oe_inline" context='{"default_supply_method":"produce", "default_type": "product"}'/>
<field name="product_id" on_change="product_id_change(product_id)" domain="[('bom_ids','!=',False),('bom_ids.bom_id','=',False)]" class="oe_inline" context='{"default_type": "product"}'/>
<label for="product_qty"/>
<div>
<field name="product_qty" class="oe_inline"/>
@@ -950,20 +950,7 @@
</data>
</field>
</record>
<record id="product_product_normal_form_supply_view" model="ir.ui.view">
<field name="name">product.normal.form.mrp.inherit</field>
<field name="model">product.product</field>
<field name="inherit_id" ref="stock.product_form_view_procurement_button"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='supply_method']" position="attributes">
<attribute name="invisible">False</attribute>
</xpath>
<group name="procurement_help" position="inside">
<!-- TODO: create help with routes -->
<p>Here should come some help about manufacturing routes</p>
</group>
</field>
</record>
<!-- Menu for Resource for MRP-->
+3 -4
View File
@@ -44,9 +44,8 @@ class procurement_order(osv.osv):
rule_id = super(procurement_order, self)._find_suitable_rule(cr, uid, procurement, context=context)
if not rule_id:
#if there isn't any specific procurement.rule defined for the product, we try to directly supply it from a supplier
if procurement.product_id.supply_method == 'manufacture':
rule_id = self._search_suitable_rule(cr, uid, procurement, [('action', '=', 'manufacture'), ('location_id', '=', procurement.location_id.id)], context=context)
rule_id = rule_id and rule_id[0] or False
rule_id = self._search_suitable_rule(cr, uid, procurement, [('action', '=', 'manufacture'), ('location_id', '=', procurement.location_id.id)], context=context)
rule_id = rule_id and rule_id[0] or False
return rule_id
def _run(self, cr, uid, procurement, context=None):
@@ -92,7 +91,7 @@ class procurement_order(osv.osv):
def get_phantom_bom_id(self, cr, uid, ids, context=None):
for procurement in self.browse(cr, uid, ids, context=context):
if procurement.move_dest_id and procurement.move_dest_id.product_id.supply_method=='produce':
if procurement.move_dest_id:
phantom_bom_id = self.pool.get('mrp.bom').search(cr, uid, [
('product_id', '=', procurement.move_dest_id.product_id.id),
('bom_id', '=', False),
+50 -50
View File
@@ -41,57 +41,57 @@ class StockMove(osv.osv):
procurement_obj = self.pool.get('procurement.order')
product_obj = self.pool.get('product.product')
processed_ids = [move.id]
if move.product_id.supply_method == 'produce':
bis = bom_obj.search(cr, uid, [
('product_id','=',move.product_id.id),
('bom_id','=',False),
('type','=','phantom')])
if bis:
factor = move.product_qty
bom_point = bom_obj.browse(cr, uid, bis[0], context=context)
res = bom_obj._bom_explode(cr, uid, bom_point, factor, [])
state = 'confirmed'
if move.state == 'assigned':
state = 'assigned'
for line in res[0]:
valdef = {
'picking_id': move.picking_id.id,
'product_id': line['product_id'],
'product_uom': line['product_uom'],
'product_qty': line['product_qty'],
'product_uos': line['product_uos'],
'product_uos_qty': line['product_uos_qty'],
'move_dest_id': move.id,
'state': state,
'name': line['name'],
'procurements': [],
}
mid = move_obj.copy(cr, uid, move.id, default=valdef)
processed_ids.append(mid)
prodobj = product_obj.browse(cr, uid, line['product_id'], context=context)
proc_id = procurement_obj.create(cr, uid, {
'name': (move.picking_id.origin or ''),
'origin': (move.picking_id.origin or ''),
'date_planned': move.date,
'product_id': line['product_id'],
'product_qty': line['product_qty'],
'product_uom': line['product_uom'],
'product_uos_qty': line['product_uos'] and line['product_uos_qty'] or False,
'product_uos': line['product_uos'],
'location_id': move.location_id.id,
'procure_method': prodobj.procure_method,
'move_id': mid,
})
procurement_obj.signal_button_confirm(cr, uid, [proc_id])
move_obj.write(cr, uid, [move.id], {
'location_dest_id': move.location_id.id, # dummy move for the kit
'picking_id': False,
'state': 'confirmed'
bis = bom_obj.search(cr, uid, [
('product_id','=',move.product_id.id),
('bom_id','=',False),
('type','=','phantom')])
if bis:
factor = move.product_qty
bom_point = bom_obj.browse(cr, uid, bis[0], context=context)
res = bom_obj._bom_explode(cr, uid, bom_point, factor, [])
state = 'confirmed'
if move.state == 'assigned':
state = 'assigned'
for line in res[0]:
valdef = {
'picking_id': move.picking_id.id,
'product_id': line['product_id'],
'product_uom': line['product_uom'],
'product_qty': line['product_qty'],
'product_uos': line['product_uos'],
'product_uos_qty': line['product_uos_qty'],
'move_dest_id': move.id,
'state': state,
'name': line['name'],
'procurements': [],
}
mid = move_obj.copy(cr, uid, move.id, default=valdef)
processed_ids.append(mid)
prodobj = product_obj.browse(cr, uid, line['product_id'], context=context)
proc_id = procurement_obj.create(cr, uid, {
'name': (move.picking_id.origin or ''),
'origin': (move.picking_id.origin or ''),
'date_planned': move.date,
'product_id': line['product_id'],
'product_qty': line['product_qty'],
'product_uom': line['product_uom'],
'product_uos_qty': line['product_uos'] and line['product_uos_qty'] or False,
'product_uos': line['product_uos'],
'location_id': move.location_id.id,
'procure_method': prodobj.procure_method,
'move_id': mid,
})
procurement_ids = procurement_obj.search(cr, uid, [('move_id','=',move.id)], context)
procurement_obj.signal_button_confirm(cr, uid, procurement_ids)
procurement_obj.signal_button_wait_done(cr, uid, procurement_ids)
procurement_obj.signal_button_confirm(cr, uid, [proc_id])
move_obj.write(cr, uid, [move.id], {
'location_dest_id': move.location_id.id, # dummy move for the kit
'picking_id': False,
'state': 'confirmed'
})
procurement_ids = procurement_obj.search(cr, uid, [('move_id','=',move.id)], context)
procurement_obj.signal_button_confirm(cr, uid, procurement_ids)
procurement_obj.signal_button_wait_done(cr, uid, procurement_ids)
return processed_ids
def action_consume(self, cr, uid, ids, product_qty, location_id=False, context=None):
+5 -3
View File
@@ -157,6 +157,7 @@ class mrp_repair(osv.osv):
'quotation_notes': fields.text('Quotation Notes'),
'company_id': fields.many2one('res.company', 'Company'),
'deliver_bool': fields.boolean('Deliver', help="Check this box if you want to manage the delivery once the product is repaired and create a picking with selected product. Note that you can select the locations in the Info tab, if you have the extended view.", states={'confirmed':[('readonly',True)]}),
'picking_type_id': fields.many2one('stock.picking.type', 'Picking Type'),
'invoiced': fields.boolean('Invoiced', readonly=True),
'repaired': fields.boolean('Repaired', readonly=True),
'amount_untaxed': fields.function(_amount_untaxed, string='Untaxed Amount',
@@ -501,7 +502,6 @@ class mrp_repair(osv.osv):
res = {}
move_obj = self.pool.get('stock.move')
repair_line_obj = self.pool.get('mrp.repair.line')
seq_obj = self.pool.get('ir.sequence')
pick_obj = self.pool.get('stock.picking')
for repair in self.browse(cr, uid, ids, context=context):
for move in repair.operations:
@@ -517,7 +517,9 @@ class mrp_repair(osv.osv):
})
repair_line_obj.write(cr, uid, [move.id], {'move_id': move_id, 'state': 'done'}, context=context)
if repair.deliver_bool:
pick_name = seq_obj.get(cr, uid, 'stock.picking.out')
if not repair.picking_type_id:
raise osv.except_osv(_('Warning!'), _('No picking type set.'))
pick_name = self.pool.get('ir.sequence').get_id(cr, uid, repair.picking_type_id.sequence_id.id, 'id', context=context)
picking = pick_obj.create(cr, uid, {
'name': pick_name,
'origin': repair.name,
@@ -526,7 +528,7 @@ class mrp_repair(osv.osv):
'partner_id': repair.address_id and repair.address_id.id or False,
'note': repair.internal_notes,
'invoice_state': 'none',
'type': 'out',
'picking_type_id': repair.picking_type_id.id,
})
move_id = move_obj.create(cr, uid, {
'name': repair.name,
+3
View File
@@ -18,6 +18,7 @@
partner_invoice_id: base.res_partner_address_1
location_dest_id: stock.stock_location_14
location_id: stock.stock_location_14
picking_type_id: stock.picking_type_out
move_id: 'stock_move_pcbasicpc0'
name: RMA00004
operations:
@@ -59,6 +60,7 @@
partner_invoice_id: base.res_partner_address_1
location_dest_id: stock.stock_location_14
location_id: stock.stock_location_14
picking_type_id: stock.picking_type_out
move_id: 'stock.stock_move_stockmvmrp1'
name: RMA-00007
operations:
@@ -100,6 +102,7 @@
partner_invoice_id: base.res_partner_address_1
location_dest_id: stock.stock_location_14
location_id: stock.stock_location_14
picking_type_id: stock.picking_type_out
move_id: 'stock.stock_move_stockmvmrp2'
name: RMA-00011
operations:
+1
View File
@@ -53,6 +53,7 @@
<group>
<field name="guarantee_limit"/>
<field name="deliver_bool"/>
<field name="picking_type_id" attrs="{'invisible': [('deliver_bool','=', False)], 'required': [('deliver_bool', '=', True)]}"/>
<field name="repaired" groups="base.group_no_one"/>
<field name="invoiced" groups="base.group_no_one"/>
</group>
+1 -1
View File
@@ -256,7 +256,7 @@
<para style="P16">[[ o.product_id.name or '' ]]</para>
</td>
<td>
<para style="P16">[[ o.prodlot_id.name or ' ' ]]</para>
<para style="P16">[[ ','.join([x.name for x in o.move_id.lot_ids]) ]]</para>
</td>
<td>
<para style="P16">[[ formatLang(o.guarantee_limit,date = True) ]]</para>
@@ -150,7 +150,6 @@
</record>
<record id="product_product_odoo1" model="product.product">
<field name="default_code">ODOO</field>
<field name="supply_method">produce</field>
<field name="list_price">35.0</field>
<field name="standard_price">10.0</field>
<field name="uom_id" ref="product.product_uom_unit"/>
@@ -2,7 +2,6 @@
<data noupdate="1">
<record id="product_product_jambon" model="product.product">
<field name="supply_method">buy</field>
<field name="list_price">7.0</field>
<field name="standard_price">5.0</field>
<field name="uom_id" ref="product.product_uom_unit"/>
@@ -32,7 +31,6 @@
</record>
<record id="product_product_from" model="product.product">
<field name="supply_method">buy</field>
<field name="list_price">8.0</field>
<field name="standard_price">6.0</field>
<field name="uom_id" ref="product.product_uom_unit"/>
@@ -62,7 +60,6 @@
</record>
<record id="product_product_pain" model="product.product">
<field name="supply_method">buy</field>
<field name="list_price">8.0</field>
<field name="standard_price">6.0</field>
<field name="uom_id" ref="product.product_uom_unit"/>
@@ -92,7 +89,6 @@
</record>
<record id="product_product_lait" model="product.product">
<field name="supply_method">buy</field>
<field name="list_price">8.0</field>
<field name="standard_price">6.0</field>
<field name="uom_id" ref="product.product_uom_unit"/>
-10
View File
@@ -41,16 +41,6 @@
</field>
</field>
</record>
<record id="product_product_normal_form_supply_view" model="ir.ui.view">
<field name="name">product.normal.form.project_mrp.inherit</field>
<field name="model">product.product</field>
<field name="inherit_id" ref="stock.product_form_view_procurement_button"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='supply_method']" position="attributes">
<attribute name="invisible">False</attribute>
</xpath>
</field>
</record>
</data>
</openerp>
@@ -16,7 +16,6 @@
mes_type: fixed
name: HR Manager
standard_price: 1.0
supply_method: buy
type: service
uom_id: product.product_uom_hour
uom_po_id: product.product_uom_hour
+2 -3
View File
@@ -1122,9 +1122,8 @@ class procurement_order(osv.osv):
rule_id = super(procurement_order, self)._find_suitable_rule(cr, uid, procurement, context=context)
if not rule_id:
#if there isn't any specific procurement.rule defined for the product, we try to directly supply it from a supplier
if procurement.product_id.supply_method == 'buy' and self._check_supplier_info(cr, uid, [procurement.id], context=context):
rule_id = self._search_suitable_rule(cr, uid, procurement, [('action', '=', 'buy'), ('location_id', '=', procurement.location_id.id)], context=context)
rule_id = rule_id and rule_id[0] or False
rule_id = self._search_suitable_rule(cr, uid, procurement, [('action', '=', 'buy'), ('location_id', '=', procurement.location_id.id)], context=context)
rule_id = rule_id and rule_id[0] or False
return rule_id
def _run(self, cr, uid, procurement, context=None):
+8 -2
View File
@@ -67,14 +67,20 @@
</record>
<!--
Procurement rules
Procurement rules and routes
-->
<record id="route_warehouse0_buy" model='stock.location.route'>
<field name="name">Buy</field>
<field name="sequence">5</field>
</record>
<record id="procurement_rule_supply_stock" model="procurement.rule">
<field name="name">Buy to refill stock</field>
<field name="name">Buy to stock</field>
<field name="action">buy</field>
<field name="location_id" ref="stock.stock_location_stock"/>
<field name="picking_type_id" ref="stock.picking_type_in"/>
<field name="route_id" ref="route_warehouse0_buy"/>
</record>
</data>
-12
View File
@@ -573,18 +573,6 @@
</field>
</record>
<record id="product_product_normal_form_procurement_help_view" model="ir.ui.view">
<field name="name">product.normal.form.procurement.help.inherit</field>
<field name="model">product.product</field>
<field name="inherit_id" ref="stock.product_form_view_procurement_button"/>
<field name="arch" type="xml">
<group name="procurement_help" position="inside">
<!--TODO: Should add some procurement help here (something like route.description)-->
<p> Procurement help </p>
</group>
</field>
</record>
<record id="product_search_form_view_purchase" model="ir.ui.view">
<field name="name">product.search.purchase.form</field>
<field name="model">product.product</field>
+2 -2
View File
@@ -33,9 +33,9 @@ class stock_move(osv.osv):
if isinstance(ids, (int, long)):
ids = [ids]
res = super(stock_move, self).write(cr, uid, ids, vals, context=context)
wf_service = netsvc.LocalService('workflow')
from openerp import workflow
for id in ids:
wf_service.trg_trigger(uid, 'stock.move', id, cr)
workflow.trg_trigger(uid, 'stock.move', id, cr)
return res
#
+13 -7
View File
@@ -190,7 +190,7 @@
Process the reception of purchase order with usd
-
!python {model: stock.picking}: |
pick_obj = self.pool.get('stock.picking').browse(cr, uid, ref("purchase_order_fifo_usd"))
pick_obj = self.pool.get('purchase.order').browse(cr, uid, ref("purchase_order_fifo_usd")).picking_ids[0]
pick_obj.do_partial(context=context)
-
We create delivery order of 49.5 kg
@@ -255,9 +255,11 @@
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), "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))
product = self.browse(cr, uid, ref("product_fifo_icecream"))
assert round(product.standard_price) == round(150.0 / 1.2834), "Product price not updated accordingly. %s found instead of %s" %(product.standard_price, round(150.0/1.2834))
assert product.qty_available == 0.0, 'Wrong quantity in stock after first reception'
-
Let us create some outs to get negative stock. Create outpicking. We create delivery order of 200 kg, but will pick only 100 kg
Let us create some outs to get negative stock. Create outpicking. We create delivery order of 100 kg.
-
!record {model: stock.picking, id: outgoing_fifo_shipment_neg}:
picking_type_id: stock.picking_type_out
@@ -268,12 +270,18 @@
picking_id: outgoing_fifo_shipment_neg
product_id: product_fifo_icecream
product_uom: product.product_uom_kgm
product_uom_qty: 200.0
product_uom_qty: 100.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
Process the delivery of the first outgoing shipment
-
!python {model: stock.picking}: |
picking_obj = self.browse(cr, uid, ref("outgoing_fifo_shipment_neg"))
picking_obj.do_partial(context=context)
-
Let us create another out of 400 kg
-
!record {model: stock.picking, id: outgoing_fifo_shipment_neg2}:
picking_type_id: stock.picking_type_out
@@ -292,8 +300,6 @@
Process the delivery of the outgoing shipments
-
!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)
-
+12 -8
View File
@@ -1,14 +1,18 @@
-
In order to test the scheduler to generate RFQ, I create a new product
-
!record {model: product.product, id: scheduler_product}:
name: scheduler prod
type: product
supply_method: buy
seller_ids:
- delay: 1
name: base.res_partner_2
min_qty: 5.0
!record {model: product.product, id: scheduler_product}:
name: scheduler prod
type: product
seller_ids:
- delay: 1
name: base.res_partner_2
min_qty: 5.0
-
Add Buy route
-
!python {model: product.product, id: scheduler_product}: |
self.write(cr, uid, [ref("scheduler_product")], {"route_ids": [(4, ref("purchase.route_warehouse0_buy"))]})
-
I create a procurement order.
-
@@ -58,13 +58,20 @@ class purchase_requisition(osv.osv):
'Status', track_visibility='onchange', required=True),
'multiple_rfq_per_supplier': fields.boolean('Multiple RFQ per supplier'),
'account_analytic_id': fields.many2one('account.analytic.account', 'Analytic Account'),
'picking_type_id': fields.many2one('stock.picking.type', 'Picking Type', required=True),
}
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]
_defaults = {
'state': 'draft',
'exclusive': 'multiple',
'company_id': lambda self, cr, uid, c: self.pool.get('res.company')._company_default_get(cr, uid, 'purchase.requisition', context=c),
'user_id': lambda self, cr, uid, c: self.pool.get('res.users').browse(cr, uid, uid, c).id,
'name': lambda obj, cr, uid, context: obj.pool.get('ir.sequence').get(cr, uid, 'purchase.order.requisition'),
'picking_type_id': _get_picking_in,
}
def copy(self, cr, uid, id, default=None, context=None):
@@ -142,12 +149,11 @@ class purchase_requisition(osv.osv):
'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': requisition.picking_type_id.default_location_dest_id.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,
'notes': requisition.description,
'warehouse_id': requisition.warehouse_id.id if requisition.warehouse_id else False,
}
def _prepare_purchase_order_line(self, cr, uid, requisition, requisition_line, purchase_id, supplier, context=None):
@@ -62,7 +62,7 @@
<field name="ordering_date" attrs="{'readonly': [('state','not in',('draft'))]}"/>
<field name="schedule_date" attrs="{'readonly': [('state','not in',('draft'))]}"/>
<field name="origin" placeholder="e.g. OP0025" attrs="{'readonly': [('state','not in',('draft'))]}"/>
<field name="warehouse_id" widget="selection" groups="stock.group_locations" attrs="{'readonly': [('state','not in',('draft'))]}"/>
<field name="picking_type_id" widget="selection" groups="stock.group_locations" attrs="{'readonly': [('state','not in',('draft'))]}"/>
<field name="company_id" groups="base.group_multi_company" widget="selection" attrs="{'readonly': [('state','not in',('draft'))]}"/>
</group>
</group>
@@ -197,7 +197,7 @@
<field name="model">product.product</field>
<field name="inherit_id" ref="stock.product_form_view_procurement_button"/>
<field name="arch" type="xml">
<field name="supply_method" position="after">
<field name="route_ids" position="after">
<field name="purchase_requisition"/>
</field>
</field>
+8 -4
View File
@@ -640,9 +640,13 @@ class sale_order(osv.osv):
def _get_date_planned(self, cr, uid, order, line, start_date, context=None):
date_planned = datetime.strptime(start_date, DEFAULT_SERVER_DATETIME_FORMAT) + relativedelta(days=line.delay or 0.0)
date_planned = (date_planned - timedelta(days=order.company_id.security_lead)).strftime(DEFAULT_SERVER_DATETIME_FORMAT)
return date_planned
def _prepare_procurement_group(self, cr, uid, order, context=None):
return {
'name': order.name, 'partner_id': order.partner_shipping_id.id,
}
def action_ship_create(self, cr, uid, ids, context=None):
"""Create the required procurements to supply sales order lines, also connecting
the procurements to appropriate stock moves in order to bring the goods to the
@@ -657,9 +661,9 @@ class sale_order(osv.osv):
procurement_obj = self.pool.get('procurement.order')
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, 'move_type': order.picking_policy
}, context=context)
vals = self._prepare_procurement_group(cr, uid, order, context=context)
group_id = self.pool.get("procurement.group").create(cr, uid, vals, context=context)
order.write({'procurement_group_id': group_id}, context=context)
for line in order.order_line:
if (line.state == 'done') or not line.product_id:
-1
View File
@@ -325,7 +325,6 @@ Thanks!</field>
<field name="type">service</field>
<field name="list_price">150.0</field>
<field name="standard_price">100.0</field>
<field name="supply_method">produce</field> <!-- TODO this is a procurement field, which is not a sale dependency -->
<field name="uom_id" ref="product.product_uom_day"/>
<field name="uom_po_id" ref="product.product_uom_day"/>
<field name="company_id" eval="[]"/>
+2 -2
View File
@@ -64,9 +64,9 @@ class mrp_production(osv.osv):
if parent_move_line:
move = move_obj.browse(cr, uid, parent_move_line)
if field_name == 'name':
res[production.id] = move.sale_line_id and move.sale_line_id.order_id.name or False
res[production.id] = move.procurement_id and move.procurement_id.sale_line_id and move.procurement_id.sale_line_id.order_id.name or False
if field_name == 'client_order_ref':
res[production.id] = move.sale_line_id and move.sale_line_id.order_id.client_order_ref or False
res[production.id] = move.procurement_id and move.procurement_id.sale_line_id and move.procurement_id.sale_line_id.order_id.client_order_ref or False
return res
_columns = {
+8 -5
View File
@@ -19,7 +19,6 @@
mes_type: fixed
name: Slider Mobile
standard_price: 189.0
supply_method: produce
type: product
uom_id: product.product_uom_unit
uom_po_id: product.product_uom_unit
@@ -38,10 +37,14 @@
min_qty: 2.0
qty: 5.0
standard_price: 189.0
supply_method: produce
type: product
uom_id: product.product_uom_unit
uom_po_id: product.product_uom_unit
-
I add the routes manufacture and mto to the product
-
!python {model: product.product, id: scheduler_product}: |
self.write(cr, uid, [ref("product_product_slidermobile0")], {"route_ids": [(4, ref("mrp.route_warehouse0_manufacture")), (4, ref("stock.route_warehouse0_mto"))]})
-
I create a Bill of Material record for Slider Mobile
-
@@ -97,7 +100,7 @@
sale_order_obj = self.pool.get('sale.order')
so = sale_order_obj.browse(cr, uid, ref("sale_order_so0"))
proc_ids = self.search(cr, uid, [('origin','=',so.name)])
self.signal_button_check(cr, uid, proc_ids)
self.run(cr, uid, proc_ids)
-
I verify that a procurement state is "running"
-
@@ -105,14 +108,14 @@
sale_order_obj = self.pool.get('sale.order')
so = sale_order_obj.browse(cr, uid, ref("sale_order_so0"))
proc_ids = self.search(cr, uid, [('origin','=',so.name) and ('state','=','running')])
assert proc_ids, 'Procurement is not in the running state!'
assert len(proc_ids) == 2, 'Not both procurements are in the running state!'
-
I verify that a manufacturing order has been generated, and that its name and reference are correct
-
!python {model: sale.order}: |
mnf_obj = self.pool.get('mrp.production')
so = self.browse(cr, uid, ref("sale_order_so0"))
mnf_id = mnf_obj.search(cr, uid, [('origin','=',so.name)])
mnf_id = mnf_obj.search(cr, uid, [('origin','like',so.name)])
assert mnf_id, 'Manufacturing order has not been generated'
mo = mnf_obj.browse(cr, uid, mnf_id)[0]
assert mo.sale_name == so.name, 'Wrong Name for the Manufacturing Order. Expected %s, Got %s' % (so.name, mo.name)
+41
View File
@@ -246,6 +246,17 @@ class sale_order(osv.osv):
return finished
elif mode == 'canceled':
return canceled
def _get_date_planned(self, cr, uid, order, line, start_date, context=None):
date_planned = super(sale_order, self)._get_date_planned(cr, uid, order, line, start_date, context=context)
date_planned = (date_planned - timedelta(days=order.company_id.security_lead)).strftime(DEFAULT_SERVER_DATETIME_FORMAT)
return date_planned
def _prepare_procurement_group(self, cr, uid, order, context=None):
res = super(sale_order, self)._prepare_procurement_group(cr, uid, order, context=None)
res.update({'move_type': order.picking_policy})
return res
def action_ship_end(self, cr, uid, ids, context=None):
@@ -419,3 +430,33 @@ class sale_order_line(osv.osv):
res.update({'warning': warning})
return res
class stock_move(osv.osv):
_inherit = 'stock.move'
def _create_invoice_line_from_vals(self, cr, uid, move, invoice_line_vals, context=None):
invoice_line_id = self.pool.get('account.invoice.line').create(cr, uid, invoice_line_vals, context=context)
if move.procurement_id and move.procurement_id.sale_line_id:
sale_line = move.procurement_id.sale_line_id
self.pool.get('sale.order.line').write(cr, uid, [sale_line.id], {
'invoice_lines': [(4, invoice_line_id)]
}, context=context)
self.pool.get('sale.order').write(cr, uid, [sale_line.order_id.id], {
'invoice_ids': [(4, invoice_line_vals['invoice_id'])],
})
return invoice_line_id
def _get_master_data(self, cr, uid, move, company, context=None):
if move.procurement_id and move.procurement_id.sale_line_id:
sale_order = move.procurement_id.sale_line_id.order_id
return sale_order.partner_invoice_id, sale_order.user_id.id, sale_order.pricelist_id.currency_id.id
return super(stock_move, self)._get_master_data(cr, uid, move, company, context=context)
def _get_invoice_line_vals(self, cr, uid, move, partner, inv_type, context=None):
res = super(stock_move, self)._get_invoice_line_vals(cr, uid, move, partner, inv_type, context=context)
if move.procurement_id and move.procurement_id.sale_line_id:
sale_line = move.procurement_id.sale_line_id
res['invoice_line_tax_id'] = [(6, 0, [x.id for x in sale_line.tax_id])]
res['account_analytic_id'] = sale_line.order_id.project_id and sale_line.order_id.project_id.id or False
res['price_unit'] = sale_line.price_unit
res['discount'] = sale_line.discount
return res
@@ -48,14 +48,14 @@ class sale_order(osv.osv):
class sale_order_line(osv.osv):
_inherit = 'sale.order.line'
_columns = {
'route_id': fields.many2one('stock.location.route', 'Route', domain=[('sale', '=', True)]),
'route_id': fields.many2one('stock.location.route', 'Route', domain=[('sale_selectable', '=', True)]),
}
class stock_location_route(osv.osv):
_inherit = "stock.location.route"
_columns = {
'sale':fields.boolean("Can be set on sale order line")
'sale_selectable':fields.boolean("Selectable on Sales Order Line")
}
@@ -41,13 +41,24 @@
<record id="stock_location_route_form_view_inherit" model="ir.ui.view">
<field name="name">stock.location.route.form</field>
<field name="inherit_id" ref="stock_location.stock_location_route_form_view"/>
<field name="inherit_id" ref="stock.stock_location_route_form_view"/>
<field name="model">stock.location.route</field>
<field name="arch" type="xml">
<xpath expr="//field[@name='pull_ids']" position="after">
<field name="sale"/>
<xpath expr="//field[@name='warehouse_selectable']" position="after">
<field name="sale_selectable"/>
</xpath>
</field>
</record>
<record id="stock_location_route_tree_inherit" model="ir.ui.view">
<field name="name">stock.location.route.tree.inherit</field>
<field name="inherit_id" ref="stock_location.stock_location_route_tree"/>
<field name="model">stock.location.route</field>
<field name="arch" type="xml">
<xpath expr="//field[@name='warehouse_selectable']" position="after">
<field name="sale_selectable"/>
</xpath>
</field>
</record>
</data>
</openerp>
+30 -13
View File
@@ -40,14 +40,29 @@ class procurement_rule(osv.osv):
result = super(procurement_rule, self)._get_action(cr, uid, context=context)
return result + [('move', 'Move From Another Location')]
def _get_rules(self, cr, uid, ids, context=None):
res = []
for route in self.browse(cr, uid, ids):
res += [x.id for x in route.pull_ids]
return res
_columns = {
'location_id': fields.many2one('stock.location', 'Destination Location'),
'location_src_id': fields.many2one('stock.location', 'Source Location',
help="Source location is action=move"),
'route_id': fields.many2one('stock.location.route', 'Route',
help="If route_id is False, the rule is global"),
'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."),
'route_sequence': fields.related('route_id', 'sequence', string='Route Sequence', store={'stock.location.route': (_get_rules, ['sequence'], 10)}),
'sequence': fields.integer('Sequence'),
'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, ...")
}
_defaults = {
'procure_method': 'make_to_stock',
}
class procurement_order(osv.osv):
_inherit = "procurement.order"
_columns = {
@@ -57,13 +72,18 @@ class procurement_order(osv.osv):
}
def _search_suitable_rule(self, cr, uid, procurement, domain, context=None):
'''method overwritten in stock_location that is used to search the best suitable rule'''
return self.pool.get('procurement.rule').search(cr, uid, domain, context=context)
'''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.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 + [('route_id', '=', False)], order='sequence', context=context)
return res
def _find_suitable_rule(self, cr, uid, procurement, context=None):
rule_id = super(procurement_order, self)._find_suitable_rule(cr, uid, procurement, context=context)
if not rule_id:
rule_id = self._search_suitable_rule(cr, uid, procurement, [('location_id', '=', procurement.location_id.id)], context=context) #action=move
rule_id = self._search_suitable_rule(cr, uid, procurement, [('location_id', 'child_of', procurement.location_id.id)], context=context) #action=move
rule_id = rule_id and rule_id[0] or False
return rule_id
@@ -88,6 +108,7 @@ class procurement_order(osv.osv):
'move_dest_id': procurement.move_dest_id and procurement.move_dest_id.id or False,
'procurement_id': procurement.id,
'rule_id': procurement.rule_id.id,
'procure_method': procurement.rule_id.procure_method,
'origin': procurement.origin,
'picking_type_id': procurement.rule_id.picking_type_id.id,
}
@@ -173,7 +194,7 @@ class procurement_order(osv.osv):
'product_uom': product.uom_id.id,
'location_id': location_id,
'company_id': warehouse.company_id.id,
'procure_method': 'make_to_order',}
}
def create_automatic_op(self, cr, uid, context=None):
"""
@@ -203,17 +224,14 @@ class procurement_order(osv.osv):
continue
product = product_obj.browse(cr, uid, [product_read['id']], context=context)[0]
if product.supply_method == 'buy':
location_id = warehouse.lot_input_id.id
elif product.supply_method == 'produce':
location_id = warehouse.lot_stock_id.id
else:
continue
location_id = warehouse.lot_stock_id.id
proc_id = proc_obj.create(cr, uid,
self._prepare_automatic_op_procurement(cr, uid, product, warehouse, location_id, context=context),
context=context)
self.signal_button_confirm(cr, uid, [proc_id])
self.signal_button_check(cr, uid, [proc_id])
self.assign(cr, uid, [proc_id])
self.run(cr, uid, [proc_id])
return True
def _get_orderpoint_date_planned(self, cr, uid, orderpoint, start_date, context=None):
@@ -229,7 +247,6 @@ class procurement_order(osv.osv):
'company_id': orderpoint.company_id.id,
'product_uom': orderpoint.product_uom.id,
'location_id': orderpoint.location_id.id,
'procure_method': 'make_to_order',
'origin': orderpoint.name}
def _product_virtual_get(self, cr, uid, order_point):
-19
View File
@@ -1,29 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data noupdate="1">
<record id="product.product_product_1" model="product.product">
<field name="supply_method">produce</field>
</record>
<record id="product.product_product_2" model="product.product">
<field name="supply_method">produce</field>
</record>
<record id="product.product_product_3" model="product.product">
<field name="type">product</field>
<field name="supply_method">produce</field>
</record>
<record id="product.product_product_4" model="product.product">
<field name="type">product</field>
<field name="supply_method">produce</field>
</record>
<record id="product.product_product_5" model="product.product">
<field name="type">product</field>
<field name="supply_method">produce</field>
</record>
<record id="product.product_product_6" model="product.product">
@@ -68,7 +56,6 @@
<record id="product.product_product_16" model="product.product">
<field name="type">product</field>
<field name="supply_method">produce</field>
</record>
<record id="product.product_product_17" model="product.product">
@@ -77,12 +64,10 @@
<record id="product.product_product_18" model="product.product">
<field name="type">product</field>
<field name="supply_method">produce</field>
</record>
<record id="product.product_product_19" model="product.product">
<field name="type">product</field>
<field name="supply_method">produce</field>
</record>
<record id="product.product_product_20" model="product.product">
@@ -115,7 +100,6 @@
<record id="product.product_product_27" model="product.product">
<field name="type">product</field>
<field name="supply_method">produce</field>
</record>
<record id="product.product_product_28" model="product.product">
@@ -166,9 +150,6 @@
<field name="type">product</field>
</record>
<record id="product.product_product_44" model="product.product">
<field name="supply_method">produce</field>
</record>
<record id="product.product_product_45" model="product.product">
<field name="type">product</field>
+2
View File
@@ -211,6 +211,8 @@ class product_product(osv.osv):
'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'),
'route_ids': fields.many2many('stock.location.route', 'stock_location_route_product', 'product_id', 'route_id', 'Routes',
help="Depending on the modules installed, this will allow you to define the route of the product: whether it will be bought, manufactured, MTO/MTS,..."),
}
def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
+67 -30
View File
@@ -33,6 +33,8 @@ import openerp.addons.decimal_precision as dp
import logging
_logger = logging.getLogger(__name__)
#----------------------------------------------------------
# Incoterms
#----------------------------------------------------------
@@ -123,6 +125,28 @@ class stock_location(osv.osv):
def get_removal_strategy(self, cr, uid, location, product, context=None):
return None
#----------------------------------------------------------
# Routes
#----------------------------------------------------------
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'),
'pull_ids': fields.one2many('procurement.rule', 'route_id', 'Pull Rules'),
}
_defaults = {
'sequence': lambda self,cr,uid,ctx: 0,
}
#----------------------------------------------------------
# Quants
#----------------------------------------------------------
@@ -167,6 +191,7 @@ class stock_quant(osv.osv):
# Used for negative quants to reconcile after compensated by a new positive one
'propagated_from_id': fields.many2one('stock.quant', 'Linked Quant', help='The negative quant this is coming from'),
'negative_dest_location_id': fields.many2one('stock.location', 'Destination Location', help='Technical field used to record the destination location of a move that created a negative quant'),
}
_defaults = {
@@ -231,7 +256,6 @@ 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
#
@@ -261,6 +285,7 @@ class stock_quant(osv.osv):
negative_vals['location_id'] = move.location_id.id
negative_vals['qty'] = -qty
negative_vals['cost'] = price_unit
negative_vals['negative_dest_location_id'] = move.location_dest_id.id
negative_quant_id = self.create(cr, uid, negative_vals, context=context)
vals.update({'propagated_from_id': negative_quant_id})
@@ -284,6 +309,20 @@ class stock_quant(osv.osv):
move = m
return move
def _reconcile_single_negative_quant(self, cr, uid, to_solve_quant, quant, quant_neg, qty, context=None):
move = self._get_latest_move(cr, uid, to_solve_quant, context=context)
self._quant_split(cr, uid, quant, qty, context=context)
remaining_to_solve_quant = self._quant_split(cr, uid, to_solve_quant, qty, context=context)
remaining_neg_quant = self._quant_split(cr, uid, quant_neg, -qty, context=context)
#if the reconciliation was not complete, we need to link together the remaining parts
if remaining_to_solve_quant and remaining_neg_quant:
self.write(cr, uid, remaining_to_solve_quant.id, {'propagated_from_id': remaining_neg_quant.id}, context=context)
#delete the reconciled quants, as it is replaced by the solving quant
self.unlink(cr, SUPERUSER_ID, [quant_neg.id, to_solve_quant.id], context=context)
#call move_single_quant to ensure recursivity if necessary and do the stock valuation
self.move_single_quant(cr, uid, quant, qty, move, context=context)
return remaining_to_solve_quant
def _quant_reconcile_negative(self, cr, uid, quant, context=None):
"""
When new quant arrive in a location, try to reconcile it with
@@ -293,27 +332,14 @@ class stock_quant(osv.osv):
if quant.location_id.usage != 'internal':
return False
quants = self.quants_get(cr, uid, quant.location_id, quant.product_id, quant.qty, [('qty', '<', '0')], context=context)
result = False
for quant_neg, qty in quants:
if not quant_neg:
continue
result = True
to_solve_quant = self.search(cr, uid, [('propagated_from_id', '=', quant_neg.id), ('id', '!=', quant.id)], context=context)
if not to_solve_quant:
continue
to_solve_quant = self.browse(cr, uid, to_solve_quant[0], context=context)
move = self._get_latest_move(cr, uid, to_solve_quant, context=context)
self._quant_split(cr, uid, quant, qty, context=context)
remaining_to_solve_quant = self._quant_split(cr, uid, to_solve_quant, qty, context=context)
remaining_neg_quant = self._quant_split(cr, uid, quant_neg, -qty, context=context)
#if the reconciliation was not complete, we need to link together the remaining parts
if remaining_to_solve_quant and remaining_neg_quant:
self.write(cr, uid, remaining_to_solve_quant.id, {'propagated_from_id': remaining_neg_quant.id}, context=context)
#delete the reconciled quants, as it is replaced by the solving quant
self.unlink(cr, SUPERUSER_ID, [quant_neg.id, to_solve_quant.id], context=context)
#call move_single_quant to ensure recursivity if necessary and do the stock valuation
self.move_single_quant(cr, uid, quant, qty, move, context=context)
return result
self._reconcile_single_negative_quant(cr, uid, to_solve_quant, quant, quant_neg, qty, context=context)
def _price_update(self, cr, uid, quant, newprice, context=None):
self.write(cr, uid, [quant.id], {'cost': newprice}, context=context)
@@ -345,6 +371,8 @@ class stock_quant(osv.osv):
domain += [('product_id','=',product.id)] + domain
res = []
offset = 0
#id is added at the end of the order to make sure the order is always the same for quants created at the same second
orderby += ', id'
while quantity > 0:
quants = self.search(cr, uid, domain, order=orderby, limit=10, offset=offset, context=context)
if not quants:
@@ -1441,7 +1469,7 @@ class stock_move(osv.osv):
done.append(move.id)
continue
else:
qty = uom_obj._compute_qty(cr, uid, move.product_uom.id, move.product_qty, move.product_id.uom_id.id)
qty = move.product_qty
dp = []
for m2 in move.move_orig_ids:
for q in m2.quant_ids:
@@ -1509,7 +1537,7 @@ class stock_move(osv.osv):
# 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)]
domain = ['|', ('reservation_id', '=', False), ('reservation_id', '=', move.id), ('qty', '>', 0)]
prefered_order = 'reservation_id'
# if lot_id:
# prefered_order = 'lot_id<>' + lot_id + ", " + prefered_order
@@ -1956,7 +1984,10 @@ class stock_package(osv.osv):
"""
_name = "stock.quant.package"
_description = "Physical Packages"
_order = 'name'
_parent_name = "parent_id"
_parent_store = True
_parent_order = 'name'
_order = 'parent_left'
def name_get(self, cr, uid, ids, context=None):
res = self._complete_name(cr, uid, ids, 'complete_name', None, context=context)
@@ -1991,16 +2022,20 @@ class stock_package(osv.osv):
res.add(pack.parent_id.id)
return list(res)
# TODO: Problem when package is empty!
#
def _get_package_info(self, cr, uid, ids, name, args, context=None):
default_company_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.id
res = {}.fromkeys(ids, {'location_id': False, 'company_id': default_company_id})
for pack in self.browse(cr, uid, ids, context=context):
if pack.quant_ids:
res[pack.id]['location_id'] = pack.quant_ids[0].location_id.id
res[pack.id]['owner_id'] = pack.quant_ids[0].owner_id and pack.quant_ids[0].owner_id.id or False
res[pack.id]['company_id'] = pack.quant_ids[0].company_id.id
elif pack.children_ids:
res[pack.id]['location_id'] = pack.children_ids[0].location_id.id
res[pack.id]['company_id'] = pack.children_ids[0].company_id.id
res[pack.id]['location_id'] = pack.children_ids[0].location_id and pack.children_ids[0].location_id.id or False
res[pack.id]['owner_id'] = pack.children_ids[0].owner_id and pack.children_ids[0].owner_id.id or False
res[pack.id]['company_id'] = pack.children_ids[0].company_id and pack.children_ids[0].company_id.id or False
return res
_columns = {
@@ -2012,12 +2047,21 @@ class stock_package(osv.osv):
'location_id': fields.function(_get_package_info, type='many2one', relation='stock.location', string='Location', multi="package",
store={
'stock.quant': (_get_packages, ['location_id'], 10),
'stock.quant.package': (_get_packages_to_relocate, ['children_ids', 'quant_ids', 'parent_id'], 10),
'stock.quant.package': (_get_packages_to_relocate, ['quant_ids', 'children_ids', 'parent_id'], 10),
}, readonly=True),
'quant_ids': fields.one2many('stock.quant', 'package_id', 'Bulk Content'),
'parent_id': fields.many2one('stock.quant.package', 'Parent Package', help="The package containing this item"),
'children_ids': fields.one2many('stock.quant.package', 'parent_id', 'Contained Packages'),
'company_id': fields.function(_get_package_info, type="many2one", relation='res.company', string='Company', multi="package"),
'company_id': fields.function(_get_package_info, type="many2one", relation='res.company', string='Company', multi="package",
store={
'stock.quant': (_get_packages, ['company_id'], 10),
'stock.quant.package': (_get_packages_to_relocate, ['quant_ids', 'children_ids', 'parent_id'], 10),
}, readonly=True),
'owner_id': fields.function(_get_package_info, type='many2one', relation='res.partner', string='Owner', multi="package",
store={
'stock.quant': (_get_packages, ['owner_id'], 10),
'stock.quant.package': (_get_packages_to_relocate, ['quant_ids', 'children_ids', 'parent_id'], 10),
}, readonly=True),
}
_defaults = {
'name': lambda self, cr, uid, context: self.pool.get('ir.sequence').get(cr, uid, 'stock.quant.package') or _('Unknown Pack')
@@ -2277,14 +2321,7 @@ class stock_warehouse_orderpoint(osv.osv):
})
return super(stock_warehouse_orderpoint, self).copy(cr, uid, id, default, context=context)
class product_template(osv.osv):
_inherit = "product.template"
_columns = {
'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 stock_picking_type(osv.osv):
_name = "stock.picking.type"
+35 -3
View File
@@ -120,6 +120,8 @@
<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="default_location_src_id" ref="stock_location_stock"/>
<field name="default_location_dest_id" ref="stock_location_stock"/>
<field name="code_id">internal</field>
</record>
@@ -156,15 +158,30 @@ watch your stock valuation, and track production lots upstream and downstream (b
-->
<record id="procurement_rule_customer0" model="procurement.rule">
<field name="name">Stock > Customer</field>
<field name="name">Stock → Customer</field>
<field name="action">move</field>
<field name="location_src_id" ref="stock_location_stock"/>
<field name="location_id" ref="stock_location_customers"/>
<field name="picking_type_id" ref="picking_type_out"/>
<field name="procure_method">make_to_stock</field>
</record>
<record id="route_warehouse0_mto" model='stock.location.route'>
<field name="name">MTO</field>
<field name="sequence">10</field>
</record>
<record id="procurement_rule_customer_mto" model="procurement.rule">
<field name="name">Stock → Customer</field>
<field name="action">move</field>
<field name="location_id" ref="stock_location_customers"/>
<field name="location_src_id" ref="stock_location_stock"/>
<field name="picking_type_id" ref="picking_type_out"/>
<field name="procure_method">make_to_order</field>
<field name="route_id" ref="route_warehouse0_mto"/>
</record>
<!--
Properties
-->
@@ -226,5 +243,20 @@ watch your stock valuation, and track production lots upstream and downstream (b
<field name="number_next">1</field>
<field name="number_increment">1</field>
</record>
<!--
Add Warehouse to picking_type
-->
<record id="picking_type_in" model="stock.picking.type">
<field name="warehouse_id" ref="warehouse0"/>
</record>
<record id="picking_type_out" model="stock.picking.type">
<field name="warehouse_id" ref="warehouse0"/>
</record>
<record id="picking_type_internal" model="stock.picking.type">
<field name="warehouse_id" ref="warehouse0"/>
</record>
</data>
</openerp>
+35 -4
View File
@@ -604,7 +604,7 @@
<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="product_qty" attrs="{'required': [('product_id', '!=', False)]}"/>
<field name="result_package_id" groups="stock.group_tracking_lot"/>
</tree>
</field>
@@ -1475,10 +1475,14 @@
<group>
<field name="location_src_id"/>
<field name="location_id"/>
<field name="route_id"/>
<field name="route_sequence"/>
<field name="sequence"/>
</group>
<group>
<field name="action"/>
<field name="picking_type_id" attrs="{'required': [('action', '!=', 'manufacture')]}"/>
<field name="procure_method" attrs="{'invisible': [('action', '!=', 'move')]}"/>
<field name="group_id"/>
</group>
</group>
@@ -1592,7 +1596,7 @@
<field name="inherit_id" ref="product.product_template_form_view"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='type']" position="after">
<field name="supply_method"/>
<field name="route_ids" widget="many2many_tags"/>
</xpath>
</field>
</record>
@@ -1625,8 +1629,8 @@
<button string="Orderpoints" name="%(product_open_orderpoint)d" type="action" attrs="{'invisible':[('type', '=', 'service')]}"/>
</xpath>
<xpath expr="//field[@name='standard_price']" position="before">
<field name="supply_method" groups="base.group_user"/>
<xpath expr="//field[@name='type']" position="after">
<field name="route_ids" widget="many2many_tags"/>
</xpath>
<group name="procurement_uom" position="before">
@@ -1715,6 +1719,7 @@
<group>
<field name="packaging_id"/>
<field name='company_id' groups="base.group_multi_company"/>
<field name='owner_id' groups="stock.group_tracking_owner"/>
</group>
<group>
<field name="location_id"/>
@@ -1790,5 +1795,31 @@
<field name="res_model">stock.quant.package</field>
</record>
<menuitem id="menu_package" name="Packages" parent="menu_stock_product" action="action_package_view"/>
<!--Routes-->
<record id="stock_location_route_form_view" model="ir.ui.view">
<field name="name">stock.location.route.form</field>
<field name="model">stock.location.route</field>
<field eval="7" name="priority" />
<field name="arch" type="xml">
<form string="Route" version="7.0">
<sheet>
<div class="oe_title">
<h1>
<label string="Route name"/>
</h1>
<h2>
<field name="name" class="oe_inline"/>
</h2>
</div>
<field name="sequence" groups="base.group_no_one"/>
<group string="Pull Rules" colspan="4" >
<field name="pull_ids" colspan="4" nolabel="1"/>
</group>
</sheet>
</form>
</field>
</record>
</data>
</openerp>
+80 -74
View File
@@ -20,21 +20,20 @@
##############################################################################
from openerp.osv import fields, osv
from openerp.tools.translate import _
#----------------------------------------------------------
# Procurement Rule
#----------------------------------------------------------
class procurement_rule(osv.osv):
_inherit = 'procurement.rule'
_columns= {
_columns = {
'invoice_state': fields.selection([
("invoiced", "Invoiced"),
("2binvoiced", "To Be Invoiced"),
("none", "Not Applicable")], "Invoice Status",
required=False),
}
#----------------------------------------------------------
# Procurement Order
#----------------------------------------------------------
@@ -43,18 +42,17 @@ class procurement_rule(osv.osv):
class procurement_order(osv.osv):
_inherit = "procurement.order"
_columns = {
'invoice_state': fields.selection(
[("invoiced", "Invoiced"),
'invoice_state': fields.selection([("invoiced", "Invoiced"),
("2binvoiced", "To Be Invoiced"),
("none", "Not Applicable")
], "Invoice Control", required=True),
], "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'
}
@@ -70,15 +68,51 @@ class stock_move(osv.osv):
'invoice_state': fields.selection([("invoiced", "Invoiced"),
("2binvoiced", "To Be Invoiced"),
("none", "Not Applicable")], "Invoice Control",
select=True, required=True, track_visibility='onchange',
select=True, required=True, track_visibility='onchange',
states={'draft': [('readonly', False)]}),
}
_defaults= {
_defaults = {
'invoice_state': lambda *args, **argv: 'none'
}
def _get_master_data(self, cr, uid, move, company, context=None):
''' returns a tupple (browse_record(res.partner), ID(res.users), ID(res.currency)'''
return move.picking_id.partner_id, uid, company.currency_id.id
def _create_invoice_line_from_vals(self, cr, uid, move, invoice_line_vals, context=None):
return self.pool.get('account.invoice.line').create(cr, uid, invoice_line_vals, context=context)
def _get_invoice_line_vals(self, cr, uid, move, partner, inv_type, context=None):
fp_obj = self.pool.get('account.fiscal.position')
# Get account_id
if inv_type in ('out_invoice', 'out_refund'):
account_id = move.product_id.property_account_income.id
if not account_id:
account_id = move.product_id.categ_id.property_account_income_categ.id
else:
account_id = move.product_id.property_account_expense.id
if not account_id:
account_id = move.product_id.categ_id.property_account_expense_categ.id
fiscal_position = partner.property_account_position
account_id = fp_obj.map_account(cr, uid, fiscal_position, account_id)
# set UoS if it's a sale and the picking doesn't have one
uos_id = move.product_uom.id
quantity = move.product_uom_qty
if move.product_uos:
uos_id = move.product_uos.id
quantity = move.product_uos_qty
return {
'name': move.name,
'account_id': account_id,
'product_id': move.product_id.id,
'uos_id': uos_id,
'quantity': quantity,
'price_unit': move.product_id.list_price, # TODO: use price_get
'discount': 0.0,
'account_analytic_id': False,
}
#----------------------------------------------------------
# Picking
#----------------------------------------------------------
@@ -90,9 +124,9 @@ 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.invoice_state=='invoiced':
if move.invoice_state == 'invoiced':
result[pick.id] = 'invoiced'
elif move.invoice_state=='2binvoiced':
elif move.invoice_state == '2binvoiced':
result[pick.id] = '2binvoiced'
break
return result
@@ -100,7 +134,7 @@ class stock_picking(osv.osv):
def __get_picking_move(self, cr, uid, ids, context={}):
res = []
for move in self.pool.get('stock.move').browse(cr, uid, ids, context=context):
if move.picking_id:
if move.picking_id:
res.append(move.picking_id.id)
return res
@@ -109,11 +143,11 @@ class stock_picking(osv.osv):
("invoiced", "Invoiced"),
("2binvoiced", "To Be Invoiced"),
("none", "Not Applicable")
], string="Invoice Control", required=True,
], string="Invoice Control", required=True,
store={
'stock.picking': (lambda self, cr, uid, ids, c={}: ids, ['state'], 10),
'stock.move': (__get_picking_move, ['picking_id'], 10),
'stock.picking': (lambda self, cr, uid, ids, c={}: ids, ['state'], 10),
'stock.move': (__get_picking_move, ['picking_id', 'invoice_state'], 10),
},
),
}
@@ -121,6 +155,12 @@ class stock_picking(osv.osv):
'invoice_state': lambda *args, **argv: 'none'
}
def _create_invoice_from_picking(self, cr, uid, picking, vals, context=None):
''' This function simply creates the invoice from the given values. It is overriden in delivery module to add the delivery costs.
'''
invoice_obj = self.pool.get('account.invoice')
return invoice_obj.create(cr, uid, vals, context=context)
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.
@param journal_id: Id of journal
@@ -133,8 +173,8 @@ class stock_picking(osv.osv):
for picking in self.browse(cr, uid, ids, context=context):
key = group and picking.id or True
for move in picking.move_lines:
if move.procurement_id and (move.procurement_id.invoice_state=='2binvoiced'):
if (move.state <> 'cancel') and not move.scrapped:
if move.procurement_id and (move.procurement_id.invoice_state == '2binvoiced') or move.invoice_state == '2binvoiced':
if (move.state != 'cancel') and not move.scrapped:
todo.setdefault(key, [])
todo[key].append(move)
invoices = []
@@ -144,14 +184,13 @@ class stock_picking(osv.osv):
def __invoice_create_line(self, cr, uid, moves, journal_id=False, inv_type='out_invoice', context=None):
invoice_obj = self.pool.get('account.invoice')
move_obj = self.pool.get('stock.move')
invoices = {}
for move in moves:
sale_line = move.procurement_id.sale_line_id
sale = sale_line.order_id
partner = sale.partner_invoice_id
currency_id = sale.pricelist_id.currency_id.id
key = (partner.id, currency_id, sale.company_id.id, sale.user_id and sale.user_id.id or False)
company = move.company_id
origin = move.picking_id.name
partner, user_id, currency_id = move_obj._get_master_data(cr, uid, move, company, context=context)
key = (partner.id, currency_id, company.id, user_id)
if key not in invoices:
# Get account and payment terms
@@ -162,66 +201,33 @@ class stock_picking(osv.osv):
account_id = partner.property_account_payable.id
payment_term = partner.property_supplier_payment_term.id or False
invoice_id = invoice_obj.create(cr, uid, {
'origin': sale.name,
invoice_vals = {
'origin': origin,
'date_invoice': context.get('date_inv', False),
'user_id': sale.user_id and sale.user_id.id or False,
'user_id': user_id,
'partner_id': partner.id,
'account_id': account_id,
'payment_term': payment_term,
'type': inv_type,
'fiscal_position': partner.property_account_position.id,
'company_id': sale.company_id.id,
'currency_id': sale.pricelist_id.currency_id.id,
'company_id': company.id,
'currency_id': currency_id,
'journal_id': journal_id,
}, context=context)
}
invoice_id = self._create_invoice_from_picking(cr, uid, move.picking_id, invoice_vals, context=context)
invoices[key] = invoice_id
# Get account_id
if inv_type in ('out_invoice', 'out_refund'):
account_id = move.product_id.property_account_income.id
if not account_id:
account_id = move.product_id.categ_id.property_account_income_categ.id
else:
account_id = move.product_id.property_account_expense.id
if not account_id:
account_id = move.product_id.categ_id.property_account_expense_categ.id
fp_obj = self.pool.get('account.fiscal.position')
fiscal_position = partner.property_account_position
account_id = fp_obj.map_account(cr, uid, fiscal_position, account_id)
invoice_line_vals = move_obj._get_invoice_line_vals(cr, uid, move, partner, inv_type, context=context)
invoice_line_vals['invoice_id'] = invoices[key]
invoice_line_vals['origin'] = origin
# set UoS if it's a sale and the picking doesn't have one
if move.product_uos:
uos_id = move.product_uos.id
quantity = move.product_uos_qty
else:
uos_id = move.product_uom.id
quantity = move.product_uom_qty
move_obj._create_invoice_line_from_vals(cr, uid, move, invoice_line_vals, context=context)
invoice_line_id = self.pool.get('account.invoice.line').create(cr, uid, {
'name': move.name,
'origin': move.picking_id and move.picking_id.origin or False,
'invoice_id': invoices[key],
'account_id': account_id,
'product_id': move.product_id.id,
'uos_id': uos_id,
'quantity': quantity,
'price_unit': sale_line.price_unit,
'discount': sale_line.discount,
'invoice_line_tax_id': [(6, 0, [x.id for x in sale_line.tax_id])],
'account_analytic_id': sale.project_id and sale.project_id.id or False,
}, context=context)
self.pool.get('sale.order.line').write(cr, uid, [sale_line.id], {
'invoice_lines': [(4, invoice_line_id)]
}, context=context)
self.pool.get('sale.order').write(cr, uid, [sale.id], {
'invoice_ids': [(4, invoices[key])],
})
self.pool.get('procurement.order').write(cr, uid, [move.procurement_id.id], {
'invoice_state': 'invoiced',
}, context=context)
move_obj.write(cr, uid, move.id, {'invoice_state': 'invoiced'}, context=context)
if move.procurement_id:
self.pool.get('procurement.order').write(cr, uid, [move.procurement_id.id], {
'invoice_state': 'invoiced',
}, context=context)
invoice_obj.button_compute(cr, uid, invoices.values(), context=context, set_total=(inv_type in ('in_invoice', 'in_refund')))
return invoices.values()
+42 -32
View File
@@ -19,23 +19,13 @@
#
##############################################################################
from datetime import datetime
from dateutil.relativedelta import relativedelta
import time
from operator import itemgetter
from itertools import groupby
from openerp.osv import fields, osv
from openerp.tools.translate import _
from openerp import netsvc
from openerp import tools
from openerp.tools import float_compare, DEFAULT_SERVER_DATETIME_FORMAT
import openerp.addons.decimal_precision as dp
import logging
_logger = logging.getLogger(__name__)
#----------------------------------------------------------
# Stock Location
#----------------------------------------------------------
@@ -44,12 +34,12 @@ class stock_location(osv.osv):
_inherit = "stock.location"
_columns = {
'valuation_in_account_id': fields.many2one('account.account', 'Stock Valuation Account (Incoming)', domain = [('type','=','other')],
'valuation_in_account_id': fields.many2one('account.account', 'Stock Valuation Account (Incoming)', domain=[('type', '=', 'other')],
help="Used for real-time inventory valuation. When set on a virtual location (non internal type), "
"this account will be used to hold the value of products being moved from an internal location "
"into this location, instead of the generic Stock Output Account set on the product. "
"This has no effect for internal locations."),
'valuation_out_account_id': fields.many2one('account.account', 'Stock Valuation Account (Outgoing)', domain = [('type','=','other')],
'valuation_out_account_id': fields.many2one('account.account', 'Stock Valuation Account (Outgoing)', domain=[('type', '=', 'other')],
help="Used for real-time inventory valuation. When set on a virtual location (non internal type), "
"this account will be used to hold the value of products being moved out of this location "
"and into an internal location, instead of the generic Stock Output Account set on the product. "
@@ -63,7 +53,6 @@ class stock_location(osv.osv):
class stock_quant(osv.osv):
_inherit = "stock.quant"
def _get_inventory_value(self, cr, uid, line, prodbrow, context=None):
#TODO: what in case of partner_id
if prodbrow[(line.company_id.id, line.product_id.id)].cost_method in ('real'):
@@ -170,7 +159,7 @@ class stock_quant(osv.osv):
'name': move.name,
'product_id': quant.product_id.id,
'quantity': quant.qty,
'product_uom_id': quant.product_id.uom_id.id,
'product_uom_id': quant.product_id.uom_id.id,
'ref': move.picking_id and move.picking_id.name or False,
'date': time.strftime('%Y-%m-%d'),
'partner_id': partner_id,
@@ -181,7 +170,7 @@ class stock_quant(osv.osv):
'name': move.name,
'product_id': quant.product_id.id,
'quantity': quant.qty,
'product_uom_id': quant.product_id.uom_id.id,
'product_uom_id': quant.product_id.uom_id.id,
'ref': move.picking_id and move.picking_id.name or False,
'date': time.strftime('%Y-%m-%d'),
'partner_id': partner_id,
@@ -197,17 +186,42 @@ 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)
def _reconcile_single_negative_quant(self, cr, uid, to_solve_quant, quant, quant_neg, qty, context=None):
move = self._get_latest_move(cr, uid, to_solve_quant, context=context)
quant_neg_position = quant_neg.negative_dest_location_id.usage
remaining_to_solve_quant = super(stock_quant, self)._reconcile_single_negative_quant(cr, uid, to_solve_quant, quant, quant_neg, qty, context=context)
#update the standard price of the product, only if we would have done it if we'd have had enough stock at first, which means
#1) there isn't any negative quant anymore
#2) the product cost's method is 'real'
#3) we just fixed a negative quant caused by an outgoing shipment
if not remaining_to_solve_quant and move.product_id.cost_method == 'real' and quant_neg_position != 'internal':
self.pool.get('stock.move')._store_average_cost_price(cr, uid, move, context=context)
class stock_move(osv.osv):
_inherit = "stock.move"
def action_done(self, cr, uid, ids, context=None):
self.product_price_update_before_done(cr, uid, ids, context=context)
super(stock_move, self).action_done(cr, uid, ids, context=context)
self.product_price_update(cr, uid, ids, context=context)
self.product_price_update_after_done(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
'''
def _store_average_cost_price(self, cr, uid, move, context=None):
''' move is a browe record '''
product_obj = self.pool.get('product.product')
move.refresh()
if any([q.qty <= 0 for q in move.quant_ids]):
#if there is a negative quant, the standard price shouldn't be updated
return
#Note: here we can't store a 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 costs 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)
def product_price_update_before_done(self, cr, uid, ids, context=None):
product_obj = self.pool.get('product.product')
for move in self.browse(cr, uid, ids, context=context):
#adapt standard price on incomming moves if the product cost_method is 'average'
@@ -221,21 +235,17 @@ class stock_move(osv.osv):
else:
# Get the standard price
amount_unit = product.price_get('standard_price', context=ctx)[product.id]
new_std_price = (amount_unit * (product_avail - move.product_qty) + (move.price_unit * move.product_qty)) / product_avail
new_std_price = ((amount_unit * product_avail) + (move.price_unit * move.product_qty)) / (product_avail + move.product_qty)
# Write the field according to price type field
product_obj.write(cr, uid, [product.id], {'standard_price': new_std_price}, context=context)
def product_price_update_after_done(self, cr, uid, ids, context=None):
'''
This method adapts the price on the product when necessary
'''
for move in self.browse(cr, uid, ids, context=context):
#adapt standard price on outgoing moves if the product cost_method is 'real', so that a return
#or an inventory loss is made using the last value used for an outgoing valuation.
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)
#store the average price of the move on the move and product form
self._store_average_cost_price(cr, uid, move, context=context)
@@ -71,7 +71,6 @@ class stock_invoice_onshipping(osv.osv_memory):
def open_invoice(self, cr, uid, ids, context=None):
if context is None:
context = {}
print 'Create Invoice', context
invoice_ids = self.create_invoice(cr, uid, ids, context=context)
if not invoice_ids:
raise osv.except_osv(_('Error!'), _('No invoice created!'))
@@ -97,7 +96,6 @@ class stock_invoice_onshipping(osv.osv_memory):
def create_invoice(self, cr, uid, ids, context=None):
context = context or {}
picking_pool = self.pool.get('stock.picking')
onshipdata_obj = self.read(cr, uid, ids, ['journal_id', 'group', 'invoice_date', 'inv_type'])
@@ -15,6 +15,7 @@
<field name="name">dropship</field>
<field name="sequence_id" ref="seq_picking_type_dropship"/>
<field name="code_id">incoming</field>
<field name="default_location_dest_id" ref="stock.stock_location_suppliers"/>
</record>
@@ -22,7 +23,9 @@
<record id="route_drop_shipping" model='stock.location.route'>
<field name="name">Drop Shipping</field>
<field name="sequence">3</field>
<field name="sale">True</field>
<field name="sale_selectable" eval="True"/>
<field name="product_selectable" eval="False"/>
<field name="product_categ_selectable" eval="True"/>
</record>
<record id="procurement_rule_drop_shipping" model="procurement.rule">
@@ -39,7 +42,9 @@
<!-- Crossdock -->
<record id="stock_location.route_warehouse0_crossdock" model='stock.location.route'>
<field name="name">Crossdock</field>
<field name="sale">True</field>
<field name="sale_selectable" eval="True"/>
<field name="product_categ_selectable" eval="True"/>
</record>
<record id="procurement_rule_customer0_xdock" model="procurement.rule">
<field name="name"> Supplier → Output</field>
@@ -49,5 +54,7 @@
<field name="route_id" ref="stock_location.route_warehouse0_crossdock"/>
<field name="picking_type_id" ref="stock.picking_type_in"/>
</record>
</data>
</openerp>
@@ -60,4 +60,4 @@
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'
assert purchase_id, 'No Purchase Quotation is created'
+2
View File
@@ -19,4 +19,6 @@
#
##############################################################################
import wizard
import stock_location
+4 -2
View File
@@ -96,10 +96,12 @@ You can use the demo data as follow:
'author': 'OpenERP SA',
'images': ['images/pulled_flow.jpeg','images/pushed_flow.jpeg'],
'depends': ['procurement','stock'],
'data': ['stock_location_view.xml',
'data': ['wizard/configure_wh_view.xml',
'stock_location_view.xml',
'stock_location_data.xml',
'security/stock_location_security.xml',
'security/ir.model.access.csv'],
'security/ir.model.access.csv',
'res_config_view.xml'],
'demo': [
'stock_location_demo_cpu1.xml',
'stock_location_demo_cpu3.yml',
+17
View File
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="view_stock_location_config_settings" model="ir.ui.view">
<field name="name">stock settings</field>
<field name="model">stock.config.settings</field>
<field name="inherit_id" ref="stock.view_stock_config_settings"/>
<field name="arch" type="xml">
<separator string="Location &amp; Warehouse" position="before">
<button name="%(action_view_stock_configure_wh)d" string="Configure a new warehouse" type="action"/>
</separator>
</field>
</record>
</data>
</openerp>
+9 -26
View File
@@ -25,24 +25,23 @@ from dateutil.relativedelta import relativedelta
from openerp.tools.translate import _
class stock_location_route(osv.osv):
_name = 'stock.location.route'
_inherit = 'stock.location.route'
_description = "Inventory Routes"
_order = 'sequence'
_columns = {
'name': fields.char('Route Name', required=True),
'sequence': fields.integer('Sequence'),
'pull_ids': fields.one2many('procurement.rule', 'route_id', 'Pull Rules'),
'push_ids': fields.one2many('stock.location.path', 'route_id', 'Push Rules'),
'product_selectable': fields.boolean('Selectable on Product'),
'product_categ_selectable': fields.boolean('Selectable on Product Category'),
'warehouse_selectable': fields.boolean('Selectable on Warehouse'),
}
_defaults = {
'sequence': lambda self,cr,uid,ctx: 0,
'product_selectable': True
}
class stock_warehouse(osv.osv):
_inherit = 'stock.warehouse'
_columns = {
'route_id': fields.many2one('stock.location.route', 'Default Routes', help='Default route through the warehouse', required=True),
'route_id': fields.many2one('stock.location.route', 'Default Routes', domain="[('warehouse_selectable', '=', True)]", help='Default route through the warehouse', required=True),
}
@@ -112,33 +111,18 @@ class stock_location_path(osv.osv):
class procurement_rule(osv.osv):
_inherit = 'procurement.rule'
def _get_rules(self, cr, uid, ids, context=None):
res = []
for route in self.browse(cr, uid, ids):
res += [x.id for x in route.pull_ids]
return res
_columns = {
'route_id': fields.many2one('stock.location.route', 'Route',
help="If route_id is False, the rule is global"),
'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'),
'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',
'propagate': True,
'delay': 0,
}
class procurement_order(osv.osv):
_inherit = 'procurement.order'
_columns = {
@@ -154,7 +138,6 @@ class procurement_order(osv.osv):
newdate = (datetime.strptime(procurement.date_planned, '%Y-%m-%d %H:%M:%S') - relativedelta(days=procurement.rule_id.delay or 0)).strftime('%Y-%m-%d %H:%M:%S')
d.update({
'date': newdate,
'procure_method': procurement.rule_id.procure_method or 'make_to_stock',
'propagate': procurement.rule_id.propagate,
})
return d
@@ -171,7 +154,7 @@ class procurement_order(osv.osv):
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:
res = self.pool.get('procurement.rule').search(cr, uid, domain, order='sequence', context=context)
res = self.pool.get('procurement.rule').search(cr, uid, domain + [('route_id', '=', False)], order='sequence', context=context)
return res
@@ -201,13 +184,13 @@ class product_removal_strategy(osv.osv):
class product_product(osv.osv):
_inherit = 'product.product'
_columns = {
'route_ids': fields.many2many('stock.location.route', 'stock_location_route_product', 'product_id', 'route_id', 'Routes'),
'route_ids': fields.many2many('stock.location.route', 'stock_location_route_product', 'product_id', 'route_id', 'Routes', domain="[('product_selectable', '=', True)]"),
}
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'),
'route_ids': fields.many2many('stock.location.route', 'stock_location_route_categ', 'categ_id', 'route_id', 'Routes', domain="[('product_categ_selectable', '=', True)]"),
'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'),
}
+48 -49
View File
@@ -24,48 +24,56 @@
<record id="route_warehouse0_mts" model='stock.location.route'>
<field name="name">Sale: Ship only</field>
<field name="sequence">20</field>
<field name="warehouse_selectable" eval="True"/>
<field name="product_selectable" eval="False"/>
</record>
<record id="procurement_rule_customer0_mts" model="procurement.rule">
<field name="name">Stock → output</field>
<field name="name">Stock → customer</field>
<field name="action">move</field>
<field name="location_id" ref="stock.stock_location_output"/>
<field name="location_id" ref="stock.stock_location_customers"/>
<field name="location_src_id" ref="stock.stock_location_stock"/>
<field name="procure_method">make_to_stock</field>
<field name="route_id" ref="route_warehouse0_mts"/>
<field name="picking_type_id" ref="stock.picking_type_internal"/>
</record>
<record id="procurement_rule_customer1_mts" model="procurement.rule">
<field name="name">Output → Customer</field>
<!-- Override from stock-->
<!--<record id="procurement_rule_customer_mto" model="procurement.rule">
<field name="name">Stock → Customer</field>
<field name="action">move</field>
<field name="location_id" ref="stock.stock_location_customers"/>
<field name="location_src_id" ref="stock.stock_location_output"/>
<field name="procure_method">make_to_order</field>
<field name="route_id" ref="route_warehouse0_mts"/>
<field name="location_src_id" ref="stock.stock_location_stock"/>
<field name="picking_type_id" ref="stock.picking_type_out"/>
</record>
<record id="route_warehouse0_mto" model='stock.location.route'>
<field name="name">Produce or buy on Customer Order</field>
<field name="sequence">10</field>
<field name="procure_method">make_to_order</field>
<field name="route_id" ref="stock.route_warehouse0_mto"/>
<field name="product_categ_selectable" eval="True"/>
</record>
<record id="route_warehouse0_mto" model='stock.location.route'>
<field name="name">MTO</field>
<field name="sequence">10</field>
<field name="product_categ_selectable" eval="True"/>
</record>-->
<record id="procurement_rule_customer0_mto" model="procurement.rule">
<field name="name">Stock → output</field>
<field name="name">Stock → Customer</field>
<field name="action">move</field>
<field name="location_id" ref="stock.stock_location_output"/>
<field name="location_id" ref="stock.stock_location_customers"/>
<field name="location_src_id" ref="stock.stock_location_stock"/>
<field name="procure_method">make_to_order</field>
<field name="route_id" ref="route_warehouse0_mto"/>
<field name="route_id" ref="stock.route_warehouse0_mto"/>
<field name="picking_type_id" ref="stock.picking_type_internal"/>
</record>
<record id="route_warehouse0_crossdock" model='stock.location.route'>
<field name="name">Crossdock</field>
<field name="sequence">9</field>
<field name="product_categ_selectable" eval="True"/>
<field name="sale_selectable" eval="True"/>
<field name="warehouse_selectable" eval="True"/>
<field name="product_selectable" eval="False"/>
</record>
<record id="procurement_rule_customer0_xdock" model="procurement.rule">
@@ -81,13 +89,19 @@
<record id="route_warehouse0_pack" model='stock.location.route'>
<field name="name">Sale: Pack + Ship</field>
<field name="sequence">15</field>
<field name="warehouse_selectable" eval="True"/>
<field name="product_selectable" eval="False"/>
</record>
<record id="route_warehouse0_mto_pack" model='stock.location.route'>
<field name="name">MTO pack</field>
<field name="sequence">10</field>
</record>
<record id="procurement_rule_customer0_pack" model="procurement.rule">
<field name="name">Stock → Pack</field>
<field name="name">Stock → Output</field>
<field name="action">move</field>
<field name="location_id" ref="location_pack_zone"/>
<field name="location_id" ref="stock.stock_location_output"/>
<field name="location_src_id" ref="stock.stock_location_stock"/>
<field name="procure_method">make_to_stock</field>
<field name="route_id" ref="route_warehouse0_pack"/>
@@ -103,25 +117,15 @@
<field name="route_id" ref="route_warehouse0_pack"/>
<field name="picking_type_id" ref="stock.picking_type_out"/>
</record>
<record id="procurement_rule_customer2_pack" model="procurement.rule">
<field name="name">Pack → Output</field>
<field name="action">move</field>
<field name="location_id" ref="stock.stock_location_output"/>
<field name="location_src_id" ref="location_pack_zone"/>
<field name="procure_method">make_to_order</field>
<field name="route_id" ref="route_warehouse0_pack"/>
<field name="picking_type_id" ref="stock.picking_type_internal"/>
</record>
<record id="procurement_rule_customer0_mto_pack" model="procurement.rule">
<field name="name">Stock → pack</field>
<field name="name">Stock → Output MTO</field>
<field name="action">move</field>
<field name="location_id" ref="location_pack_zone"/>
<field name="location_id" ref="stock.stock_location_output"/>
<field name="location_src_id" ref="stock.stock_location_stock"/>
<field name="procure_method">make_to_order</field>
<field name="route_id" ref="route_warehouse0_mto"/>
<field name="route_id" ref="route_warehouse0_mto_pack"/>
<field name="picking_type_id" ref="stock.picking_type_internal"/>
</record>
@@ -130,30 +134,21 @@
<record id="route_warehouse0_pickpack" model='stock.location.route'>
<field name="name">Sale: Pick + Pack + Ship</field>
<field name="sequence">20</field>
<field name="warehouse_selectable" eval="True"/>
<field name="product_selectable" eval="False"/>
</record>
<record id="procurement_rule_customer0_pickpack" model="procurement.rule">
<field name="name">Stock → Pick</field>
<field name="name">Stock → Pack</field>
<field name="action">move</field>
<field name="location_id" ref="location_pick_zone"/>
<field name="location_id" ref="location_pack_zone"/>
<field name="location_src_id" ref="stock.stock_location_stock"/>
<field name="procure_method">make_to_stock</field>
<field name="route_id" ref="route_warehouse0_pickpack"/>
<field name="picking_type_id" ref="stock.picking_type_internal"/>
</record>
<record id="procurement_rule_customer3_pickpack" model="procurement.rule">
<field name="name">Pick → Pack</field>
<field name="action">move</field>
<field name="location_id" ref="location_pack_zone"/>
<field name="location_src_id" ref="location_pick_zone"/>
<field name="procure_method">make_to_order</field>
<field name="route_id" ref="route_warehouse0_pickpack"/>
<field name="picking_type_id" ref="stock.picking_type_internal"/>
</record>
<record id="procurement_rule_customer1_pickpack" model="procurement.rule">
<field name="name">Output → Customer</field>
<field name="action">move</field>
@@ -174,14 +169,18 @@
<field name="picking_type_id" ref="stock.picking_type_internal"/>
</record>
<record id="route_warehouse0_mto_pick" model='stock.location.route'>
<field name="name">MTO pick</field>
<field name="sequence">10</field>
</record>
<record id="procurement_rule_customer0_mto_pack" model="procurement.rule">
<field name="name">Stock → Pick</field>
<record id="procurement_rule_customer0_mto_pick" model="procurement.rule">
<field name="name">Stock → Pack MTO</field>
<field name="action">move</field>
<field name="location_id" ref="location_pick_zone"/>
<field name="location_id" ref="location_pack_zone"/>
<field name="location_src_id" ref="stock.stock_location_stock"/>
<field name="procure_method">make_to_order</field>
<field name="route_id" ref="route_warehouse0_mto"/>
<field name="route_id" ref="route_warehouse0_mto_pick"/>
<field name="picking_type_id" ref="stock.picking_type_internal"/>
</record>
</data>
+18 -16
View File
@@ -6,17 +6,12 @@
<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>
@@ -166,25 +161,32 @@
<tree string="Routes">
<field name="sequence" widget="handle" />
<field name="name"/>
<field name="product_selectable"/>
<field name="product_categ_selectable"/>
<field name="warehouse_selectable"/>
</tree>
</field>
</record>
<record id="stock_location_route_form_view" model="ir.ui.view">
<record id="stock_location_route_form_view_inherit" model="ir.ui.view">
<field name="name">stock.location.route.form</field>
<field name="model">stock.location.route</field>
<field eval="7" name="priority" />
<field name="inherit_id" ref="stock.stock_location_route_form_view"/>
<field name="arch" type="xml">
<form string="Route">
<field name="name" />
<field name="sequence" groups="base.group_no_one"/>
<group string="Push Rules" colspan="4" >
<field name="push_ids" colspan="4" nolabel="1"/>
<xpath expr="//field[@name='sequence']" position="after">
<group>
<group>
<field name="product_categ_selectable"/>
<field name="product_selectable"/>
</group>
<group>
<field name="warehouse_selectable"/>
</group>
</group>
<group string="Pull Rules" colspan="4" >
<field name="pull_ids" colspan="4" nolabel="1"/>
</group>
</form>
<group string="Push Rules" colspan="4" >
<field name="push_ids" colspan="4" nolabel="1"/>
</group>
</xpath>
</field>
</record>
@@ -47,7 +47,6 @@
name: res_partner_microlinktechnologies0
min_qty: 5.0
route_ids: [stock_location_route_push]
supply_method: buy
type: product
uom_id: product.product_uom_unit
uom_po_id: product.product_uom_unit
+1
View File
@@ -0,0 +1 @@
import configure_wh
@@ -0,0 +1,204 @@
# -*- 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/>.
#
##############################################################################
from openerp.osv import fields, osv
class stock_configure_wh(osv.osv_memory):
_name = "stock.configure.wh"
_description = "Configure New WH"
_columns = {
'name': fields.char('Warehouse Name', required=True),
'address_id': fields.many2one('res.partner', 'Warehouse Address'),
'internal_stock_loc_id': fields.many2one('stock.location', 'Internal Stock Location', required=True),
'input_stock_loc_id': fields.many2one('stock.location', 'Input Location', required=True),
'output_stock_loc_id': fields.many2one('stock.location', 'Output Location', required=True),
'crossdock': fields.boolean('Crossdock', help='Whether this warehouse uses generally crossdock operations or not'),
'packing': fields.boolean('Packing', help='Whether we make packing in that warehouse while making outgoing shipments or not'),
'packing_loc_id': fields.many2one('stock.location', 'Packing Zone Location')
}
def onchange_crossdock(self, cr, uid, ids, crossdock=False, context=None):
if crossdock:
return {'value': {'packing': False, 'packing_loc_id': False}}
return True
def onchange_packing(self, cr, uid, ids, packing=False, stock_loc=False, context=None):
if packing:
return {'value': {'packing_loc_id': stock_loc}}
return True
def configure_wh(self, cr, uid, ids, context=None):
""" To Import stock inventory according to products available in the selected locations.
@param self: The object pointer.
@param cr: A database cursor
@param uid: ID of the user currently logged in
@param ids: the ID or list of IDs if we want more than one
@param context: A standard dictionary
@return:
"""
if context is None:
context = {}
route_obj = self.pool.get('stock.location.route')
pull_obj = self.pool.get('procurement.rule')
inventory_obj = self.pool.get('stock.inventory')
if ids and len(ids):
ids = ids[0]
obj = self.browse(cr, uid, ids, context=context)
data_obj = self.pool.get('ir.model.data')
wh_stock_loc = obj.internal_stock_loc_id.id
wh_input_stock_loc = obj.input_stock_loc_id.id
wh_output_stock_loc = obj.output_stock_loc_id.id
customer_loc = data_obj.get_object_reference(cr, uid, 'stock', 'stock_location_customers')[1]
supplier_loc = data_obj.get_object_reference(cr, uid, 'stock', 'stock_location_suppliers')[1]
#create route
new_route_id = False
route_data = {
'name': obj.name+': Ship',
'warehouse_selectable': True,
'product_selectable': False,
}
if obj.crossdock:
route_data['name'] = obj.name+': Crossdock'
elif wh_stock_loc != wh_output_stock_loc:
if obj.packing:
route_data['name'] = obj.name+': Pick + Pack + Ship'
else:
route_data['name'] = obj.name+': Pick + Ship'
new_route_id = route_obj.create(cr, uid, vals=route_data, context=context)
#create wh
wh_data = {
'name': obj.name,
'address_id': obj.address_id and obj.address_id.id or False,
'lot_stock_id': wh_stock_loc,
'route_id': new_route_id,
}
wh_obj = self.pool.get('stock.warehouse')
new_wh_id = wh_obj.create(cr, uid, vals=wh_data, context=context)
#create in, out, internal picking types for wh
#First create new sequence
seq_obj = self.pool.get('ir.sequence')
in_seq_id = seq_obj.create(cr, uid, values={'name': 'Picking in', 'prefix': 'IN', 'padding': 5}, context=context)
out_seq_id = seq_obj.create(cr, uid, values={'name': 'Picking out', 'prefix': 'OUT', 'padding': 5}, context=context)
internal_seq_id = seq_obj.create(cr, uid, values={'name': 'Picking internal', 'prefix': 'INT', 'padding': 5}, context=context)
#then create picking_type
picking_obj = self.pool.get('stock.picking.type')
in_picking_id = picking_obj.create(cr, uid, vals={'name': 'Receptions', 'warehouse_id': new_wh_id, 'code_id': 'incoming', 'sequence_id': in_seq_id, 'default_location_src_id': supplier_loc, 'default_location_dest_id': wh_stock_loc}, context=context)
out_picking_id = picking_obj.create(cr, uid, vals={'name': 'Delivery Orders', 'warehouse_id': new_wh_id, 'code_id': 'outgoing', 'sequence_id': out_seq_id, 'default_location_src_id': wh_stock_loc, 'default_location_dest_id': customer_loc}, context=context)
internal_picking_id = picking_obj.create(cr, uid, vals={'name': 'Internal Transfers', 'warehouse_id': new_wh_id, 'code_id': 'internal', 'sequence_id': internal_seq_id, 'default_location_src_id': wh_stock_loc, 'default_location_dest_id': wh_stock_loc, 'pack': obj.packing}, context=context)
#add pull rules to default route
#ship pull rules
pull_data = {
'name': obj.name+': Stock -> Customer',
'location_src_id': wh_stock_loc,
'location_id': customer_loc,
'propagate': True,
'route_id': new_route_id,
'action': 'move',
'picking_type_id': internal_picking_id,
'procure_method': 'make_to_stock'
}
if obj.crossdock:
cross_data = pull_data.copy()
cross_data.update({
'name': obj.name+': Output -> Customer',
'location_src_id': wh_output_stock_loc,
'picking_type_id': out_picking_id,
'procure_method': 'make_to_order',
})
pull_obj.create(cr, uid, vals=cross_data, context=context)
cross_data.update({
'name': obj.name+': Supplier -> Output',
'location_src_id': supplier_loc,
'location_id': wh_output_stock_loc,
'action': 'buy',
'picking_type_id': in_picking_id,
})
pull_obj.create(cr, uid, vals=cross_data, context=context)
#ship rules
elif wh_stock_loc == wh_output_stock_loc:
pull_obj.create(cr, uid, vals=pull_data, context=context)
#pick-pack-ship rules
elif obj.packing:
#if packing zone is the same as output or stock, only create one pull rule, otherwise, create two
if obj.packing_loc_id.id == wh_stock_loc:
pull_data.update({
'name': obj.name + ' Stock -> Output',
'location_id': wh_output_stock_loc,
})
pull_obj.create(cr, uid, vals=pull_data, context=context)
else:
pull_data.update({
'name': obj.name+' Stock -> Pack',
'location_id': obj.packing_loc_id.id,
})
pull_obj.create(cr, uid, vals=pull_data, context=context)
pull_data.update({
'name': obj.name+' Pack -> Output',
'location_src_id': obj.packing_loc_id.id,
'location_id': wh_output_stock_loc,
'picking_type_id': internal_picking_id,
'procure_method': 'make_to_order',
})
pull_obj.create(cr, uid, vals=pull_data, context=context)
pull_data.update({
'name': obj.name+' Output -> Customer',
'location_src_id': wh_output_stock_loc,
'location_id': customer_loc,
'picking_type_id': out_picking_id,
'procure_method': 'make_to_order',
})
pull_obj.create(cr, uid, vals=pull_data, context=context)
#pick-ship rules
else:
pull_data.update({
'name': obj.name + ' Stock -> Output',
'location_id': wh_output_stock_loc,
})
pull_obj.create(cr, uid, vals=pull_data, context=context)
pull_data.update({
'name': obj.name+' Output -> Customer',
'location_src_id': wh_output_stock_loc,
'location_id': customer_loc,
'picking_type_id': out_picking_id,
'procure_method': 'make_to_order',
})
pull_obj.create(cr, uid, vals=pull_data, context=context)
return {'type': 'ir.actions.act_window_close'}
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="stock_configure_wh" model="ir.ui.view">
<field name="name">Configure Warehouse</field>
<field name="model">stock.configure.wh</field>
<field name="arch" type="xml">
<form string="Configure Warehouse" version="7.0">
<sheet>
<separator string="Warehouse Informations"/>
<group>
<group>
<field name="name"/>
<field name="address_id"/>
</group>
<group>
<field name="crossdock" on_change="onchange_crossdock(crossdock)"/>
<field name="packing" attrs="{'invisible': [('crossdock', '=', True)]}" on_change="onchange_packing(packing, internal_stock_loc_id)"/>
</group>
</group>
<separator string="Warehouse locations"/>
<group>
<field name="internal_stock_loc_id"/>
<field name="input_stock_loc_id"/>
<field name="output_stock_loc_id"/>
<field name="packing_loc_id" attrs="{'invisible': [('packing', '=', False)], 'required': [('packing', '=', True)]}"/>
</group>
<footer>
<button name="configure_wh" string="Configure Warehouse" type="object" class="oe_highlight"/>
or
<button string="Cancel" class="oe_link" special="cancel" />
</footer>
</sheet>
</form>
</field>
</record>
<record id="action_view_stock_configure_wh" model="ir.actions.act_window">
<field name="name">Configure Warehouse</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">stock.configure.wh</field>
<field name="view_type">form</field>
<field name="view_mode">form</field>
<field name="view_id" ref="stock_configure_wh"/>
<field name="target">new</field>
</record>
</data>
</openerp>
+2 -2
View File
@@ -33,9 +33,9 @@ It creates 3 warehouses
'author': 'OpenERP SA',
'images': [],
'depends': ['stock_complex_routes'],
'data': ['stock_multi_warehouse.yml'],
'data': [],
'demo': [
'stock_multi_warehouse.yml'
],
'installable': True,
'test': [
@@ -1,3 +1,6 @@
-
!context
noupdate: 0
-
Create Two Customers
-
@@ -16,43 +19,43 @@
-
Create Location structure of Warehouse Panama Santiago District
-
!record {model: stock.location, id: location_panama_santiago}:
!record {model: stock.location, id: location_wh1}:
name: Panama Santiago District Warehouse
location_id: location_panama
-
Input
-
!record {model: stock.location, id: location_panama_santiago_input}:
!record {model: stock.location, id: location_wh1_input}:
name: Input
location_id: location_panama_santiago
location_id: location_wh1
-
Output
-
!record {model: stock.location, id: location_panama_santiago_output}:
!record {model: stock.location, id: location_wh1_output}:
name: Output
location_id: location_panama_santiago
location_id: location_wh1
-
Stock
-
!record {model: stock.location, id: location_panama_santiago_stock}:
!record {model: stock.location, id: location_wh1_stock}:
name: Stock
location_id: location_panama_santiago
location_id: location_wh1
-
!record {model: stock.location, id: location_panama_santiago_stock_area1}:
!record {model: stock.location, id: location_wh1_stock_area1}:
name: Area1
location_id: location_panama_santiago_stock
location_id: location_wh1_stock
-
!record {model: stock.location, id: location_panama_santiago_stock_area1_bin1}:
!record {model: stock.location, id: location_wh1_stock_area1_bin1}:
name: Bin1
location_id: location_panama_santiago_stock_area1
location_id: location_wh1_stock_area1
-
Create warehouse Panama Santiago District
-
!record {model: stock.warehouse, id: wh_panama_santiago}:
!record {model: stock.warehouse, id: wh_wh1}:
name: Panama Santiago District Warehouse
lot_stock_id: location_panama_santiago
lot_stock_id: location_wh1
-
Create picking type in for this warehouse
-
@@ -60,8 +63,8 @@
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
warehouse_id: wh_wh1
default_location_dest_id: location_wh1_input
-
Create picking type out for this warehouse
-
@@ -69,8 +72,8 @@
name: Deliveries
code_id: outgoing
sequence_id: stock.seq_picking_type_out
warehouse_id: wh_panama_santiago
default_location_src_id: location_panama_santiago_output
warehouse_id: wh_wh1
default_location_src_id: location_wh1_output
-
Create picking type internal for this warehouse
-
@@ -78,44 +81,44 @@
name: Internal
code_id: internal
sequence_id: stock.seq_picking_type_internal
warehouse_id: wh_panama_santiago
default_location_src_id: location_panama_santiago_input
default_location_dest_id: location_panama_santiago_stock
warehouse_id: wh_wh1
default_location_src_id: location_wh1_stock
default_location_dest_id: location_wh1_stock
-
Create Location structure of Warehouse Panama Main
-
!record {model: stock.location, id: location_panama_main}:
!record {model: stock.location, id: location_wh2}:
name: Panama Main Warehouse
location_id: location_panama
-
Input
-
!record {model: stock.location, id: location_panama_main_input}:
!record {model: stock.location, id: location_wh2_input}:
name: Input
location_id: location_panama_main
location_id: location_wh2
-
Output
-
!record {model: stock.location, id: location_panama_main_output}:
!record {model: stock.location, id: location_wh2_output}:
name: Output
location_id: location_panama_main
location_id: location_wh2
-
Stock
-
!record {model: stock.location, id: location_panama_main_stock}:
!record {model: stock.location, id: location_wh2_stock}:
name: Stock
location_id: location_panama_main
location_id: location_wh2
-
!record {model: stock.location, id: location_panama_main_stock_area2}:
!record {model: stock.location, id: location_wh2_stock_area2}:
name: Area2
location_id: location_panama_main_stock
location_id: location_wh2_stock
-
Create warehouse Panama main
-
!record {model: stock.warehouse, id: wh_panama_main}:
!record {model: stock.warehouse, id: wh_wh2}:
name: Panama Main Warehouse
lot_stock_id: location_panama_main
lot_stock_id: location_wh2
-
Create picking type in for warehouse panama main
-
@@ -123,8 +126,8 @@
name: Reception
code_id: incoming
sequence_id: stock.seq_picking_type_in
warehouse_id: wh_panama_main
default_location_dest_id: location_panama_main_input
warehouse_id: wh_wh2
default_location_dest_id: location_wh2_input
-
Create picking type out for warehouse panama main
-
@@ -132,8 +135,8 @@
name: Deliveries
code_id: outgoing
sequence_id: stock.seq_picking_type_out
warehouse_id: wh_panama_main
default_location_src_id: location_panama_main_output
warehouse_id: wh_wh2
default_location_src_id: location_wh2_output
-
Create picking type internal for warehouse panama main
-
@@ -141,56 +144,27 @@
name: Internal
code_id: internal
sequence_id: stock.seq_picking_type_internal
warehouse_id: wh_panama_main
default_location_src_id: location_panama_main_input
default_location_dest_id: location_panama_main_stock
warehouse_id: wh_wh2
default_location_src_id: location_wh2_stock
default_location_dest_id: location_wh2_stock
-
Create Location structure of Warehouse Dubai
-
!record {model: stock.location, id: location_panama_main}:
name: Panama Main Warehouse
location_id: location_panama
-
Create United Arab Emirates in Location structure
-
!record {model: stock.location, id: location_uae}:
name: United Arab Emirates
location_id: stock.stock_location_locations
-
Create Location structure of Dubai Warehouse
-
!record {model: stock.location, id: location_dubai}:
name: Dubai Warehouse
location_id: location_uae
child_ids:
- name: Input
- name: Output
- name: Stock
child_ids:
- name: Area3
-
Create Dubai warehouse
-
!record {model: stock.warehouse, id: wh_dubai}:
name: Dubai Warehouse
lot_stock_id: location_dubai
-
Create Ship Route for main panama warehouse
-
!record {model: stock.location.route, id: route_wh_panama_main_ship}:
!record {model: stock.location.route, id: route_wh_wh2_ship}:
name: Ship main panama
warehouse_selectable: True
product_selectable: False
pull_ids:
- invoice_state: none
location_id: location_panama_main_output
location_src_id: location_panama_main_stock
location_id: location_wh2_output
location_src_id: location_wh2_stock
name: Panama Main Stock -> Panama Main Output
procure_method: make_to_stock
picking_type_id: picking_type_main_internal
action: move
- invoice_state: none
location_id: stock.stock_location_customers
location_src_id: location_panama_main_output
location_src_id: location_wh2_output
name: Panama Main Output -> Customer
picking_type_id: picking_type_main_out
procure_method: make_to_order
@@ -198,28 +172,31 @@
-
Create Ship Route for panama santiago warehouse
-
!record {model: stock.location.route, id: route_wh_panama_santiago_ship}:
!record {model: stock.location.route, id: route_wh_wh1_ship}:
name: Ship panama santiago
warehouse_selectable: True
product_selectable: False
pull_ids:
- invoice_state: none
location_id: location_panama_santiago_output
location_src_id: location_panama_santiago_stock
location_id: location_wh1_output
location_src_id: location_wh1_stock
name: Santiago Stock -> Santiago Output
procure_method: make_to_stock
picking_type_id: picking_type_santiago_internal
action: move
- invoice_state: none
location_id: stock.stock_location_customers
location_src_id: location_panama_santiago_output
location_src_id: location_wh1_output
name: Santiago Output -> Customer
picking_type_id: picking_type_santiago_out
procure_method: make_to_order
action: move
-
Add routes to warehouses
-
!python {model: stock.warehouse}: |
self.write(cr, uid, [ref('wh_panama_main')], {'route_id': ref('route_wh_panama_main_ship')}, context=context)
self.write(cr, uid, [ref('wh_panama_santiago')], {'route_id': ref('route_wh_panama_santiago_ship')}, context=context)
self.write(cr, uid, [ref('wh_wh2')], {'route_id': ref('route_wh_wh2_ship'), 'lot_stock_id': ref('location_wh2_stock')}, context=context)
self.write(cr, uid, [ref('wh_wh1')], {'route_id': ref('route_wh_wh1_ship'), 'lot_stock_id': ref('location_wh1_stock')}, context=context)
-
Create Products
-
@@ -331,7 +308,7 @@
-
!record {model: stock.inventory, id: inventory_stock1}:
name: Inventory Stock 1
location_id: location_panama_santiago_stock
location_id: location_wh1_stock
-
I create the wizard to confirm the inventory
-
@@ -350,14 +327,14 @@
inventory_id: inventory_stock1
product_id: product_A
product_qty: 6
location_id: location_panama_santiago_stock
location_id: location_wh1_stock
-
!record {model: stock.inventory.line, id: inventory_stock1line_productB}:
inventory_id: inventory_stock1
product_id: product_B
product_qty: 12
location_id: location_panama_santiago_stock
location_id: location_wh1_stock
-
I confirm Stock1 inventory
-
@@ -368,7 +345,7 @@
-
!record {model: stock.inventory, id: inventory_stock2}:
name: Inventory Stock 2
location_id: location_panama_main_stock
location_id: location_wh2_stock
-
I fill Stock2 inventory
-
@@ -382,21 +359,21 @@
inventory_id: inventory_stock2
product_id: product_A
product_qty: 8
location_id: location_panama_main_stock
location_id: location_wh2_stock
-
!record {model: stock.inventory.line, id: inventory_stock2line_productB}:
inventory_id: inventory_stock2
product_id: product_B
product_qty: 120
location_id: location_panama_main_stock
location_id: location_wh2_stock
-
!record {model: stock.inventory.line, id: inventory_stock2line_productC_1}:
inventory_id: inventory_stock2
product_id: product_C
product_qty: 5
location_id: location_panama_main_stock
location_id: location_wh2_stock
prod_lot_id: lot003
-
@@ -404,7 +381,7 @@
inventory_id: inventory_stock2
product_id: product_C
product_qty: 15
location_id: location_panama_main_stock
location_id: location_wh2_stock
prod_lot_id: lot004
-
I confirm Stock2 inventory
@@ -416,7 +393,7 @@
-
!record {model: stock.inventory, id: inventory_stock1_area1}:
name: Inventory Stock1 area 1
location_id: location_panama_santiago_stock_area1
location_id: location_wh1_stock_area1
-
I fill Area1 inventory
-
@@ -430,14 +407,14 @@
inventory_id: inventory_stock1_area1
product_id: product_D
product_qty: 4
location_id: location_panama_santiago_stock_area1
location_id: location_wh1_stock_area1
-
!record {model: stock.inventory.line, id: inventory_stock1line_productD_2}:
inventory_id: inventory_stock1_area1
product_id: product_D
product_qty: 5
location_id: location_panama_santiago_stock_area1
location_id: location_wh1_stock_area1
-
I confirm Area1 inventory
-
@@ -448,7 +425,7 @@
-
!record {model: stock.inventory, id: inventory_stock2_area2}:
name: Inventory Stock2 area 2
location_id: location_panama_main_stock_area2
location_id: location_wh2_stock_area2
-
I fill Area2 inventory
-
@@ -462,14 +439,14 @@
inventory_id: inventory_stock2_area2
product_id: product_D
product_qty: 2
location_id: location_panama_main_stock_area2
location_id: location_wh2_stock_area2
-
!record {model: stock.inventory.line, id: inventory_stock2line_productD_2}:
inventory_id: inventory_stock2_area2
product_id: product_D
product_qty: 3
location_id: location_panama_main_stock_area2
location_id: location_wh2_stock_area2
-
I confirm Area2 inventory
-
@@ -481,7 +458,7 @@
-
!record {model: stock.inventory, id: inventory_stock1_area1_bin1}:
name: Inventory Stock1 area1 bin 1
location_id: location_panama_santiago_stock_area1_bin1
location_id: location_wh1_stock_area1_bin1
-
I fill Bin1 inventory
-
@@ -495,7 +472,7 @@
inventory_id: inventory_stock1_area1_bin1
product_id: product_C
product_qty: 3
location_id: location_panama_santiago_stock_area1_bin1
location_id: location_wh1_stock_area1_bin1
prod_lot_id: lot001
-
@@ -503,7 +480,7 @@
inventory_id: inventory_stock1_area1_bin1
product_id: product_C
product_qty: 14
location_id: location_panama_santiago_stock_area1_bin1
location_id: location_wh1_stock_area1_bin1
prod_lot_id: lot002
-
I confirm Bin1 inventory
@@ -515,7 +492,7 @@
-
!record {model: sale.order, id: so1}:
partner_id: customer_1
warehouse_id: wh_panama_main
warehouse_id: wh_wh2
order_line:
- product_id: product_A
product_uom_qty: 7
@@ -529,7 +506,7 @@
-
!record {model: sale.order, id: so2}:
partner_id: customer_1
warehouse_id: wh_panama_main
warehouse_id: wh_wh2
order_line:
- product_id: product_E
product_uom_qty: 5
@@ -543,7 +520,7 @@
-
!record {model: sale.order, id: so3}:
partner_id: customer_2
warehouse_id: wh_panama_santiago
warehouse_id: wh_wh1
order_line:
- product_id: product_A
product_uom_qty: 5