Merge pull request #2021 from odoo-dev/8.0-wmsbomtasks-jco
8.0 wmsbomtasks jco
This commit is contained in:
+41
-9
@@ -198,9 +198,9 @@ class mrp_bom(osv.osv):
|
||||
'type': fields.selection([('normal', 'Normal'), ('phantom', 'Set')], 'BoM Type', required=True,
|
||||
help= "Set: When processing a sales order for this product, the delivery order will contain the raw materials, instead of the finished product."),
|
||||
'position': fields.char('Internal Reference', help="Reference to a position in an external plan."),
|
||||
'product_tmpl_id': fields.many2one('product.template', 'Product', required=True),
|
||||
'product_tmpl_id': fields.many2one('product.template', 'Product', domain="[('type', '!=', 'service')]", required=True),
|
||||
'product_id': fields.many2one('product.product', 'Product Variant',
|
||||
domain="[('product_tmpl_id','=',product_tmpl_id)]",
|
||||
domain="['&', ('product_tmpl_id','=',product_tmpl_id), ('type','!=', 'service')]",
|
||||
help="If a product variant is defined the BOM is available only for this product."),
|
||||
'bom_line_ids': fields.one2many('mrp.bom.line', 'bom_id', 'BoM Lines', copy=True),
|
||||
'product_qty': fields.float('Product Quantity', required=True, digits_compute=dp.get_precision('Product Unit of Measure')),
|
||||
@@ -509,7 +509,7 @@ class mrp_production(osv.osv):
|
||||
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:
|
||||
if any(states) or len(states) == 0: #When no moves, ready_production will be False, but test_ready will pass
|
||||
res[production.id] = False
|
||||
return res
|
||||
|
||||
@@ -527,7 +527,8 @@ class mrp_production(osv.osv):
|
||||
'priority': fields.selection([('0', 'Not urgent'), ('1', 'Normal'), ('2', 'Urgent'), ('3', 'Very Urgent')], 'Priority',
|
||||
select=True, readonly=True, states=dict.fromkeys(['draft', 'confirmed'], [('readonly', False)])),
|
||||
|
||||
'product_id': fields.many2one('product.product', 'Product', required=True, readonly=True, states={'draft': [('readonly', False)]}),
|
||||
'product_id': fields.many2one('product.product', 'Product', required=True, readonly=True, states={'draft': [('readonly', False)]},
|
||||
domain=[('type','!=','service')]),
|
||||
'product_qty': fields.float('Product Quantity', digits_compute=dp.get_precision('Product Unit of Measure'), required=True, readonly=True, states={'draft': [('readonly', False)]}),
|
||||
'product_uom': fields.many2one('product.uom', 'Product Unit of Measure', required=True, readonly=True, states={'draft': [('readonly', False)]}),
|
||||
'product_uos_qty': fields.float('Product UoS Quantity', readonly=True, states={'draft': [('readonly', False)]}),
|
||||
@@ -831,6 +832,8 @@ class mrp_production(osv.osv):
|
||||
dicts = {}
|
||||
# Find product qty to be consumed and consume it
|
||||
for scheduled in production.product_lines:
|
||||
if scheduled.product_id.type == 'service':
|
||||
continue
|
||||
product_id = scheduled.product_id.id
|
||||
|
||||
consumed_qty = consumed_data.get(product_id, 0.0)
|
||||
@@ -1018,11 +1021,12 @@ class mrp_production(osv.osv):
|
||||
return res
|
||||
|
||||
def test_ready(self, cr, uid, ids):
|
||||
res = False
|
||||
res = True
|
||||
for production in self.browse(cr, uid, ids):
|
||||
if production.ready_production:
|
||||
res = True
|
||||
if production.move_lines and not production.ready_production:
|
||||
res = False
|
||||
return res
|
||||
|
||||
|
||||
|
||||
def _make_production_produce_line(self, cr, uid, production, context=None):
|
||||
@@ -1115,6 +1119,26 @@ class mrp_production(osv.osv):
|
||||
def _make_production_consume_line(self, cr, uid, line, context=None):
|
||||
return self._make_consume_line_from_data(cr, uid, line.production_id, line.product_id, line.product_uom.id, line.product_qty, line.product_uos.id, line.product_uos_qty, context=context)
|
||||
|
||||
|
||||
def _make_service_procurement(self, cr, uid, line, context=None):
|
||||
prod_obj = self.pool.get('product.product')
|
||||
if prod_obj.need_procurement(cr, uid, [line.product_id.id], context=context):
|
||||
vals = {
|
||||
'name': line.production_id.name,
|
||||
'origin': line.production_id.name,
|
||||
'company_id': line.production_id.company_id.id,
|
||||
'date_planned': line.production_id.date_planned,
|
||||
'product_id': line.product_id.id,
|
||||
'product_qty': line.product_qty,
|
||||
'product_uom': line.product_uom.id,
|
||||
'product_uos_qty': line.product_uos_qty,
|
||||
'product_uos': line.product_uos.id,
|
||||
}
|
||||
proc_obj = self.pool.get("procurement.order")
|
||||
proc = proc_obj.create(cr, uid, vals, context=context)
|
||||
proc_obj.run(cr, uid, [proc], context=context)
|
||||
|
||||
|
||||
def action_confirm(self, cr, uid, ids, context=None):
|
||||
""" Confirms production order.
|
||||
@return: Newly generated Shipment Id.
|
||||
@@ -1126,9 +1150,11 @@ class mrp_production(osv.osv):
|
||||
|
||||
stock_moves = []
|
||||
for line in production.product_lines:
|
||||
stock_move_id = self._make_production_consume_line(cr, uid, line, context=context)
|
||||
if stock_move_id:
|
||||
if line.product_id.type != 'service':
|
||||
stock_move_id = self._make_production_consume_line(cr, uid, line, context=context)
|
||||
stock_moves.append(stock_move_id)
|
||||
else:
|
||||
self._make_service_procurement(cr, uid, line, context=context)
|
||||
if stock_moves:
|
||||
self.pool.get('stock.move').action_confirm(cr, uid, stock_moves, context=context)
|
||||
production.write({'state': 'confirmed'}, context=context)
|
||||
@@ -1138,9 +1164,12 @@ class mrp_production(osv.osv):
|
||||
"""
|
||||
Checks the availability on the consume lines of the production order
|
||||
"""
|
||||
from openerp import workflow
|
||||
move_obj = self.pool.get("stock.move")
|
||||
for production in self.browse(cr, uid, ids, context=context):
|
||||
move_obj.action_assign(cr, uid, [x.id for x in production.move_lines], context=context)
|
||||
if self.pool.get('mrp.production').test_ready(cr, uid, [production.id]):
|
||||
workflow.trg_validate(uid, 'mrp.production', production.id, 'moves_ready', cr)
|
||||
|
||||
|
||||
def force_production(self, cr, uid, ids, *args):
|
||||
@@ -1148,9 +1177,12 @@ class mrp_production(osv.osv):
|
||||
@param *args: Arguments
|
||||
@return: True
|
||||
"""
|
||||
from openerp import workflow
|
||||
move_obj = self.pool.get('stock.move')
|
||||
for order in self.browse(cr, uid, ids):
|
||||
move_obj.force_assign(cr, uid, [x.id for x in order.move_lines])
|
||||
if self.pool.get('mrp.production').test_ready(cr, uid, [order.id]):
|
||||
workflow.trg_validate(uid, 'mrp.production', order.id, 'moves_ready', cr)
|
||||
return True
|
||||
|
||||
|
||||
|
||||
@@ -349,7 +349,6 @@
|
||||
<group>
|
||||
<field name="product_tmpl_id" on_change="onchange_product_tmpl_id(product_tmpl_id, product_qty, context)"/>
|
||||
<field name="product_id"/>
|
||||
<field name="product_uom" class="oe_inline" on_change="onchange_uom(product_tmpl_id, product_uom)" groups="product.group_uom"/>
|
||||
<label for="product_qty" string="Quantity"/>
|
||||
<div>
|
||||
<field name="product_qty" class="oe_inline" on_change="onchange_product_tmpl_id(product_tmpl_id, product_qty, context)"/>
|
||||
|
||||
@@ -56,9 +56,7 @@
|
||||
<record id="prod_trans_picking_ready" model="workflow.transition">
|
||||
<field name="act_from" ref="prod_act_confirmed"/>
|
||||
<field name="act_to" ref="prod_act_ready"/>
|
||||
<field name="trigger_model">stock.move</field>
|
||||
<field name="trigger_expr_id">consume_lines_get()</field>
|
||||
<field name="condition">test_ready()</field>
|
||||
<field name="signal">moves_ready</field>
|
||||
</record>
|
||||
<record id="prod_trans_ready_in_production" model="workflow.transition">
|
||||
<field name="act_from" ref="prod_act_ready"/>
|
||||
|
||||
+58
-21
@@ -65,6 +65,8 @@ class StockMove(osv.osv):
|
||||
"""
|
||||
bom_obj = self.pool.get('mrp.bom')
|
||||
move_obj = self.pool.get('stock.move')
|
||||
prod_obj = self.pool.get("product.product")
|
||||
proc_obj = self.pool.get("procurement.order")
|
||||
to_explode_again_ids = []
|
||||
processed_ids = []
|
||||
bis = self._check_phantom_bom(cr, uid, move, context=context)
|
||||
@@ -72,33 +74,65 @@ class StockMove(osv.osv):
|
||||
factor = move.product_qty
|
||||
bom_point = bom_obj.browse(cr, SUPERUSER_ID, bis[0], context=context)
|
||||
res = bom_obj._bom_explode(cr, SUPERUSER_ID, bom_point, move.product_id, factor, [])
|
||||
|
||||
state = 'confirmed'
|
||||
if move.state == 'assigned':
|
||||
state = 'assigned'
|
||||
for line in res[0]:
|
||||
valdef = {
|
||||
'picking_id': move.picking_id.id if move.picking_id else False,
|
||||
'product_id': line['product_id'],
|
||||
'product_uom': line['product_uom'],
|
||||
'product_uom_qty': line['product_qty'],
|
||||
'product_uos': line['product_uos'],
|
||||
'product_uos_qty': line['product_uos_qty'],
|
||||
'state': state,
|
||||
'name': line['name'],
|
||||
'procurement_id': move.procurement_id.id,
|
||||
'split_from': move.id, #Needed in order to keep purchase connection, but will be removed by unlink
|
||||
}
|
||||
mid = move_obj.copy(cr, uid, move.id, default=valdef)
|
||||
to_explode_again_ids.append(mid)
|
||||
product = prod_obj.browse(cr, uid, line['product_id'], context=context)
|
||||
if product.type != 'service':
|
||||
valdef = {
|
||||
'picking_id': move.picking_id.id if move.picking_id else False,
|
||||
'product_id': line['product_id'],
|
||||
'product_uom': line['product_uom'],
|
||||
'product_uom_qty': line['product_qty'],
|
||||
'product_uos': line['product_uos'],
|
||||
'product_uos_qty': line['product_uos_qty'],
|
||||
'state': state,
|
||||
'name': line['name'],
|
||||
'procurement_id': move.procurement_id.id,
|
||||
'split_from': move.id, #Needed in order to keep purchase connection, but will be removed by unlink
|
||||
}
|
||||
mid = move_obj.copy(cr, uid, move.id, default=valdef, context=context)
|
||||
to_explode_again_ids.append(mid)
|
||||
else:
|
||||
if prod_obj.need_procurement(cr, uid, [product.id], context=context):
|
||||
valdef = {
|
||||
'name': move.rule_id and move.rule_id.name or "/",
|
||||
'origin': move.origin,
|
||||
'company_id': move.company_id and move.company_id.id or False,
|
||||
'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_qty'],
|
||||
'product_uos': line['product_uos'],
|
||||
'group_id': move.group_id.id,
|
||||
'priority': move.priority,
|
||||
'partner_dest_id': move.partner_id.id,
|
||||
}
|
||||
if move.procurement_id:
|
||||
proc = proc_obj.copy(cr, uid, move.procurement_id.id, default=valdef, context=context)
|
||||
else:
|
||||
proc = proc_obj.create(cr, uid, valdef, context=context)
|
||||
proc_obj.run(cr, uid, [proc], context=context) #could be omitted
|
||||
|
||||
#delete the move with original product which is not relevant anymore
|
||||
move_obj.unlink(cr, SUPERUSER_ID, [move.id], context=context)
|
||||
|
||||
#check if new moves needs to be exploded
|
||||
if to_explode_again_ids:
|
||||
for new_move in self.browse(cr, uid, to_explode_again_ids, context=context):
|
||||
processed_ids.extend(self._action_explode(cr, uid, new_move, context=context))
|
||||
#return list of newly created move or the move id otherwise
|
||||
return processed_ids or [move.id]
|
||||
|
||||
if not move.split_from and move.procurement_id:
|
||||
# Check if procurements have been made to wait for
|
||||
moves = move.procurement_id.move_ids
|
||||
if len(moves) == 1:
|
||||
proc_obj.write(cr, uid, [move.procurement_id.id], {'state': 'done'}, context=context)
|
||||
|
||||
#delete the move with original product which is not relevant anymore
|
||||
move_obj.unlink(cr, SUPERUSER_ID, [move.id], context=context)
|
||||
#return list of newly created move or the move id otherwise, unless there is no move anymore
|
||||
return processed_ids or (not bis and [move.id]) or []
|
||||
|
||||
def action_confirm(self, cr, uid, ids, context=None):
|
||||
move_ids = []
|
||||
@@ -194,9 +228,12 @@ class StockMove(osv.osv):
|
||||
ids = [ids]
|
||||
res = super(StockMove, self).write(cr, uid, ids, vals, context=context)
|
||||
from openerp import workflow
|
||||
for move in self.browse(cr, uid, ids, context=context):
|
||||
if move.raw_material_production_id and move.raw_material_production_id.state == 'confirmed':
|
||||
workflow.trg_trigger(uid, 'stock.move', move.id, cr)
|
||||
if vals.get('state') == 'assigned':
|
||||
moves = self.browse(cr, uid, ids, context=context)
|
||||
orders = list(set([x.raw_material_production_id.id for x in moves if x.raw_material_production_id and x.raw_material_production_id.state == 'confirmed']))
|
||||
for order_id in orders:
|
||||
if self.pool.get('mrp.production').test_ready(cr, uid, [order_id]):
|
||||
workflow.trg_validate(uid, 'mrp.production', order_id, 'moves_ready', cr)
|
||||
return res
|
||||
|
||||
class stock_warehouse(osv.osv):
|
||||
|
||||
@@ -764,6 +764,9 @@ class product_template(osv.osv):
|
||||
return super(product_template, self).name_get(cr, user, ids, context)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class product_product(osv.osv):
|
||||
_name = "product.product"
|
||||
_description = "Product"
|
||||
@@ -1089,6 +1092,11 @@ class product_product(osv.osv):
|
||||
return super(product_product, self).create(cr, uid, vals, context=ctx)
|
||||
|
||||
|
||||
|
||||
def need_procurement(self, cr, uid, ids, context=None):
|
||||
return False
|
||||
|
||||
|
||||
class product_packaging(osv.osv):
|
||||
_name = "product.packaging"
|
||||
_description = "Packaging"
|
||||
|
||||
@@ -225,11 +225,11 @@ FaceTime HD Camera, 1.2 MP Photos</field>
|
||||
|
||||
<!-- -->
|
||||
|
||||
<record id="product_product_5" model="product.product">
|
||||
<record id="product_product_5b" model="product.product">
|
||||
<field name="name">Bose Mini Bluetooth Speaker</field>
|
||||
<field name="categ_id" ref="accessories"/>
|
||||
<field name="standard_price">600.0</field>
|
||||
<field name="list_price">147.0</field>
|
||||
<field name="standard_price">140.0</field>
|
||||
<field name="list_price">247.0</field>
|
||||
<field name="type">consu</field>
|
||||
<field name="uom_id" ref="product_uom_unit"/>
|
||||
<field name="uom_po_id" ref="product_uom_unit"/>
|
||||
@@ -239,6 +239,18 @@ FaceTime HD Camera, 1.2 MP Photos</field>
|
||||
</record>
|
||||
|
||||
|
||||
<record id="product_product_5" model="product.product">
|
||||
<field name="name">PC Assemble + Custom (PC on Demand) </field>
|
||||
<field name="categ_id" ref="accessories"/>
|
||||
<field name="standard_price">600.0</field>
|
||||
<field name="list_price">147.0</field>
|
||||
<field name="type">consu</field>
|
||||
<field name="uom_id" ref="product_uom_unit"/>
|
||||
<field name="uom_po_id" ref="product_uom_unit"/>
|
||||
<field name="description">Custom computer assembled on order based on customer's requirement.</field>
|
||||
<field name="default_code">B3423</field>
|
||||
</record>
|
||||
|
||||
<record id="product_product_6" model="product.product">
|
||||
<field name="name">iPad Mini</field>
|
||||
<field name="categ_id" ref="ipad"/>
|
||||
|
||||
@@ -23,6 +23,10 @@
|
||||
</record>
|
||||
|
||||
<record id="product_product_5" model="product.product">
|
||||
<field name="image" type="base64" file="product/static/img/product_product_5-image.jpg"/>
|
||||
</record>
|
||||
|
||||
<record id="product_product_5b" model="product.product">
|
||||
<field name="image" type="base64" file="product/static/img/product_product_5-image.png"/>
|
||||
</record>
|
||||
|
||||
|
||||
@@ -740,7 +740,7 @@ class purchase_order(osv.osv):
|
||||
'move_dest_id': procurement.move_dest_id.id, #move destination is same as procurement destination
|
||||
'group_id': procurement.group_id.id or group_id, #move group is same as group of procurements if it exists, otherwise take another group
|
||||
'procurement_id': procurement.id,
|
||||
'invoice_state': procurement.rule_id.invoice_state or (procurement.location_id and procurement.location_id.usage == 'customer' and procurement.invoice_state) or (order.invoice_method == 'picking' and '2binvoiced') or 'none', #dropship case takes from sale
|
||||
'invoice_state': procurement.rule_id.invoice_state or (procurement.location_id and procurement.location_id.usage == 'customer' and procurement.invoice_state=='picking' and '2binvoiced') or (order.invoice_method == 'picking' and '2binvoiced') or 'none', #dropship case takes from sale
|
||||
})
|
||||
diff_quantity -= min(procurement_qty, diff_quantity)
|
||||
res.append(tmp)
|
||||
|
||||
@@ -82,7 +82,7 @@ class stock_move(osv.osv):
|
||||
|
||||
class stock_picking(osv.osv):
|
||||
_inherit = 'stock.picking'
|
||||
|
||||
|
||||
def _get_to_invoice(self, cr, uid, ids, name, args, context=None):
|
||||
res = {}
|
||||
for picking in self.browse(cr, uid, ids, context=context):
|
||||
|
||||
+5
-1
@@ -815,6 +815,10 @@ class sale_order_line(osv.osv):
|
||||
def need_procurement(self, cr, uid, ids, context=None):
|
||||
#when sale is installed only, there is no need to create procurements, that's only
|
||||
#further installed modules (sale_service, sale_stock) that will change this.
|
||||
prod_obj = self.pool.get('product.product')
|
||||
for line in self.browse(cr, uid, ids, context=context):
|
||||
if prod_obj.need_procurement(cr, uid, [line.product_id.id], context=context):
|
||||
return True
|
||||
return False
|
||||
|
||||
def _amount_line(self, cr, uid, ids, field_name, arg, context=None):
|
||||
@@ -1251,7 +1255,7 @@ class procurement_order(osv.osv):
|
||||
from openerp import workflow
|
||||
if vals.get('state') in ['done', 'cancel', 'exception']:
|
||||
for proc in self.browse(cr, uid, ids, context=context):
|
||||
if proc.sale_line_id and proc.sale_line_id.order_id and proc.move_ids:
|
||||
if proc.sale_line_id and proc.sale_line_id.order_id:
|
||||
order_id = proc.sale_line_id.order_id.id
|
||||
if self.pool.get('sale.order').test_procurements_done(cr, uid, [order_id], context=context):
|
||||
workflow.trg_validate(uid, 'sale.order', order_id, 'ship_end', cr)
|
||||
|
||||
@@ -69,19 +69,6 @@ class mrp_production(osv.osv):
|
||||
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
|
||||
|
||||
def _hook_create_post_procurement(self, cr, uid, production, procurement_id, context=None):
|
||||
def get_parent_move(move):
|
||||
if move.move_dest_id:
|
||||
return get_parent_move(move.move_dest_id)
|
||||
return move
|
||||
|
||||
res = super(mrp_production, self)._hook_create_post_procurement(cr, uid, production, procurement_id, context)
|
||||
if production.move_prod_id:
|
||||
parent_move_line = get_parent_move(production.move_prod_id)
|
||||
if parent_move_line and parent_move_line.sale_line_id:
|
||||
self.pool.get('procurement.order').write(cr, uid, procurement_id, {'sale_line_id': parent_move_line.sale_line_id.id})
|
||||
return res
|
||||
|
||||
_columns = {
|
||||
'sale_name': fields.function(_ref_calc, multi='sale_name', type='char', string='Sale Name', help='Indicate the name of sales order.'),
|
||||
'sale_ref': fields.function(_ref_calc, multi='sale_name', type='char', string='Sale Reference', help='Indicate the Customer Reference from sales order.'),
|
||||
|
||||
@@ -27,7 +27,6 @@ class procurement_order(osv.osv):
|
||||
_inherit = "procurement.order"
|
||||
_columns = {
|
||||
'task_id': fields.many2one('project.task', 'Task', copy=False),
|
||||
'sale_line_id': fields.many2one('sale.order.line', 'Sales order line', copy=False)
|
||||
}
|
||||
|
||||
def _is_procurement_task(self, cr, uid, procurement, context=None):
|
||||
@@ -81,7 +80,7 @@ class procurement_order(osv.osv):
|
||||
'date_deadline': procurement.date_planned,
|
||||
'planned_hours': planned_hours,
|
||||
'remaining_hours': planned_hours,
|
||||
'partner_id': procurement.sale_line_id and procurement.sale_line_id.order_id.partner_id.id or False,
|
||||
'partner_id': procurement.sale_line_id and procurement.sale_line_id.order_id.partner_id.id or procurement.partner_dest_id.id,
|
||||
'user_id': procurement.product_id.product_manager.id,
|
||||
'procurement_id': procurement.id,
|
||||
'description': procurement.name + '\n',
|
||||
@@ -139,23 +138,22 @@ class project_task(osv.osv):
|
||||
self._validate_subflows(cr, uid, ids, context=context)
|
||||
return res
|
||||
|
||||
class product_product(osv.osv):
|
||||
class product_template(osv.osv):
|
||||
_inherit = "product.template"
|
||||
_columns = {
|
||||
'project_id': fields.many2one('project.project', 'Project', ondelete='set null',),
|
||||
'auto_create_task': fields.boolean('Create Task Automatically', help="Thick this option if you want to create a task automatically each time this product is sold"),
|
||||
}
|
||||
|
||||
|
||||
class sale_order_line(osv.osv):
|
||||
_inherit = 'sale.order.line'
|
||||
|
||||
class product_product(osv.osv):
|
||||
_inherit = "product.product"
|
||||
|
||||
def need_procurement(self, cr, uid, ids, context=None):
|
||||
#when sale is installed alone, there is no need to create procurements, but with sale_service
|
||||
#we must create a procurement for each service that has the auto_create_task boolean set to True.
|
||||
for line in self.browse(cr, uid, ids, context=context):
|
||||
if line.product_id and line.product_id.type == 'service' and line.product_id.auto_create_task:
|
||||
for product in self.browse(cr, uid, ids, context=context):
|
||||
if product.type == 'service' and product.auto_create_task:
|
||||
return True
|
||||
return super(sale_order_line, self).need_procurement(cr, uid, ids, context=context)
|
||||
return super(product_product, self).need_procurement(cr, uid, ids, context=context)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -202,16 +202,20 @@ class sale_order(osv.osv):
|
||||
return False
|
||||
|
||||
|
||||
class product_product(osv.osv):
|
||||
_inherit = 'product.product'
|
||||
|
||||
def need_procurement(self, cr, uid, ids, context=None):
|
||||
#when sale/product is installed alone, there is no need to create procurements, but with sale_stock
|
||||
#we must create a procurement for each product that is not a service.
|
||||
for product in self.browse(cr, uid, ids, context=context):
|
||||
if product.type != 'service':
|
||||
return True
|
||||
return super(product_product, self).need_procurement(cr, uid, ids, context=context)
|
||||
|
||||
class sale_order_line(osv.osv):
|
||||
_inherit = 'sale.order.line'
|
||||
|
||||
def need_procurement(self, cr, uid, ids, context=None):
|
||||
#when sale is installed alone, there is no need to create procurements, but with sale_stock
|
||||
#we must create a procurement for each product that is not a service.
|
||||
for line in self.browse(cr, uid, ids, context=context):
|
||||
if line.product_id and line.product_id.type != 'service':
|
||||
return True
|
||||
return super(sale_order_line, self).need_procurement(cr, uid, ids, context=context)
|
||||
|
||||
def _number_packages(self, cr, uid, ids, field_name, arg, context=None):
|
||||
res = {}
|
||||
@@ -396,6 +400,17 @@ class stock_location_route(osv.osv):
|
||||
class stock_picking(osv.osv):
|
||||
_inherit = "stock.picking"
|
||||
|
||||
def _get_partner_to_invoice(self, cr, uid, picking, context=None):
|
||||
""" Inherit the original function of the 'stock' module
|
||||
We select the partner of the sales order as the partner of the customer invoice
|
||||
"""
|
||||
saleorder_ids = self.pool['sale.order'].search(cr, uid, [('procurement_group_id' ,'=', picking.group_id.id)], context=context)
|
||||
saleorders = self.pool['sale.order'].browse(cr, uid, saleorder_ids, context=context)
|
||||
if saleorders and saleorders[0]:
|
||||
saleorder = saleorders[0]
|
||||
return saleorder.partner_invoice_id.id
|
||||
return super(stock_picking, self)._get_partner_to_invoice(cr, uid, picking, context=context)
|
||||
|
||||
def _get_sale_id(self, cr, uid, ids, name, args, context=None):
|
||||
sale_obj = self.pool.get("sale.order")
|
||||
res = {}
|
||||
|
||||
@@ -206,11 +206,13 @@ class procurement_order(osv.osv):
|
||||
return super(procurement_order, self)._run(cr, uid, procurement, context=context)
|
||||
|
||||
def run(self, cr, uid, ids, autocommit=False, context=None):
|
||||
res = super(procurement_order, self).run(cr, uid, ids, autocommit=autocommit, context=context)
|
||||
new_ids = [x.id for x in self.browse(cr, uid, ids, context=context) if x.state not in ('running', 'done', 'cancel')]
|
||||
res = super(procurement_order, self).run(cr, uid, new_ids, autocommit=autocommit, context=context)
|
||||
|
||||
#after all the procurements are run, check if some created a draft stock move that needs to be confirmed
|
||||
#(we do that in batch because it fasts the picking assignation and the picking state computation)
|
||||
move_to_confirm_ids = []
|
||||
for procurement in self.browse(cr, uid, ids, context=context):
|
||||
for procurement in self.browse(cr, uid, new_ids, context=context):
|
||||
if procurement.state == "running" and procurement.rule_id and procurement.rule_id.action == "move":
|
||||
move_to_confirm_ids += [m.id for m in procurement.move_ids if m.state == 'draft']
|
||||
if move_to_confirm_ids:
|
||||
@@ -223,6 +225,9 @@ class procurement_order(osv.osv):
|
||||
'''
|
||||
if procurement.rule_id and procurement.rule_id.action == 'move':
|
||||
uom_obj = self.pool.get('product.uom')
|
||||
# In case Phantom BoM splits only into procurements
|
||||
if not procurement.move_ids:
|
||||
return True
|
||||
cancel_test_list = [x.state == 'cancel' for x in procurement.move_ids]
|
||||
done_cancel_test_list = [x.state in ('done', 'cancel') for x in procurement.move_ids]
|
||||
at_least_one_cancel = any(cancel_test_list)
|
||||
|
||||
@@ -8,12 +8,16 @@
|
||||
|
||||
<record id="product.product_product_4" model="product.product">
|
||||
<field name="type">product</field>
|
||||
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_5" model="product.product">
|
||||
<field name="type">product</field>
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_5b" model="product.product">
|
||||
<field name="type">product</field>
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_6" model="product.product">
|
||||
<field name="type">product</field>
|
||||
</record>
|
||||
|
||||
@@ -1269,6 +1269,7 @@ class stock_picking(osv.osv):
|
||||
move_obj.action_confirm(cr, uid, moves, context=context)
|
||||
return moves
|
||||
|
||||
|
||||
def rereserve_quants(self, cr, uid, picking, move_ids=[], context=None):
|
||||
""" Unreserve quants then try to reassign quants."""
|
||||
stock_move_obj = self.pool.get('stock.move')
|
||||
@@ -2395,7 +2396,6 @@ class stock_move(osv.osv):
|
||||
defaults = {
|
||||
'product_uom_qty': uom_qty,
|
||||
'product_uos_qty': uos_qty,
|
||||
'state': move.state,
|
||||
'procure_method': 'make_to_stock',
|
||||
'restrict_lot_id': restrict_lot_id,
|
||||
'restrict_partner_id': restrict_partner_id,
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
<field name="location_id" ref="stock_location_14"/>
|
||||
</record>
|
||||
<record id="stock_inventory_line_15" model="stock.inventory.line">
|
||||
<field name="product_id" ref="product.product_product_5"/>
|
||||
<field name="product_id" ref="product.product_product_5b"/>
|
||||
<field name="product_uom_id" ref="product.product_uom_unit"/>
|
||||
<field name="inventory_id" ref="stock_inventory_0"/>
|
||||
<field name="product_qty">8.0</field>
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
<field name="inherit_id" ref="product.product_template_form_view"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="standard_price" position="replace">
|
||||
<field name="cost_method" groups="stock_account.group_inventory_valuation"/>
|
||||
<field name="cost_method" groups="stock_account.group_inventory_valuation" attrs="{'invisible': [('type','=','service')]}"/>
|
||||
<label string="Cost Price" for="standard_price" align="1.0" groups="base.group_user"/>
|
||||
<div groups="base.group_user">
|
||||
<field name="standard_price" attrs="{'readonly':['&', ('valuation','=','real_time'), ('cost_method', 'in', ['standard', 'average'])]}" nolabel="1"/>
|
||||
@@ -36,7 +36,7 @@
|
||||
</div>
|
||||
</field>
|
||||
<xpath expr="//group[@name='properties']" position="before">
|
||||
<group groups="stock_account.group_inventory_valuation">
|
||||
<group groups="stock_account.group_inventory_valuation" attrs="{'invisible': [('type','=','service')]}">
|
||||
<separator string="Inventory Valuation" colspan="4"/>
|
||||
<group colspan="2" col="2">
|
||||
<field name="valuation" attrs="{'readonly':[('type', '=', 'service')]}"/>
|
||||
|
||||
@@ -38,4 +38,10 @@ class stock_config_settings(osv.osv_memory):
|
||||
help="""Install the module that allows to affect landed costs on pickings, and split them onto the different products."""),
|
||||
}
|
||||
|
||||
|
||||
def onchange_landed_costs(self, cr, uid, ids, module_landed_costs, context=None):
|
||||
if module_landed_costs:
|
||||
return {'value': {'group_stock_inventory_valuation': True}}
|
||||
return {}
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
<label for="module_stock_invoice_directly"/>
|
||||
</div>
|
||||
<div>
|
||||
<field name="module_stock_landed_costs" class="oe_inline"/>
|
||||
<field name="module_stock_landed_costs" on_change="onchange_landed_costs(module_stock_landed_costs)" class="oe_inline"/>
|
||||
<label for="module_stock_landed_costs"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -190,6 +190,14 @@ class stock_picking(osv.osv):
|
||||
invoice_obj = self.pool.get('account.invoice')
|
||||
return invoice_obj.create(cr, uid, vals, context=context)
|
||||
|
||||
def _get_partner_to_invoice(self, cr, uid, picking, context=None):
|
||||
""" Gets the partner that will be invoiced
|
||||
Note that this function is inherited in the sale and purchase modules
|
||||
@param picking: object of the picking for which we are selecting the partner to invoice
|
||||
@return: object of the partner to invoice
|
||||
"""
|
||||
return picking.partner_id and picking.partner_id.id
|
||||
|
||||
def action_invoice_create(self, cr, uid, ids, journal_id, group=False, type='out_invoice', context=None):
|
||||
""" Creates invoice based on the invoice state selected for picking.
|
||||
@param journal_id: Id of journal
|
||||
@@ -200,7 +208,12 @@ class stock_picking(osv.osv):
|
||||
context = context or {}
|
||||
todo = {}
|
||||
for picking in self.browse(cr, uid, ids, context=context):
|
||||
key = group and picking.id or True
|
||||
partner = self._get_partner_to_invoice(cr, uid, picking, context)
|
||||
#grouping is based on the invoiced partner
|
||||
if group:
|
||||
key = partner
|
||||
else:
|
||||
key = picking.id
|
||||
for move in picking.move_lines:
|
||||
if move.invoice_state == '2binvoiced':
|
||||
if (move.state != 'cancel') and not move.scrapped:
|
||||
@@ -208,7 +221,7 @@ class stock_picking(osv.osv):
|
||||
todo[key].append(move)
|
||||
invoices = []
|
||||
for moves in todo.values():
|
||||
invoices = self._invoice_create_line(cr, uid, moves, journal_id, type, context=context)
|
||||
invoices += self._invoice_create_line(cr, uid, moves, journal_id, type, context=context)
|
||||
return invoices
|
||||
|
||||
def _get_invoice_vals(self, cr, uid, key, inv_type, journal_id, origin, context=None):
|
||||
|
||||
@@ -18,6 +18,6 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
import wizard
|
||||
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
import stock_invoice_onshipping
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
@@ -0,0 +1,46 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
from openerp.osv import fields, osv
|
||||
from openerp.tools.translate import _
|
||||
|
||||
class stock_invoice_onshipping(osv.osv_memory):
|
||||
_inherit = "stock.invoice.onshipping"
|
||||
|
||||
def _get_journal_type(self, cr, uid, context=None):
|
||||
if context is None:
|
||||
context = {}
|
||||
res_ids = context and context.get('active_ids', [])
|
||||
pick_obj = self.pool.get('stock.picking')
|
||||
pickings = pick_obj.browse(cr, uid, res_ids, context=context)
|
||||
pick = pickings and pickings[0]
|
||||
src_usage = pick.move_lines[0].location_id.usage
|
||||
dest_usage = pick.move_lines[0].location_dest_id.usage
|
||||
pick_purchase = pick.move_lines and pick.move_lines[0].purchase_line_id and pick.move_lines[0].purchase_line_id.order_id.invoice_method == 'picking'
|
||||
if pick.picking_type_id.code == 'outgoing' and src_usage == 'supplier' and dest_usage == 'customer' and pick_purchase:
|
||||
return 'purchase'
|
||||
else:
|
||||
return super(stock_invoice_onshipping, self)._get_journal_type(cr, uid, context=context)
|
||||
|
||||
|
||||
_defaults = {
|
||||
'journal_type': _get_journal_type,
|
||||
}
|
||||
@@ -60,7 +60,7 @@
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_5" model="product.product">
|
||||
<record id="product.product_product_5b" model="product.product">
|
||||
<field name="website_published" eval="True"/>
|
||||
<field name="website_size_x">2</field>
|
||||
<field name="website_style_ids" eval="[(6,0,[ref('website_sale.image_promo')])]"/>
|
||||
@@ -688,7 +688,7 @@ Weight: 1.1 ounces</field>
|
||||
<record id="product.product_product_4_product_template" model="product.template">
|
||||
<field name="public_categ_ids" eval="[(6,0,[ref('product.Computer_all_in_one')])]"/>
|
||||
</record>
|
||||
<record id="product.product_product_5_product_template" model="product.template">
|
||||
<record id="product.product_product_5b_product_template" model="product.template">
|
||||
<field name="public_categ_ids" eval="[(6,0,[ref('product.Speakers')])]"/>
|
||||
</record>
|
||||
<record id="product.product_product_6_product_template" model="product.template">
|
||||
|
||||
Reference in New Issue
Block a user