def format_amount(self, amount, currency=None): self.ensure_one() currency = currency or self.get_currency() return format_amount(self.env, amount, currency)
def render_template(self, template_txt, model, res_ids, post_process=False): """ Render the given template text, replace mako expressions ``${expr}`` with the result of evaluating these expressions with an evaluation context containing: - ``user``: browse_record of the current user - ``object``: record of the document record this mail is related to - ``context``: the context passed to the mail composition wizard :param str template_txt: the template text to render :param str model: model name of the document record this mail is related to. :param int res_ids: list of ids of document records those mails are related to. """ multi_mode = True if isinstance(res_ids, (int, long)): multi_mode = False res_ids = [res_ids] results = dict.fromkeys(res_ids, u"") # try to load the template try: mako_env = mako_safe_template_env if self.env.context.get('safe') \ else mako_template_env template = mako_env.from_string(tools.ustr(template_txt)) except Exception: _logger.info("Failed to load template %r", template_txt, exc_info=True) return multi_mode and results or results[res_ids[0]] # prepare template variables # filter to avoid browsing [None] records = self.env[model].browse(list(filter(None, res_ids))) res_to_rec = dict.fromkeys(res_ids, None) for record in records: res_to_rec[record.id] = record variables = { 'format_date': lambda date, format=False, context=self._context: format_date( self.env, date, format), 'format_tz': lambda dt, tz=False, format=False, context=self._context: format_tz(self.env, dt, tz, format), 'format_amount': lambda amount, currency, context=self._context: format_amount( self.env, amount, currency), 'format_numeric': lambda value, column, options=None: self.format_numeric( value, column, options), # Added by Smile 'user': self.env.user, 'ctx': self._context, # context kw would clash with mako internals } for res_id, record in res_to_rec.items(): variables['object'] = record try: render_result = template.render(variables) except Exception: _logger.info("Failed to render template %r using values %r" % (template, variables), exc_info=True) raise UserError( _("Failed to render template %r using values %r") % (template, variables)) if render_result == u"False": render_result = u"" results[res_id] = render_result if post_process: for res_id, result in results.items(): results[res_id] = self.render_post_process(result) return multi_mode and results or results[res_ids[0]]
def _get_dict_values(self, o, objects): if not o: return {} lang_obj = self.env['res.lang'] order = objects['order'] invoice = objects['invoice'] partner = objects['partner'] address = objects['address'] address_pose = objects['address_pose'] cal_event = objects['cal_event'] lang_code = self._context.get('lang', partner.lang) lang = lang_obj.search([('code', '=', lang_code or 'fr_FR')]) values = ( ('amount_total', o, ''), ('origin', o, ''), ('date_order', order or o, ''), ('confirmation_date', order or o, ''), ('date_invoice', invoice or o, ''), ('residual', invoice or o, ''), ('number', invoice, ''), ('user_id', o, ''), ('objet', o, ''), ) res = { key: getattr(obj, key, default) or default for key, obj, default in values } res['date_confirm_order'] = res['confirmation_date'] del res['confirmation_date'] res['user_name'] = res['user_id'] and res['user_id'].name del res['user_id'] if cal_event: date_event = fields.Datetime.from_string(cal_event.start) date_event = fields.Datetime.context_timestamp(self, date_event) date_format = lang.date_format.encode('utf-8') res.update({ 'c_title': address.title.name or 'Madame, Monsieur', 'c_name': address.name or (address.parent_id and address.parent_id.name) or '', 'c_note': partner.comment, 'c_street': address.street or '', 'c_street2': address.street2 or '', 'c_zip': address.zip or '', 'c_city': address.city or '', 'c_phone': address.phone or '', 'c_mobile': address.mobile or '', 'c_fax': address.fax or '', 'c_email': address.email or '', 'c_adr_intervention_name': address_pose.name or address_pose.parent_id.name or '', 'c_adr_intervention_street': address_pose.street or '', 'c_adr_intervention_street2': address_pose.street2 or '', 'c_adr_intervention_city': address_pose.city or '', 'c_adr_intervention_zip': address_pose.zip or '', 'c_rdv_date': cal_event and date_event.strftime(date_format) or '', 'c_rdv_heure': cal_event and date_event.strftime('%H:%M') or '', 'date': time.strftime(date_format), 'order_name': order and order.name or '', }) for date_field in ('date_order', 'date_confirm_order', 'date_invoice'): if not res[date_field]: continue date = fields.Datetime.from_string(res[date_field]) date = date.strftime(date_format) res[date_field] = date # Pour rétrocompatibilité res.update({ 'c_adr_pose_name': res['c_adr_intervention_name'], 'c_adr_pose_street': res['c_adr_intervention_street'], 'c_adr_pose_street2': res['c_adr_intervention_street2'], 'c_adr_pose_city': res['c_adr_intervention_city'], 'c_adr_pose_zip': res['c_adr_intervention_zip'], }) res.update(objects) res.update({ 'o': o, 'format_date': lambda date, format=False, context=self._context: format_date( self.env, date, format), 'format_tz': lambda dt, tz=False, format=False, context=self._context: format_tz(self.env, dt, tz, format), 'format_amount': lambda amount, currency, context=self._context: format_amount( self.env, amount, currency), 'user': self.env.user, 'ctx': self._context, # context kw would clash with mako internals }) return res
def _render_template(self, template_txt, model, res_ids, post_process=False): """Override to add website to context""" multi_mode = True if isinstance(res_ids, pycompat.integer_types): multi_mode = False res_ids = [res_ids] results = dict.fromkeys(res_ids, u"") # try to load the template try: mako_env = mako_safe_template_env if self.env.context.get('safe') else mako_template_env template = mako_env.from_string(tools.ustr(template_txt)) except Exception: _logger.info("Failed to load template %r", template_txt, exc_info=True) return multi_mode and results or results[res_ids[0]] # prepare template variables records = self.env[model].browse(it for it in res_ids if it) # filter to avoid browsing [None] res_to_rec = dict.fromkeys(res_ids, None) for record in records: res_to_rec[record.id] = record variables = { 'format_date': lambda date, format=False, context=self._context: format_date(self.env, date, format), 'format_tz': lambda dt, tz=False, format=False, context=self._context: format_tz(self.env, dt, tz, format), 'format_amount': lambda amount, currency, context=self._context: format_amount(self.env, amount, currency), 'user': self.env.user, 'ctx': self._context, # context kw would clash with mako internals } # [NEW] Check website and company context company = self.env['res.company'] # empty value company_id = self.env.context.get('force_company') if company_id: company = self.env['res.company'].sudo().browse(company_id) if self.env.context.get('website_id'): website = self.env['website'].browse(self.env.context.get('website_id')) else: website = self.env.user.backend_website_id for res_id, record in res_to_rec.items(): record_company = company if not record_company: if hasattr(record, 'company_id') and record.company_id: record_company = record.company_id record_website = website if hasattr(record, 'website_id') and record.website_id: record_website = record.website_id if record_company and record_website \ and record_website.company_id != company: # company and website are incompatible, so keep only company record_website = self.env['website'] # empty value record_context = dict(force_company=record_company.id, website_id=record_website.id) variables['object'] = record.with_context(**record_context) variables['website'] = record_website try: render_result = template.render(variables) except Exception: _logger.info("Failed to render template %r using values %r" % (template, variables), exc_info=True) raise UserError(_("Failed to render template %r using values %r") % (template, variables)) if render_result == u"False": render_result = u"" if post_process: render_result = self.with_context(**record_context).render_post_process(render_result) results[res_id] = render_result return multi_mode and results or results[res_ids[0]]
def calculer(self, simuler=False): """ Calcule les nouveaux prix des articles sélectionnés en fonction de la méthode de calcul choisie. """ self.ensure_one() order = self.order_id cur = order.pricelist_id.currency_id round_tax = self.env.user.company_id.tax_calculation_rounding_method != 'round_globally' lines_select = self.line_ids.filtered( lambda line: line.is_selected and line.order_line_id.price_unit) if not lines_select: # Toutes les lignes sélectionnées ont un prix unitaire à 0 lines_select = self.line_ids.filtered( lambda line: line.is_selected) nb_select = len(lines_select) lines_nonselect = self.line_ids - lines_select # Les totaux des lignes non sélectionnées sont gardés en précison standard total_ht_nonselect = sum( lines_nonselect.mapped('order_line_id').mapped('price_subtotal')) total_ttc_nonselect = sum(lines_nonselect.mapped('prix_total_ttc')) # Les totaux des lignes sélectionnées sont calculés en précison maximale total_ttc_select = total_ht_select = 0.0 line_taxes = {} for line in lines_select.with_context(round=False): # Calcul manuel des taxes avec context['round']==False pour conserver la précision des calculs order_line = line.order_line_id price = order_line.price_unit * ( 1 - (order_line.discount or 0.0) / 100.0) taxes = order_line.tax_id.compute_all( price, order_line.currency_id, order_line.product_uom_qty, product=order_line.product_id, partner=order_line.order_id.partner_id) if line.is_selected: total_ttc_select += taxes['total_included'] total_ht_select += taxes['total_excluded'] line_taxes[line.id] = taxes # On récupère les données du wizard et vérifie les données saisies par l'utilisateur if self.methode_remise == 'prix_ttc_cible': if self.valeur <= 0: raise UserError( u"(Erreur #RG105)\nVous devez saisir un montant total TTC cible." ) elif self.methode_remise == 'montant_ttc': if not self.valeur: raise UserError( u"(Erreur #RG115)\nVous devez saisir un montant TTC à déduire." ) if self.valeur > self.montant_total_ttc_initial: raise UserError( u"(Erreur #RG120)\n" u"Le montant TTC à déduire est supérieur au montant total TTC des articles" u"sur lesquels s'appliquent la remise.") elif self.methode_remise == 'pc': if not 0 < self.valeur <= 100: raise UserError( u"(Erreur #RG125)\nLe pourcentage de remise doit être supérieur à 0 et inférieur ou égal à 100." ) elif self.methode_remise == 'pc_marge': if self.valeur >= 100: raise UserError( u"(Erreur #RG130)\nLe pourcentage de marge doit être inférieur à 100." ) elif self.methode_remise == 'reset': pass else: return False total_select = total_ttc_select tax_field = 'total_included' # On détermine le montant TTC cible en fonction de la méthode de calcul choisie if self.methode_remise == 'prix_ttc_cible': total = self.valeur - total_ttc_nonselect elif self.methode_remise == 'montant_ttc': total = total_ttc_select - self.valeur elif self.methode_remise == 'pc': total = total_ttc_select * (1 - self.valeur / 100.0) elif self.methode_remise == 'pc_marge': # Attention : dans ce cas particulier le total est HT total = (100 * order.of_total_cout) / ( 100.0 - self.valeur) - total_ht_nonselect total_select = total_ht_select tax_field = 'total_excluded' self = self.with_context(pc_marge=True) else: total = False # On applique les nouveaux prix sur les lignes dans l'ordre décroissant de quantité vendue. # Cela permet d'ajuster plus facilement le prix sur les dernières lignes lines_select = self._get_ordered_lines(lines_select) values = {} to_distribute = total if self.arrondi_mode == 'no': line_rounding = False else: if not self.arrondi_prec: raise UserError( "Vous devez sélectionner la précision de l'arrondi") line_rounding = { 'field': self.arrondi_mode, 'precision': int(self.arrondi_prec) } for line in lines_select: order_line = line.order_line_id if self.methode_remise == 'reset': vals, taxes = self._calcule_reset_vals_ligne( order_line, line_rounding=line_rounding) else: vals, taxes = self._calcule_vals_ligne( order_line, to_distribute, total_select, currency=cur, # On arrondit toutes les lignes sauf la dernière rounding=line != lines_select[-1], line_rounding=line_rounding) # Recalcul de 'total_excluded' et 'total_included' sans les arrondis if not round_tax: amount_tax = sum(tax['amount'] for tax in taxes['taxes']) taxes.update({ 'total_excluded': taxes['base'], 'total_included': taxes['base'] + amount_tax }) if self.methode_remise != 'reset': to_distribute -= taxes[tax_field] total_select -= line_taxes[line.id][tax_field] nb_select -= 1 values.update(vals) # Mise à jour du prix simulé dans la ligne du wizard line.prix_total_ht_simul = taxes['total_excluded'] line.prix_total_ttc_simul = taxes['total_included'] for line in lines_nonselect: line.prix_total_ht_simul = line.order_line_id.price_subtotal line.prix_total_ttc_simul = line.order_line_id.price_total # Pour une simulation, le travail s'arrête ici if simuler: return total_ttc_init = order.amount_total self._appliquer(values) total_ttc_fin = order.amount_total # On ajoute le libellé de la remise dans les notes du devis si case cochée if self.afficher_remise: text = u"Remise exceptionnelle déduite de %s.\n" text = text % (format_amount(self.env, total_ttc_init - total_ttc_fin, cur)) order.note = text + (order.note or '') order.write({'of_echeance_line_ids': order._of_compute_echeances()})