for line in res:
if a == line['account_id'] and i_line.product_id.id == line['product_id']:
uom = i_line.product_id.uos_id or i_line.product_id.uom_id
- standard_price = self.pool.get('product.uom')._compute_price(cr, uid, uom.id, i_line.product_id.standard_price, i_line.uos_id.id)
+ valuation_price_unit = self.pool.get('product.uom')._compute_price(cr, uid, uom.id, i_line.product_id.standard_price, i_line.uos_id.id)
+ if inv.currency_id.id != company_currency:
+ standard_price = self.pool.get('res.currency').compute(cr, uid, company_currency, inv.currency_id.id, standard_price, context={'date': inv.date_invoice})
- if standard_price != i_line.price_unit and line['price_unit'] == i_line.price_unit and acc:
- price_diff = i_line.price_unit - standard_price
- line.update({'price':standard_price * line['quantity']})
+ if i_line.product_id.cost_method != 'standard' and i_line.purchase_line_id:
+ #for average/fifo/lifo costing method, fetch real cost price from incomming moves
+ stock_move_obj = self.pool.get('stock.move')
+ valuation_stock_move = stock_move_obj.search(cr, uid, [('purchase_line_id', '=', i_line.purchase_line_id.id)], limit=1, context=context)
+ if valuation_stock_move:
+ valuation_price_unit = stock_move_obj.browse(cr, uid, valuation_stock_move[0], context=context).price_unit
+ if valuation_price_unit != i_line.price_unit and line['price_unit'] == i_line.price_unit and acc:
+ price_diff = i_line.price_unit - valuation_price_unit
+ line.update({'price': valuation_price_unit * line['quantity']})
diff_res.append({
- 'type':'src',
+ 'type': 'src',
'name': i_line.name[:64],
- 'price_unit':price_diff,
- 'quantity':line['quantity'],
+ 'price_unit': price_diff,
+ 'quantity': line['quantity'],
'price': price_diff * line['quantity'],
- 'account_id':acc,
- 'product_id':line['product_id'],
- 'uos_id':line['uos_id'],
- 'account_analytic_id':line['account_analytic_id'],
- 'taxes':line.get('taxes',[]),
+ 'account_id': acc,
+ 'product_id': line['product_id'],
+ 'uos_id': line['uos_id'],
+ 'account_analytic_id': line['account_analytic_id'],
+ 'taxes': line.get('taxes', []),
})
res += diff_res
return res
})
return {'value': result}
- def generate_email(self, cr, uid, template_id, res_id, context=None):
- """Generates an email from the template for given (model, res_id) pair.
-
- :param template_id: id of the template to render.
- :param res_id: id of the record to use for rendering the template (model
- is taken from template definition)
- :returns: a dict containing all relevant fields for creating a new
- mail.mail entry, with one extra key ``attachments``, in the
- format [(report_name, data)] where data is base64 encoded.
+ def generate_email_batch(self, cr, uid, template_id, res_ids, context=None, fields=None):
+ """Generates an email from the template for given the given model based on
+ records given by res_ids.
+
+ :param template_id: id of the template to render.
+ :param res_id: id of the record to use for rendering the template (model
+ is taken from template definition)
+ :returns: a dict containing all relevant fields for creating a new
+ mail.mail entry, with one extra key ``attachments``, in the
- format expected by :py:meth:`mail_thread.message_post`.
++ format [(report_name, data)] where data is base64 encoded.
"""
if context is None:
context = {}
+ if fields is None:
+ fields = ['subject', 'body_html', 'email_from', 'email_to', 'partner_to', 'email_cc', 'reply_to']
+
report_xml_pool = self.pool.get('ir.actions.report.xml')
- template = self.get_email_template(cr, uid, template_id, res_id, context)
- values = {}
- for field in ['subject', 'body_html', 'email_from',
- 'email_to', 'email_recipients', 'email_cc', 'reply_to']:
- values[field] = self.render_template(cr, uid, getattr(template, field),
- template.model, res_id, context=context) \
- or False
- if template.user_signature:
- signature = self.pool.get('res.users').browse(cr, uid, uid, context).signature
- values['body_html'] = tools.append_content_to_html(values['body_html'], signature)
-
- if values['body_html']:
- values['body'] = tools.html_sanitize(values['body_html'])
-
- values.update(mail_server_id=template.mail_server_id.id or False,
- auto_delete=template.auto_delete,
- model=template.model,
- res_id=res_id or False)
-
- attachments = []
- # Add report in attachments
- if template.report_template:
- report_name = self.render_template(cr, uid, template.report_name, template.model, res_id, context=context)
- report_service = 'report.' + report_xml_pool.browse(cr, uid, template.report_template.id, context).report_name
- # Ensure report is rendered using template's language
- ctx = context.copy()
- if template.lang:
- ctx['lang'] = self.render_template(cr, uid, template.lang, template.model, res_id, context)
- service = netsvc.LocalService(report_service)
- (result, format) = service.create(cr, uid, [res_id], {'model': template.model}, ctx)
+ res_ids_to_templates = self.get_email_template_batch(cr, uid, template_id, res_ids, context)
+
+ # templates: res_id -> template; template -> res_ids
+ templates_to_res_ids = {}
+ for res_id, template in res_ids_to_templates.iteritems():
+ templates_to_res_ids.setdefault(template, []).append(res_id)
+
+ results = dict()
+ for template, template_res_ids in templates_to_res_ids.iteritems():
+ # generate fields value for all res_ids linked to the current template
+ for field in fields:
+ generated_field_values = self.render_template_batch(
+ cr, uid, getattr(template, field), template.model, template_res_ids,
+ post_process=(field == 'body_html'),
+ context=context)
+ for res_id, field_value in generated_field_values.iteritems():
+ results.setdefault(res_id, dict())[field] = field_value
+ # update values for all res_ids
+ for res_id in template_res_ids:
+ values = results[res_id]
+ if 'body_html' in fields and template.user_signature:
+ signature = self.pool.get('res.users').browse(cr, uid, uid, context).signature
+ values['body_html'] = tools.append_content_to_html(values['body_html'], signature)
+ if values.get('body_html'):
+ values['body'] = tools.html_sanitize(values['body_html'])
+ values.update(
+ mail_server_id=template.mail_server_id.id or False,
+ auto_delete=template.auto_delete,
+ model=template.model,
+ res_id=res_id or False,
+ attachment_ids=[attach.id for attach in template.attachment_ids],
+ )
+
+ # Add report in attachments: generate once for all template_res_ids
+ if template.report_template:
+ for res_id in template_res_ids:
+ attachments = []
+ report_name = self.render_template(cr, uid, template.report_name, template.model, res_id, context=context)
+ report_service = report_xml_pool.browse(cr, uid, template.report_template.id, context).report_name
+ # Ensure report is rendered using template's language
+ ctx = context.copy()
+ if template.lang:
+ ctx['lang'] = self.render_template_batch(cr, uid, template.lang, template.model, [res_id], context)[res_id] # take 0 ?
+ result, format = openerp.report.render_report(cr, uid, [res_id], report_service, {'model': template.model}, ctx)
+ # TODO in trunk, change return format to binary to match message_post expected format
- result = base64.b64encode(result)
- if not report_name:
- report_name = report_service
- ext = "." + format
- if not report_name.endswith(ext):
- report_name += ext
- attachments.append((report_name, result))
-
- attachment_ids = []
- # Add template attachments
- for attach in template.attachment_ids:
- attachment_ids.append(attach.id)
-
- values['attachments'] = attachments
- values['attachment_ids'] = attachment_ids
- return values
-
- def send_mail(self, cr, uid, template_id, res_id, force_send=False, context=None):
+ result = base64.b64encode(result)
+ if not report_name:
+ report_name = 'report.' + report_service
+ ext = "." + format
+ if not report_name.endswith(ext):
+ report_name += ext
+ attachments.append((report_name, result))
+ results[res_id]['attachments'] = attachments
+
+ return results
+
+ def send_mail(self, cr, uid, template_id, res_id, force_send=False, raise_exception=False, context=None):
"""Generates a new mail message for the given template and record,
and schedules it for delivery through the ``mail`` module's scheduler.
if context is None:
context = {}
seq_obj = self.pool.get('ir.sequence')
- for picking, todo in self._chain_compute(cr, uid, moves, context=context).items():
- ptype = todo[0][1][5] and todo[0][1][5] or location_obj.picking_type_get(cr, uid, todo[0][0].location_dest_id, todo[0][1][0])
- if picking:
- # name of new picking according to its type
- if ptype == 'internal':
- new_pick_name = seq_obj.get(cr, uid,'stock.picking')
- else :
- new_pick_name = seq_obj.get(cr, uid, 'stock.picking.' + ptype)
- pickid = self._create_chained_picking(cr, uid, new_pick_name, picking, ptype, todo, context=context)
- # Need to check name of old picking because it always considers picking as "OUT" when created from Sales Order
- old_ptype = location_obj.picking_type_get(cr, uid, picking.move_lines[0].location_id, picking.move_lines[0].location_dest_id)
- if old_ptype != picking.type:
- old_pick_name = seq_obj.get(cr, uid, 'stock.picking.' + old_ptype)
- self.pool.get('stock.picking').write(cr, uid, [picking.id], {'name': old_pick_name, 'type': old_ptype}, context=context)
- else:
- pickid = False
- for move, (loc, dummy, delay, dummy, company_id, ptype, invoice_state) in todo:
- new_id = move_obj.copy(cr, uid, move.id, {
- 'location_id': move.location_dest_id.id,
- 'location_dest_id': loc.id,
- 'date': time.strftime('%Y-%m-%d'),
- 'picking_id': pickid,
- 'state': 'waiting',
- 'company_id': company_id or res_obj._company_default_get(cr, uid, 'stock.company', context=context) ,
- 'move_history_ids': [],
- 'date_expected': (datetime.strptime(move.date, '%Y-%m-%d %H:%M:%S') + relativedelta(days=delay or 0)).strftime('%Y-%m-%d'),
- 'move_history_ids2': []}
- )
- move_obj.write(cr, uid, [move.id], {
- 'move_dest_id': new_id,
- 'move_history_ids': [(4, new_id)]
- })
- new_moves.append(self.browse(cr, uid, [new_id])[0])
- if pickid:
- self.pool.get('stock.picking').signal_button_confirm(cr, uid, [pickid])
+ for picking, chained_moves in self._chain_compute(cr, uid, moves, context=context).items():
+ # We group the moves by automatic move type, so it creates different pickings for different types
+ moves_by_type = {}
+ for move in chained_moves:
+ moves_by_type.setdefault(move[1][1], []).append(move)
+ for todo in moves_by_type.values():
+ ptype = todo[0][1][5] and todo[0][1][5] or location_obj.picking_type_get(cr, uid, todo[0][0].location_dest_id, todo[0][1][0])
+ if picking:
+ # name of new picking according to its type
+ if ptype == 'internal':
+ new_pick_name = seq_obj.get(cr, uid,'stock.picking')
+ else :
+ new_pick_name = seq_obj.get(cr, uid, 'stock.picking.' + ptype)
+ pickid = self._create_chained_picking(cr, uid, new_pick_name, picking, ptype, todo, context=context)
+ # Need to check name of old picking because it always considers picking as "OUT" when created from Sales Order
+ old_ptype = location_obj.picking_type_get(cr, uid, picking.move_lines[0].location_id, picking.move_lines[0].location_dest_id)
+ if old_ptype != picking.type:
+ old_pick_name = seq_obj.get(cr, uid, 'stock.picking.' + old_ptype)
+ self.pool.get('stock.picking').write(cr, uid, [picking.id], {'name': old_pick_name, 'type': old_ptype}, context=context)
+ else:
+ pickid = False
+ for move, (loc, dummy, delay, dummy, company_id, ptype, invoice_state) in todo:
+ new_id = move_obj.copy(cr, uid, move.id, {
+ 'location_id': move.location_dest_id.id,
+ 'location_dest_id': loc.id,
+ 'date': time.strftime('%Y-%m-%d'),
+ 'picking_id': pickid,
+ 'state': 'waiting',
+ 'company_id': company_id or res_obj._company_default_get(cr, uid, 'stock.company', context=context) ,
+ 'move_history_ids': [],
+ 'date_expected': (datetime.strptime(move.date, '%Y-%m-%d %H:%M:%S') + relativedelta(days=delay or 0)).strftime('%Y-%m-%d'),
+ 'move_history_ids2': []}
+ )
+ move_obj.write(cr, uid, [move.id], {
+ 'move_dest_id': new_id,
+ 'move_history_ids': [(4, new_id)]
+ })
+ new_moves.append(self.browse(cr, uid, [new_id])[0])
+ if pickid:
- wf_service.trg_validate(uid, 'stock.picking', pickid, 'button_confirm', cr)
++ self.pool.get('stock.picking').signal_button_confirm(cr, uid, [pickid])
if new_moves:
new_moves += self.create_chained_picking(cr, uid, new_moves, context)
return new_moves