Example #1
0
 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]]
Example #3
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
Example #4
0
    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]]
Example #5
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()})