diff --git a/addons/mrp/mrp.py b/addons/mrp/mrp.py
index 8a25af01e7b..d2db471421a 100644
--- a/addons/mrp/mrp.py
+++ b/addons/mrp/mrp.py
@@ -130,6 +130,7 @@ class mrp_bom(osv.osv):
"""
_name = 'mrp.bom'
_description = 'Bill of Material'
+ _inherit = ['mail.thread']
def _child_compute(self, cr, uid, ids, name, arg, context=None):
""" Gets child bom.
@@ -312,7 +313,7 @@ class mrp_bom(osv.osv):
phantom = False
if bom.type == 'phantom' and not bom.bom_lines:
newbom = self._bom_find(cr, uid, bom.product_id.id, bom.product_uom.id, properties)
-
+
if newbom:
res = self._bom_explode(cr, uid, self.browse(cr, uid, [newbom])[0], factor*bom.product_qty, properties, addthis=True, level=level+10)
result = result + res[0]
@@ -358,6 +359,19 @@ class mrp_bom(osv.osv):
default.update({'name': bom_data['name'] + ' ' + _('Copy'), 'bom_id':False})
return super(mrp_bom, self).copy_data(cr, uid, id, default, context=context)
+ def create(self, cr, uid, vals, context=None):
+ obj_id = super(mrp_bom, self).create(cr, uid, vals, context=context)
+ self.create_notification(cr, uid, [obj_id], context=context)
+ return obj_id
+
+ def create_notification(self, cr, uid, ids, context=None):
+ prod_obj = self.pool.get('product.product')
+ for obj in self.browse(cr, uid, ids, context=context):
+ for prod in prod_obj.browse(cr, uid, [obj.product_id], context=context):
+ self.message_append_note(cr, uid, ids, _('System notification'),
+ _("Bill of Material is Created for %s product.") % (prod.id.name_template), type='notification', context=context)
+ return True
+
mrp_bom()
class mrp_bom_revision(osv.osv):
@@ -393,6 +407,7 @@ class mrp_production(osv.osv):
_name = 'mrp.production'
_description = 'Manufacturing Order'
_date_name = 'date_planned'
+ _inherit = ['mail.thread']
def _production_calc(self, cr, uid, ids, prop, unknow_none, context=None):
""" Calculates total hours and total no. of cycles for a production order.
@@ -497,12 +512,54 @@ class mrp_production(osv.osv):
(_check_qty, 'Order quantity cannot be negative or zero!', ['product_qty']),
]
+ def create(self, cr, uid, vals, context=None):
+ obj_id = super(mrp_production, self).create(cr, uid, vals, context=context)
+ self.create_notification(cr, uid, [obj_id], context=context)
+ return obj_id
+
def unlink(self, cr, uid, ids, context=None):
for production in self.browse(cr, uid, ids, context=context):
if production.state not in ('draft', 'cancel'):
raise osv.except_osv(_('Invalid action !'), _('Cannot delete a manufacturing order in state \'%s\'') % production.state)
return super(mrp_production, self).unlink(cr, uid, ids, context=context)
+ def create_notification(self, cr, uid, ids, context=None):
+ for obj in self.browse(cr, uid, ids, context=context):
+ if obj.user_id.id :
+ self.message_subscribe(cr, uid, ids, [obj.user_id.id], context=context)
+ self.message_append_note(cr, uid, ids, _('System notification'),
+ _("Manufacturing Order is Created."), type='notification', need_action_user_id=obj.user_id.id, context=context)
+ else :
+ self.message_append_note(cr, uid, ids, _('System notification'),
+ _("Manufacturing Order is Created."), type='notification', context=context)
+ return True
+
+ def cancel_notification(self, cr, uid, ids, context=None):
+ message = _("Manufacturing order is cancelled.")
+ self.message_append_note(cr, uid, ids, '', message, context=context)
+ return True
+
+ def ready_notification(self, cr, uid, ids, context=None):
+ message = _("Manufacturing order is ready to produce.")
+ self.message_append_note(cr, uid, ids, '', message, context=context)
+ return True
+
+ def inproduction_notification(self, cr, uid, ids, context=None):
+ message = _("Manufacturing order is in production.")
+ self.message_append_note(cr, uid, ids, '', message, context=context)
+ return True
+
+ def done_notification(self, cr, uid, ids, context=None):
+ self.message_mark_done(cr, uid, ids, context)
+ message = _("Manufacturing order is done.")
+ self.message_append_note(cr, uid, ids, '', message, context=context)
+ return True
+
+ def waiting_notification(self, cr, uid, ids, context=None):
+ message = _("Manufacturing order is waiting for goods.")
+ self.message_append_note(cr, uid, ids, '', message, context=context)
+ return True
+
def copy(self, cr, uid, id, default=None, context=None):
if default is None:
default = {}
@@ -629,9 +686,10 @@ class mrp_production(osv.osv):
move_obj.action_cancel(cr, uid, [x.id for x in production.move_created_ids])
move_obj.action_cancel(cr, uid, [x.id for x in production.move_lines])
self.write(cr, uid, ids, {'state': 'cancel'})
+ self.cancel_notification(cr, uid, ids, context)
return True
- def action_ready(self, cr, uid, ids):
+ def action_ready(self, cr, uid, ids, context=None):
""" Changes the production state to Ready and location id of stock move.
@return: True
"""
@@ -643,18 +701,18 @@ class mrp_production(osv.osv):
if production.move_prod_id:
move_obj.write(cr, uid, [production.move_prod_id.id],
{'location_id': production.location_dest_id.id})
-
- message = _("Manufacturing order '%s' is ready to produce.") % ( name,)
- self.log(cr, uid, production_id, message)
+ self.ready_notification(cr, uid, [production_id], context)
return True
- def action_production_end(self, cr, uid, ids):
+ def action_production_end(self, cr, uid, ids, context=None):
""" Changes production state to Finish and writes finished date.
@return: True
"""
for production in self.browse(cr, uid, ids):
self._costs_generate(cr, uid, production)
- return self.write(cr, uid, ids, {'state': 'done', 'date_finished': time.strftime('%Y-%m-%d %H:%M:%S')})
+ self.write(cr, uid, ids, {'state': 'done', 'date_finished': time.strftime('%Y-%m-%d %H:%M:%S')})
+ self.done_notification(cr, uid, ids, context)
+ return True
def test_production_done(self, cr, uid, ids):
""" Tests whether production is done or not.
@@ -670,9 +728,9 @@ class mrp_production(osv.osv):
return res
def _get_subproduct_factor(self, cr, uid, production_id, move_id=None, context=None):
- """ Compute the factor to compute the qty of procucts to produce for the given production_id. By default,
- it's always equal to the quantity encoded in the production order or the production wizard, but if the
- module mrp_subproduct is installed, then we must use the move_id to identify the product to produce
+ """ Compute the factor to compute the qty of procucts to produce for the given production_id. By default,
+ it's always equal to the quantity encoded in the production order or the production wizard, but if the
+ module mrp_subproduct is installed, then we must use the move_id to identify the product to produce
and its quantity.
:param production_id: ID of the mrp.order
:param move_id: ID of the stock move that needs to be produced. Will be used in mrp_subproduct.
@@ -698,7 +756,6 @@ class mrp_production(osv.osv):
if (produced_product.scrapped) or (produced_product.product_id.id <> production.product_id.id):
continue
produced_qty += produced_product.product_qty
-
if production_mode in ['consume','consume_produce']:
consumed_data = {}
@@ -733,7 +790,7 @@ class mrp_production(osv.osv):
prod_name = scheduled.product_id.name_get()[0][1]
raise osv.except_osv(_('Warning!'), _('You are going to consume total %s quantities of "%s".\nBut you can only consume up to total %s quantities.') % (qty, prod_name, qty_avail))
if qty <= 0.0:
- # we already have more qtys consumed than we need
+ # we already have more qtys consumed than we need
continue
raw_product[0].action_consume(qty, raw_product[0].location_id.id, context=context)
@@ -819,11 +876,12 @@ class mrp_production(osv.osv):
} )
return amount
- def action_in_production(self, cr, uid, ids):
+ def action_in_production(self, cr, uid, ids, context=None):
""" Changes state to In Production and writes starting date.
@return: True
"""
self.write(cr, uid, ids, {'state': 'in_production', 'date_start': time.strftime('%Y-%m-%d %H:%M:%S')})
+ self.inproduction_notification(cr, uid, ids, context)
return True
def test_if_product(self, cr, uid, ids):
@@ -890,14 +948,14 @@ class mrp_production(osv.osv):
'state': 'waiting',
'company_id': production.company_id.id,
})
-
+
def _make_production_internal_shipment(self, cr, uid, production, context=None):
ir_sequence = self.pool.get('ir.sequence')
stock_picking = self.pool.get('stock.picking')
routing_loc = None
pick_type = 'internal'
address_id = False
-
+
# Take routing address as a Shipment Address.
# If usage of routing location is a internal, make outgoing shipment otherwise internal shipment
if production.bom_id.routing_id and production.bom_id.routing_id.location_id:
@@ -926,7 +984,7 @@ class mrp_production(osv.osv):
stock_move = self.pool.get('stock.move')
source_location_id = production.product_id.product_tmpl_id.property_stock_production.id
destination_location_id = production.location_dest_id.id
- move_name = _('PROD: %s') + production.name
+ move_name = _('PROD: %s') + production.name
data = {
'name': move_name,
'date': production.date_planned,
@@ -983,7 +1041,7 @@ class mrp_production(osv.osv):
for production in self.browse(cr, uid, ids, context=context):
shipment_id = self._make_production_internal_shipment(cr, uid, production, context=context)
produce_move_id = self._make_production_produce_line(cr, uid, production, context=context)
-
+
# Take routing location as a Source Location.
source_location_id = production.location_src_id.id
if production.bom_id.routing_id and production.bom_id.routing_id.location_id:
@@ -994,14 +1052,14 @@ class mrp_production(osv.osv):
shipment_move_id = self._make_production_internal_shipment_line(cr, uid, line, shipment_id, consume_move_id,\
destination_location_id=source_location_id, context=context)
self._make_production_line_procurement(cr, uid, line, shipment_move_id, context=context)
-
+
wf_service.trg_validate(uid, 'stock.picking', shipment_id, 'button_confirm', cr)
production.write({'state':'confirmed'}, context=context)
message = _("Manufacturing order '%s' is scheduled for the %s.") % (
production.name,
datetime.strptime(production.date_planned,'%Y-%m-%d %H:%M:%S').strftime('%m/%d/%Y'),
)
- self.log(cr, uid, production.id, message)
+ self.waiting_notification(cr, uid, [production.id], context);
return shipment_id
def force_production(self, cr, uid, ids, *args):
@@ -1019,6 +1077,7 @@ class mrp_production_workcenter_line(osv.osv):
_name = 'mrp.production.workcenter.line'
_description = 'Work Order'
_order = 'sequence'
+ _inherit = ['mail.thread']
_columns = {
'name': fields.char('Work Order', size=64, required=True),
diff --git a/addons/mrp/mrp_view.xml b/addons/mrp/mrp_view.xml
index 9c3fd6eb335..ad90b7dadae 100644
--- a/addons/mrp/mrp_view.xml
+++ b/addons/mrp/mrp_view.xml
@@ -410,6 +410,7 @@
+
@@ -644,6 +645,7 @@
+
@@ -773,16 +775,14 @@
-
-
-
+
diff --git a/addons/mrp_operations/mrp_operations.py b/addons/mrp_operations/mrp_operations.py
index fbf307e860e..7df77c8d4b3 100644
--- a/addons/mrp_operations/mrp_operations.py
+++ b/addons/mrp_operations/mrp_operations.py
@@ -156,11 +156,62 @@ class mrp_production_workcenter_line(osv.osv):
prod_obj.write(cr, uid, [prod.production_id.id], {'date_start':dstart}, context=context, mini=False)
return result
+ def draft_notification(self, cr, uid, ids):
+ prod_obj = self.pool.get('mrp.production')
+ for workorder in self.browse(cr, uid, ids):
+ for prod in prod_obj.browse(cr, uid, [workorder.production_id]):
+ message = _("%s Work Order is created.") % (workorder.name)
+ prod_obj.message_append_note(cr, uid, [prod.id], '', message)
+ womessage = _("Work Order is created for %s production order.") % (prod.id.name)
+ self.message_append_note(cr, uid, ids, '', womessage)
+ return True
+
+ def start_notification(self, cr, uid, ids):
+ prod_obj = self.pool.get('mrp.production')
+ for workorder in self.browse(cr, uid, ids):
+ for prod in prod_obj.browse(cr, uid, [workorder.production_id]):
+ message = _("%s Work Order is started.") % (workorder.name)
+ prod_obj.message_append_note(cr, uid, [prod.id], '', message)
+ womessage = _("Work Order is started for %s production order.") % (prod.id.name)
+ self.message_append_note(cr, uid, ids, '', womessage)
+ return True
+
+ def done_notification(self, cr, uid, ids):
+ prod_obj = self.pool.get('mrp.production')
+ for workorder in self.browse(cr, uid, ids):
+ for prod in prod_obj.browse(cr, uid, [workorder.production_id]):
+ message = _("%sWork Order is finished.") % (workorder.name)
+ prod_obj.message_append_note(cr, uid, [prod.id], '', message)
+ womessage = _("Work Order is done for %s production order.") % (prod.id.name)
+ self.message_append_note(cr, uid, ids, '', womessage)
+ return True
+
+ def pending_notification(self, cr, uid, ids):
+ prod_obj = self.pool.get('mrp.production')
+ for workorder in self.browse(cr, uid, ids):
+ for prod in prod_obj.browse(cr, uid, [workorder.production_id]):
+ message = _("%sWork Order is pending.") % (workorder.name)
+ prod_obj.message_append_note(cr, uid, [prod.id], '', message)
+ womessage = _("Work Order is pending for %s production order.") % (prod.id.name)
+ self.message_append_note(cr, uid, ids, '', womessage)
+ return True
+
+ def cancel_notification(self, cr, uid, ids):
+ prod_obj = self.pool.get('mrp.production')
+ for workorder in self.browse(cr, uid, ids):
+ for prod in prod_obj.browse(cr, uid, [workorder.production_id]):
+ message = _("%sWork Order is cancelled.") % (workorder.name)
+ prod_obj.message_append_note(cr, uid, [prod.id], '', message)
+ womessage = _("Work Order is cancelled for %s production order.") % (prod.id.name)
+ self.message_append_note(cr, uid, ids, '', womessage)
+ return True
+
def action_draft(self, cr, uid, ids):
""" Sets state to draft.
@return: True
"""
self.write(cr, uid, ids, {'state':'draft'})
+ self.draft_notification(cr, uid, ids)
return True
def action_start_working(self, cr, uid, ids):
@@ -169,6 +220,7 @@ class mrp_production_workcenter_line(osv.osv):
"""
self.modify_production_order_state(cr, uid, ids, 'start')
self.write(cr, uid, ids, {'state':'startworking', 'date_start': time.strftime('%Y-%m-%d %H:%M:%S')})
+ self.start_notification(cr, uid, ids)
return True
def action_done(self, cr, uid, ids):
@@ -185,6 +237,7 @@ class mrp_production_workcenter_line(osv.osv):
delay += (date_finished-date_start).seconds / float(60*60)
self.write(cr, uid, ids, {'state':'done', 'date_finished': date_now,'delay':delay})
+ self.done_notification(cr, uid, ids)
self.modify_production_order_state(cr,uid,ids,'done')
return True
@@ -193,6 +246,7 @@ class mrp_production_workcenter_line(osv.osv):
@return: True
"""
self.write(cr, uid, ids, {'state':'cancel'})
+ self.cancel_notification(cr, uid, ids)
return True
def action_pause(self, cr, uid, ids):
@@ -200,6 +254,7 @@ class mrp_production_workcenter_line(osv.osv):
@return: True
"""
self.write(cr, uid, ids, {'state':'pause'})
+ self.pending_notification(cr, uid, ids)
return True
def action_resume(self, cr, uid, ids):
@@ -207,6 +262,7 @@ class mrp_production_workcenter_line(osv.osv):
@return: True
"""
self.write(cr, uid, ids, {'state':'startworking'})
+ self.start_notification(cr, uid, ids)
return True
mrp_production_workcenter_line()
@@ -251,7 +307,7 @@ class mrp_production(osv.osv):
if prod.workcenter_lines:
wf_service.trg_validate(uid, 'mrp.production.workcenter.line', prod.workcenter_lines[0].id, 'button_start_working', cr)
return super(mrp_production,self).action_in_production(cr, uid, ids)
-
+
def action_cancel(self, cr, uid, ids, context=None):
""" Cancels work order if production order is canceled.
@return: Super method
diff --git a/addons/mrp_operations/mrp_operations_view.xml b/addons/mrp_operations/mrp_operations_view.xml
index c40b9611fb5..3e1d17d18e9 100644
--- a/addons/mrp_operations/mrp_operations_view.xml
+++ b/addons/mrp_operations/mrp_operations_view.xml
@@ -118,6 +118,7 @@
+
diff --git a/addons/product/product.py b/addons/product/product.py
index 0d5fa31ac11..00c3ce3b619 100644
--- a/addons/product/product.py
+++ b/addons/product/product.py
@@ -167,7 +167,7 @@ class product_uom(osv.osv):
if value == 'reference':
return {'value': {'factor': 1, 'factor_inv': 1}}
return {}
-
+
def write(self, cr, uid, ids, vals, context=None):
if 'category_id' in vals:
for uom in self.browse(cr, uid, ids, context=context):
@@ -231,7 +231,7 @@ class product_category(osv.osv):
_parent_store = True
_parent_order = 'sequence, name'
_order = 'parent_left'
-
+
def _check_recursion(self, cr, uid, ids, context=None):
level = 100
while len(ids):
@@ -259,7 +259,7 @@ class product_template(osv.osv):
_description = "Product Template"
def _get_main_product_supplier(self, cr, uid, product, context=None):
- """Determines the main (best) product supplier for ``product``,
+ """Determines the main (best) product supplier for ``product``,
returning the corresponding ``supplierinfo`` record, or False
if none were found. The default strategy is to select the
supplier with the highest priority (i.e. smallest sequence).
@@ -503,6 +503,7 @@ class product_product(osv.osv):
_description = "Product"
_table = "product_product"
_inherits = {'product.template': 'product_tmpl_id'}
+ _inherit = ['mail.thread']
_order = 'default_code,name_template'
_columns = {
'qty_available': fields.function(_product_qty_available, type='float', string='Quantity On Hand'),
@@ -526,7 +527,18 @@ class product_product(osv.osv):
'color': fields.integer('Color Index'),
'product_image': fields.binary('Image'),
}
-
+
+ def create(self, cr, uid, vals, context=None):
+ obj_id = super(product_product, self).create(cr, uid, vals, context=context)
+ self.create_notification(cr, uid, [obj_id], context=context)
+ return obj_id
+
+ def create_notification(self, cr, uid, ids, context=None):
+ for obj in self.browse(cr, uid, ids, context=context):
+ self.message_append_note(cr, uid, ids, _('System notification'),
+ _("Product is Created."), type='notification', context=context)
+ return True
+
def unlink(self, cr, uid, ids, context=None):
unlink_ids = []
unlink_product_tmpl_ids = []
diff --git a/addons/product/product_view.xml b/addons/product/product_view.xml
index a1857748b9d..4d0dea47f2b 100644
--- a/addons/product/product_view.xml
+++ b/addons/product/product_view.xml
@@ -87,7 +87,7 @@
-
+
@@ -192,6 +192,7 @@
+
@@ -257,24 +258,24 @@
tree,form,kanban
form
{"search_default_filter_to_sell":1}
-
-
+
+
You must define a Product for everything you buy or sell. Products can be raw materials, stockable products, consumables or services. The Product form contains detailed information about your products related to procurement logistics, sales price, product category, suppliers and so on.
-
+
tree
-
+
form
-
+
diff --git a/addons/stock/wizard/stock_change_product_qty.py b/addons/stock/wizard/stock_change_product_qty.py
index 9d3de5692f0..fcfb9515354 100644
--- a/addons/stock/wizard/stock_change_product_qty.py
+++ b/addons/stock/wizard/stock_change_product_qty.py
@@ -26,6 +26,7 @@ import tools
class stock_change_product_qty(osv.osv_memory):
_name = "stock.change.product.qty"
+ _inherit = ['mail.thread']
_description = "Change Product Quantity"
_columns = {
'product_id' : fields.many2one('product.product', 'Product'),
@@ -98,9 +99,19 @@ class stock_change_product_qty(osv.osv_memory):
inventry_obj.action_confirm(cr, uid, [inventory_id], context=context)
inventry_obj.action_done(cr, uid, [inventory_id], context=context)
-
+ self.quantity_change_notification(cr, uid, [data.id], context)
return {}
+ def quantity_change_notification (self, cr, uid, ids, context=None):
+ prod_obj = self.pool.get('product.product')
+ location_obj = self.pool.get('stock.location')
+ for data in self.browse(cr, uid, ids, context=context):
+ for location in location_obj.browse(cr, uid, [data.location_id.id], context=context):
+ location_name = location.name
+ for prod in prod_obj.browse(cr, uid, [data.product_id.id], context=context):
+ message = _("Quantity is changed to %s for %s location.") % (data.new_quantity,location_name)
+ prod_obj.message_append_note(cr, uid, [prod.id], '', message)
+
stock_change_product_qty()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: