[MERGE] Forward-port of latest 7.0 bugfixes, up to rev. 9991 revid:dle@openerp.com...
authorDenis Ledoux <dle@openerp.com>
Thu, 17 Apr 2014 11:26:55 +0000 (13:26 +0200)
committerDenis Ledoux <dle@openerp.com>
Thu, 17 Apr 2014 11:26:55 +0000 (13:26 +0200)
bzr revid: dle@openerp.com-20140417112655-eeaiokbqzokpwx4i

1  2 
addons/account_anglo_saxon/invoice.py
addons/crm_helpdesk/crm_helpdesk_view.xml
addons/crm_todo/crm_todo_view.xml
addons/email_template/email_template.py
addons/stock/stock.py

@@@ -122,27 -117,23 +122,29 @@@ class account_invoice_line(osv.osv)
                          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
Simple merge
@@@ -386,79 -297,68 +386,80 @@@ class email_template(osv.osv)
                          })
          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.
  
@@@ -2072,41 -2082,46 +2072,46 @@@ class stock_move(osv.osv)
          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