Exemplo n.º 1
0
 def _graph_y_query(self):
     raise UserError(
         _('Undefined graph model for Sales Team: %s') % self.name)
Exemplo n.º 2
0
    def get_claim_report_user(self, employee_id, **post):
        if not request.env.user.has_group('fleet.fleet_group_manager'):
            return request.not_found()

        employee = request.env['hr.employee'].search(
            [('id', '=', employee_id)], limit=1)
        partner_ids = (employee.user_id.partner_id
                       | employee.sudo().address_home_id).ids
        if not employee or not partner_ids:
            return request.not_found()

        car_assignation_logs = request.env[
            'fleet.vehicle.assignation.log'].search([('driver_id', 'in',
                                                      partner_ids)])
        doc_list = request.env['ir.attachment'].search(
            [('res_model', '=', 'fleet.vehicle.assignation.log'),
             ('res_id', 'in', car_assignation_logs.ids)],
            order='create_date')

        writer = PdfFileWriter()

        font = "Helvetica"
        normal_font_size = 14

        for document in doc_list:
            car_line_doc = request.env['fleet.vehicle.assignation.log'].browse(
                document.res_id)
            try:
                reader = PdfFileReader(io.BytesIO(
                    base64.b64decode(document.datas)),
                                       strict=False,
                                       overwriteWarnings=False)
            except Exception:
                continue

            width = float(reader.getPage(0).mediaBox.getUpperRight_x())
            height = float(reader.getPage(0).mediaBox.getUpperRight_y())

            header = io.BytesIO()
            can = canvas.Canvas(header)
            can.setFont(font, normal_font_size)
            can.setFillColorRGB(1, 0, 0)

            car_name = car_line_doc.vehicle_id.display_name
            date_start = car_line_doc.date_start
            date_end = car_line_doc.date_end or '...'

            text_to_print = _("%s (driven from: %s to %s)") % (
                car_name, date_start, date_end)
            can.drawCentredString(width / 2, height - normal_font_size,
                                  text_to_print)
            can.save()
            header_pdf = PdfFileReader(header, overwriteWarnings=False)

            for page_number in range(0, reader.getNumPages()):
                page = reader.getPage(page_number)
                page.mergePage(header_pdf.getPage(0))
                writer.addPage(page)

        _buffer = io.BytesIO()
        writer.write(_buffer)
        merged_pdf = _buffer.getvalue()
        _buffer.close()

        pdfhttpheaders = [('Content-Type', 'application/pdf'),
                          ('Content-Length', len(merged_pdf))]

        return request.make_response(merged_pdf, headers=pdfhttpheaders)
Exemplo n.º 3
0
 def unlink(self):
     for order in self:
         if not order.state == 'cancel':
             raise UserError(_('In order to delete a purchase order, you must cancel it first.'))
     return super(PurchaseOrder, self).unlink()
Exemplo n.º 4
0
    def _get_quants_action(self, domain=None, extend=False):
        """ Returns an action to open quant view.
        Depending of the context (user have right to be inventory mode or not),
        the list view will be editable or readonly.

        :param domain: List for the domain, empty by default.
        :param extend: If True, enables form, graph and pivot views. False by default.
        """
        self._quant_tasks()
        action = {
            'name':
            _('Update Quantity'),
            'view_type':
            'tree',
            'view_mode':
            'list',
            'res_model':
            'stock.quant',
            'type':
            'ir.actions.act_window',
            'context':
            dict(self.env.context),
            'domain':
            domain or [],
            'help':
            """
                <p class="o_view_nocontent_empty_folder">No Stock On Hand</p>
                <p>This analysis gives you an overview of the current stock
                level of your products.</p>
                """
        }

        if self._is_inventory_mode():
            action['view_id'] = self.env.ref(
                'stock.view_stock_quant_tree_editable').id
            # fixme: erase the following condition when it'll be possible to create a new record
            # from a empty grouped editable list without go through the form view.
            if not self.search_count(
                [('company_id', '=', self.env.company.id),
                 ('location_id.usage', 'in', ['internal', 'transit'])]):
                action['context'].update({
                    'search_default_productgroup': 0,
                    'search_default_locationgroup': 0
                })
        else:
            action['view_id'] = self.env.ref('stock.view_stock_quant_tree').id
            # Enables form view in readonly list
            action.update({
                'view_mode':
                'tree,form',
                'views': [
                    (action['view_id'], 'list'),
                    (self.env.ref('stock.view_stock_quant_form').id, 'form'),
                ],
            })
        if extend:
            action.update({
                'view_mode':
                'tree,form,pivot,graph',
                'views': [
                    (action['view_id'], 'list'),
                    (self.env.ref('stock.view_stock_quant_form').id, 'form'),
                    (self.env.ref('stock.view_stock_quant_pivot').id, 'pivot'),
                    (self.env.ref('stock.stock_quant_view_graph').id, 'graph'),
                ],
            })
        return action
Exemplo n.º 5
0
 def _check_parent_id(self):
     if not self._check_recursion():
         raise ValidationError(_('You cannot create recursive departments.'))
Exemplo n.º 6
0
    def _create_invoice(self, order, so_line, amount):
        if (self.advance_payment_method == 'percentage' and
                self.amount <= 0.00) or (self.advance_payment_method == 'fixed'
                                         and self.fixed_amount <= 0.00):
            raise UserError(
                _('The value of the down payment amount must be positive.'))
        if self.advance_payment_method == 'percentage':
            amount = order.amount_untaxed * self.amount / 100
            name = _("Down payment of %s%%") % (self.amount, )
        else:
            amount = self.fixed_amount
            name = _('Down Payment')

        invoice_vals = {
            'type':
            'out_invoice',
            'invoice_origin':
            order.name,
            'invoice_user_id':
            order.user_id.id,
            'narration':
            order.note,
            'partner_id':
            order.partner_invoice_id.id,
            'fiscal_position_id':
            order.fiscal_position_id.id
            or order.partner_id.property_account_position_id.id,
            'partner_shipping_id':
            order.partner_shipping_id.id,
            'currency_id':
            order.pricelist_id.currency_id.id,
            'invoice_payment_ref':
            order.client_order_ref,
            'invoice_payment_term_id':
            order.payment_term_id.id,
            'invoice_partner_bank_id':
            order.company_id.partner_id.bank_ids[:1],
            'team_id':
            order.team_id.id,
            'campaign_id':
            order.campaign_id.id,
            'medium_id':
            order.medium_id.id,
            'source_id':
            order.source_id.id,
            'invoice_line_ids': [(0, 0, {
                'name':
                name,
                'price_unit':
                amount,
                'quantity':
                1.0,
                'product_id':
                self.product_id.id,
                'product_uom_id':
                so_line.product_uom.id,
                'tax_ids': [(6, 0, so_line.tax_id.ids)],
                'sale_line_ids': [(6, 0, [so_line.id])],
                'analytic_tag_ids': [(6, 0, so_line.analytic_tag_ids.ids)],
                'analytic_account_id':
                order.analytic_account_id.id or False,
            })],
        }
        if order.fiscal_position_id:
            invoice_vals['fiscal_position_id'] = order.fiscal_position_id.id
        invoice = self.env['account.move'].create(invoice_vals)
        invoice.message_post_with_view(
            'mail.message_origin_link',
            values={
                'self': invoice,
                'origin': order
            },
            subtype_id=self.env.ref('mail.mt_note').id)
        return invoice
Exemplo n.º 7
0
 def check_location_id(self):
     for quant in self:
         if quant.location_id.usage == 'view':
             raise ValidationError(
                 _('You cannot take products from or deliver products to a location of type "view".'
                   ))
Exemplo n.º 8
0
 def _message_get_suggested_recipients(self):
     recipients = super(Partner, self)._message_get_suggested_recipients()
     for partner in self:
         partner._message_add_suggested_recipient(
             recipients, partner=partner, reason=_('Partner Profile'))
     return recipients
Exemplo n.º 9
0
 def _check_coupon_code(self, order):
     message = {}
     applicable_programs = order._get_applicable_programs()
     if self.state in ('used', 'expired') or \
        (self.expiration_date and self.expiration_date < order.date_order.date()):
         message = {
             'error':
             _('This coupon %s has been used or is expired.') % (self.code)
         }
     elif self.state == 'reserved':
         message = {
             'error':
             _('This coupon %s exists but the origin sales order is not validated yet.'
               ) % (self.code)
         }
     # Minimum requirement should not be checked if the coupon got generated by a promotion program (the requirement should have only be checked to generate the coupon)
     elif self.program_id.program_type == 'coupon_program' and not self.program_id._filter_on_mimimum_amount(
             order):
         message = {
             'error':
             _('A minimum of %s %s should be purchased to get the reward') %
             (self.program_id.rule_minimum_amount,
              self.program_id.currency_id.name)
         }
     elif not self.program_id.active:
         message = {
             'error':
             _('The coupon program for %s is in draft or closed state') %
             (self.code)
         }
     elif self.partner_id and self.partner_id != order.partner_id:
         message = {'error': _('Invalid partner.')}
     elif self.program_id in order.applied_coupon_ids.mapped('program_id'):
         message = {
             'error': _('A Coupon is already applied for the same reward')
         }
     elif self.program_id._is_global_discount_program(
     ) and order._is_global_discount_already_applied():
         message = {'error': _('Global discounts are not cumulable.')}
     elif self.program_id.reward_type == 'product' and not order._is_reward_in_order_lines(
             self.program_id):
         message = {
             'error':
             _('The reward products should be in the sales order lines to apply the discount.'
               )
         }
     elif not self.program_id._is_valid_partner(order.partner_id):
         message = {
             'error': _("The customer doesn't have access to this reward.")
         }
     # Product requirement should not be checked if the coupon got generated by a promotion program (the requirement should have only be checked to generate the coupon)
     elif self.program_id.program_type == 'coupon_program' and not self.program_id._filter_programs_on_products(
             order):
         message = {
             'error':
             _("You don't have the required product quantities on your sales order. All the products should be recorded on the sales order. (Example: You need to have 3 T-shirts on your sales order if the promotion is 'Buy 2, Get 1 Free')."
               )
         }
     else:
         if self.program_id not in applicable_programs and self.program_id.promo_applicability == 'on_current_order':
             message = {
                 'error':
                 _('At least one of the required conditions is not met to get the reward!'
                   )
             }
     return message
Exemplo n.º 10
0
 def _check_promo_code(self, order, coupon_code):
     message = {}
     applicable_programs = order._get_applicable_programs()
     if self.maximum_use_number != 0 and self.order_count >= self.maximum_use_number:
         message = {
             'error': _('Promo code %s has been expired.') % (coupon_code)
         }
     elif not self._filter_on_mimimum_amount(order):
         message = {
             'error':
             _('A minimum of %s %s should be purchased to get the reward') %
             (self.rule_minimum_amount, self.currency_id.name)
         }
     elif self.promo_code and self.promo_code == order.promo_code:
         message = {
             'error': _('The promo code is already applied on this order')
         }
     elif not self.promo_code and self in order.no_code_promo_program_ids:
         message = {
             'error':
             _('The promotional offer is already applied on this order')
         }
     elif not self.active:
         message = {'error': _('Promo code is invalid')}
     elif self.rule_date_from and self.rule_date_from > order.date_order or self.rule_date_to and order.date_order > self.rule_date_to:
         message = {'error': _('Promo code is expired')}
     elif order.promo_code and self.promo_code_usage == 'code_needed':
         message = {'error': _('Promotionals codes are not cumulative.')}
     elif self._is_global_discount_program(
     ) and order._is_global_discount_already_applied():
         message = {'error': _('Global discounts are not cumulative.')}
     elif self.promo_applicability == 'on_current_order' and self.reward_type == 'product' and not order._is_reward_in_order_lines(
             self):
         message = {
             'error':
             _('The reward products should be in the sales order lines to apply the discount.'
               )
         }
     elif not self._is_valid_partner(order.partner_id):
         message = {
             'error': _("The customer doesn't have access to this reward.")
         }
     elif not self._filter_programs_on_products(order):
         message = {
             'error':
             _("You don't have the required product quantities on your sales order. If the reward is same product quantity, please make sure that all the products are recorded on the sales order (Example: You need to have 3 T-shirts on your sales order if the promotion is 'Buy 2, Get 1 Free'."
               )
         }
     else:
         if self not in applicable_programs and self.promo_applicability == 'on_current_order':
             message = {
                 'error':
                 _('At least one of the required conditions is not met to get the reward!'
                   )
             }
     return message
Exemplo n.º 11
0
    def _merge(self, partner_ids, dst_partner=None, extra_checks=True):
        """ private implementation of merge partner
            :param partner_ids : ids of partner to merge
            :param dst_partner : record of destination res.partner
            :param extra_checks: pass False to bypass extra sanity check (e.g. email address)
        """
        # super-admin can be used to bypass extra checks
        if self.env.is_admin():
            extra_checks = False

        Partner = self.env['res.partner']
        partner_ids = Partner.browse(partner_ids).exists()
        if len(partner_ids) < 2:
            return

        if len(partner_ids) > 3:
            raise UserError(
                _("For safety reasons, you cannot merge more than 3 contacts together. You can re-open the wizard several times if needed."
                  ))

        # check if the list of partners to merge contains child/parent relation
        child_ids = self.env['res.partner']
        for partner_id in partner_ids:
            child_ids |= Partner.search([('id', 'child_of', [partner_id.id])
                                         ]) - partner_id
        if partner_ids & child_ids:
            raise UserError(
                _("You cannot merge a contact with one of his parent."))

        if extra_checks and len(set(partner.email
                                    for partner in partner_ids)) > 1:
            raise UserError(
                _("All contacts must have the same email. Only the Administrator can merge contacts with different emails."
                  ))

        # remove dst_partner from partners to merge
        if dst_partner and dst_partner in partner_ids:
            src_partners = partner_ids - dst_partner
        else:
            ordered_partners = self._get_ordered_partner(partner_ids.ids)
            dst_partner = ordered_partners[-1]
            src_partners = ordered_partners[:-1]
        _logger.info("dst_partner: %s", dst_partner.id)

        # FIXME: is it still required to make and exception for account.move.line since accounting v9.0 ?
        if extra_checks and 'account.move.line' in self.env and self.env[
                'account.move.line'].sudo().search([('partner_id', 'in', [
                    partner.id for partner in src_partners
                ])]):
            raise UserError(
                _("Only the destination contact may be linked to existing Journal Items. Please ask the Administrator if you need to merge several contacts linked to existing Journal Items."
                  ))

        # Make the company of all related users consistent
        for user in partner_ids.user_ids:
            user.sudo().write({
                'company_ids': [(6, 0, [dst_partner.company_id.id])],
                'company_id':
                dst_partner.company_id.id
            })

        # call sub methods to do the merge
        self._update_foreign_keys(src_partners, dst_partner)
        self._update_reference_fields(src_partners, dst_partner)
        self._update_values(src_partners, dst_partner)

        self._log_merge_operation(src_partners, dst_partner)

        # delete source partner, since they are merged
        src_partners.unlink()
Exemplo n.º 12
0
 def unlink(self):
     for program in self.filtered(lambda x: x.active):
         raise UserError(_('You can not delete a program in active state'))
     return super(SaleCouponProgram, self).unlink()
Exemplo n.º 13
0
 def do_print_checks(self):
     """ This method is a hook for l10n_xx_check_printing modules to implement actual check printing capabilities """
     raise UserError(
         _("You have to choose a check layout. For this, go in Apps, search for 'Checks layout' and install one."
           ))
Exemplo n.º 14
0
 def _compute_dashboard_button_name(self):
     """ Sets the adequate dashboard button name depending on the Sales Team's options
     """
     for team in self:
         team.dashboard_button_name = _(
             "Big Pretty Button :)")  # placeholder
Exemplo n.º 15
0
 def _default_content(self):
     return '''
         <p class="o_default_snippet_text">''' + _(
         "Start writing here...") + '''</p>
Exemplo n.º 16
0
 def _get_answer(self, record, body, values, command=False):
     # onboarding
     cofficebot_state = self.env.user.cofficebot_state
     if self._is_bot_in_private_channel(record):
         # main flow
         if cofficebot_state == 'onboarding_emoji' and self._body_contains_emoji(
                 body):
             self.env.user.cofficebot_state = "onboarding_attachement"
             return _(
                 "Great! 👍<br/>Now, try to <b>send an attachment</b>, like a picture of your cute dog..."
             )
         elif cofficebot_state == 'onboarding_attachement' and values.get(
                 "attachment_ids"):
             self.env.user.cofficebot_state = "onboarding_command"
             return _(
                 "Not a cute dog, but you get it 😊<br/>To access special features, <b>start your sentence with '/'</b>. Try to get help."
             )
         elif cofficebot_state == 'onboarding_command' and command == 'help':
             self.env.user.cofficebot_state = "onboarding_ping"
             return _(
                 "Wow you are a natural!<br/>Ping someone to grab its attention with @nameoftheuser. <b>Try to ping me using @COfficeBot</b> in a sentence."
             )
         elif cofficebot_state == 'onboarding_ping' and self._is_bot_pinged(
                 values):
             self.env.user.cofficebot_state = "idle"
             return _(
                 "Yep, I am here! 🎉 <br/>You finished the tour, you can <b>close this chat window</b>. Enjoy discovering COffice."
             )
         elif cofficebot_state == "idle" and (_('start the tour')
                                              in body.lower()):
             self.env.user.cofficebot_state = "onboarding_emoji"
             return _("To start, try to send me an emoji :)")
         # easter eggs
         elif cofficebot_state == "idle" and body in [
                 '❤️', _('i love you'), _('love')
         ]:
             return _(
                 "Aaaaaw that's really cute but, you know, bots don't work that way. You're too human for me! Let's keep it professional ❤️"
             )
         elif cofficebot_state == "idle" and (('help' in body)
                                              or _('help') in body):
             return _(
                 "I'm just a bot... :( You can check <a href=\"https://www.coffice.com/page/docs\">our documentation</a>) for more information!"
             )
         elif _('f**k') in body or "f**k" in body:
             return _("That's not nice! I'm a bot but I have feelings... 💔")
         else:
             #repeat question
             if cofficebot_state == 'onboarding_emoji':
                 return _(
                     "Not exactly. To continue the tour, send an emoji, <b>type \":)\"</b> and press enter."
                 )
             elif cofficebot_state == 'onboarding_attachement':
                 return _(
                     "To <b>send an attachment</b>, click the 📎 icon on the right, and select a file."
                 )
             elif cofficebot_state == 'onboarding_command':
                 return _(
                     "Not sure wat you are doing. Please press / and wait for the propositions. Select \"help\" and press enter"
                 )
             elif cofficebot_state == 'onboarding_ping':
                 return _(
                     "Sorry, I am not listening. To get someone's attention, <b>ping him</b>. Write \"@cofficebot\" and select me."
                 )
             return random.choice([
                 _("I'm not smart enough to answer your question.<br/>To follow my guide, ask"
                   ) + ": <b>" + _('start the tour') + "</b>",
                 _("Hmmm..."),
                 _("I'm afraid I don't understand. Sorry!"),
                 _("Sorry I'm sleepy. Or not! Maybe I'm just trying to hide my unawareness of human language...<br/>I can show you features if you write"
                   ) + ": '<b>" + _('start the tour') + "</b>'.",
             ])
     elif self._is_bot_pinged(values):
         return random.choice(
             [_("Yep, COfficeBot is in the place!"),
              _("Pong.")])
     return False
Exemplo n.º 17
0
    def create_invoices(self):
        sale_orders = self.env['sale.order'].browse(
            self._context.get('active_ids', []))

        if self.advance_payment_method == 'delivered':
            sale_orders._create_invoices(final=self.deduct_down_payments)
        else:
            # Create deposit product if necessary
            if not self.product_id:
                vals = self._prepare_deposit_product()
                self.product_id = self.env['product.product'].create(vals)
                self.env['ir.config_parameter'].sudo().set_param(
                    'sale.default_deposit_product_id', self.product_id.id)

            sale_line_obj = self.env['sale.order.line']
            for order in sale_orders:
                if self.advance_payment_method == 'percentage':
                    amount = order.amount_untaxed * self.amount / 100
                else:
                    amount = self.fixed_amount
                if self.product_id.invoice_policy != 'order':
                    raise UserError(
                        _('The product used to invoice a down payment should have an invoice policy set to "Ordered quantities". Please update your deposit product to be able to create a deposit invoice.'
                          ))
                if self.product_id.type != 'service':
                    raise UserError(
                        _("The product used to invoice a down payment should be of type 'Service'. Please use another product or update this product."
                          ))
                taxes = self.product_id.taxes_id.filtered(
                    lambda r: not order.company_id or r.company_id == order.
                    company_id)
                if order.fiscal_position_id and taxes:
                    tax_ids = order.fiscal_position_id.map_tax(
                        taxes, self.product_id, order.partner_shipping_id).ids
                else:
                    tax_ids = taxes.ids
                context = {'lang': order.partner_id.lang}
                analytic_tag_ids = []
                for line in order.order_line:
                    analytic_tag_ids = [
                        (4, analytic_tag.id, None)
                        for analytic_tag in line.analytic_tag_ids
                    ]
                so_line = sale_line_obj.create({
                    'name':
                    _('Down Payment: %s') % (time.strftime('%m %Y'), ),
                    'price_unit':
                    amount,
                    'product_uom_qty':
                    0.0,
                    'order_id':
                    order.id,
                    'discount':
                    0.0,
                    'product_uom':
                    self.product_id.uom_id.id,
                    'product_id':
                    self.product_id.id,
                    'analytic_tag_ids':
                    analytic_tag_ids,
                    'tax_id': [(6, 0, tax_ids)],
                    'is_downpayment':
                    True,
                })
                del context
                self._create_invoice(order, so_line, amount)
        if self._context.get('open_invoices', False):
            return sale_orders.action_view_invoice()
        return {'type': 'ir.actions.act_window_close'}
Exemplo n.º 18
0
    def _send(self,
              auto_commit=False,
              raise_exception=False,
              smtp_session=None):
        IrMailServer = self.env['ir.mail_server']
        IrAttachment = self.env['ir.attachment']
        for mail_id in self.ids:
            success_pids = []
            failure_type = None
            processing_pid = None
            mail = None
            try:
                mail = self.browse(mail_id)
                if mail.state != 'outgoing':
                    if mail.state != 'exception' and mail.auto_delete:
                        mail.sudo().unlink()
                    continue

                # remove attachments if user send the link with the access_token
                body = mail.body_html or ''
                attachments = mail.attachment_ids
                for link in re.findall(r'/web/(?:content|image)/([0-9]+)',
                                       body):
                    attachments = attachments - IrAttachment.browse(int(link))

                # load attachment binary data with a separate read(), as prefetching all
                # `datas` (binary field) could bloat the browse cache, triggerring
                # soft/hard mem limits with temporary data.
                attachments = [(a['name'], base64.b64decode(a['datas']),
                                a['mimetype'])
                               for a in attachments.sudo().read(
                                   ['name', 'datas', 'mimetype'])
                               if a['datas'] is not False]

                # specific behavior to customize the send email for notified partners
                email_list = []
                if mail.email_to:
                    email_list.append(mail._send_prepare_values())
                for partner in mail.recipient_ids:
                    values = mail._send_prepare_values(partner=partner)
                    values['partner_id'] = partner
                    email_list.append(values)

                # headers
                headers = {}
                ICP = self.env['ir.config_parameter'].sudo()
                bounce_alias = ICP.get_param("mail.bounce.alias")
                catchall_domain = ICP.get_param("mail.catchall.domain")
                if bounce_alias and catchall_domain:
                    if mail.mail_message_id.is_thread_message():
                        headers['Return-Path'] = '%s+%d-%s-%d@%s' % (
                            bounce_alias, mail.id, mail.model, mail.res_id,
                            catchall_domain)
                    else:
                        headers['Return-Path'] = '%s+%d@%s' % (
                            bounce_alias, mail.id, catchall_domain)
                if mail.headers:
                    try:
                        headers.update(safe_eval(mail.headers))
                    except Exception:
                        pass

                # Writing on the mail object may fail (e.g. lock on user) which
                # would trigger a rollback *after* actually sending the email.
                # To avoid sending twice the same email, provoke the failure earlier
                mail.write({
                    'state':
                    'exception',
                    'failure_reason':
                    _('Error without exception. Probably due do sending an email without computed recipients.'
                      ),
                })
                # Update notification in a transient exception state to avoid concurrent
                # update in case an email bounces while sending all emails related to current
                # mail record.
                notifs = self.env['mail.notification'].search([
                    ('notification_type', '=', 'email'),
                    ('mail_id', 'in', mail.ids),
                    ('notification_status', 'not in', ('sent', 'canceled'))
                ])
                if notifs:
                    notif_msg = _(
                        'Error without exception. Probably due do concurrent access update of notification records. Please see with an administrator.'
                    )
                    notifs.sudo().write({
                        'notification_status': 'exception',
                        'failure_type': 'UNKNOWN',
                        'failure_reason': notif_msg,
                    })
                    # `test_mail_bounce_during_send`, force immediate update to obtain the lock.
                    # see rev. 56596e5240ef920df14d99087451ce6f06ac6d36
                    notifs.flush(fnames=[
                        'notification_status', 'failure_type', 'failure_reason'
                    ],
                                 records=notifs)

                # build an RFC2822 email.message.Message object and send it without queuing
                res = None
                for email in email_list:
                    msg = IrMailServer.build_email(
                        email_from=mail.email_from,
                        email_to=email.get('email_to'),
                        subject=mail.subject,
                        body=email.get('body'),
                        body_alternative=email.get('body_alternative'),
                        email_cc=tools.email_split(mail.email_cc),
                        reply_to=mail.reply_to,
                        attachments=attachments,
                        message_id=mail.message_id,
                        references=mail.references,
                        object_id=mail.res_id
                        and ('%s-%s' % (mail.res_id, mail.model)),
                        subtype='html',
                        subtype_alternative='plain',
                        headers=headers)
                    processing_pid = email.pop("partner_id", None)
                    try:
                        res = IrMailServer.send_email(
                            msg,
                            mail_server_id=mail.mail_server_id.id,
                            smtp_session=smtp_session)
                        if processing_pid:
                            success_pids.append(processing_pid)
                        processing_pid = None
                    except AssertionError as error:
                        if str(error) == IrMailServer.NO_VALID_RECIPIENT:
                            failure_type = "RECIPIENT"
                            # No valid recipient found for this particular
                            # mail item -> ignore error to avoid blocking
                            # delivery to next recipients, if any. If this is
                            # the only recipient, the mail will show as failed.
                            _logger.info(
                                "Ignoring invalid recipients for mail.mail %s: %s",
                                mail.message_id, email.get('email_to'))
                        else:
                            raise
                if res:  # mail has been sent at least once, no major exception occured
                    mail.write({
                        'state': 'sent',
                        'message_id': res,
                        'failure_reason': False
                    })
                    _logger.info(
                        'Mail with ID %r and Message-Id %r successfully sent',
                        mail.id, mail.message_id)
                    # /!\ can't use mail.state here, as mail.refresh() will cause an error
                    # see revid:[email protected] in 6.1
                mail._postprocess_sent_message(success_pids=success_pids,
                                               failure_type=failure_type)
            except MemoryError:
                # prevent catching transient MemoryErrors, bubble up to notify user or abort cron job
                # instead of marking the mail as failed
                _logger.exception(
                    'MemoryError while processing mail with ID %r and Msg-Id %r. Consider raising the --limit-memory-hard startup option',
                    mail.id, mail.message_id)
                # mail status will stay on ongoing since transaction will be rollback
                raise
            except (psycopg2.Error, smtplib.SMTPServerDisconnected):
                # If an error with the database or SMTP session occurs, chances are that the cursor
                # or SMTP session are unusable, causing further errors when trying to save the state.
                _logger.exception(
                    'Exception while processing mail with ID %r and Msg-Id %r.',
                    mail.id, mail.message_id)
                raise
            except Exception as e:
                failure_reason = tools.ustr(e)
                _logger.exception('failed sending mail (id: %s) due to %s',
                                  mail.id, failure_reason)
                mail.write({
                    'state': 'exception',
                    'failure_reason': failure_reason
                })
                mail._postprocess_sent_message(success_pids=success_pids,
                                               failure_reason=failure_reason,
                                               failure_type='UNKNOWN')
                if raise_exception:
                    if isinstance(e, (AssertionError, UnicodeEncodeError)):
                        if isinstance(e, UnicodeEncodeError):
                            value = "Invalid text: %s" % e.object
                        else:
                            # get the args of the original error, wrap into a value and throw a MailDeliveryException
                            # that is an except_orm, with name and value as arguments
                            value = '. '.join(e.args)
                        raise MailDeliveryException(_("Mail Delivery Failed"),
                                                    value)
                    raise

            if auto_commit is True:
                self._cr.commit()
        return True
Exemplo n.º 19
0
 def check_product_id(self):
     if any(elem.product_id.type != 'product' for elem in self):
         raise ValidationError(
             _('Quants cannot be created for consumables or services.'))
Exemplo n.º 20
0
 def write(self, values):
     if 'display_type' in values and self.filtered(lambda line: line.display_type != values.get('display_type')):
         raise UserError(_("You cannot change the type of a sale quote line. Instead you should delete the current line and create a new line of the proper type."))
     return super(SaleOrderTemplateLine, self).write(values)
Exemplo n.º 21
0
    def _update_reserved_quantity(self,
                                  product_id,
                                  location_id,
                                  quantity,
                                  lot_id=None,
                                  package_id=None,
                                  owner_id=None,
                                  strict=False):
        """ Increase the reserved quantity, i.e. increase `reserved_quantity` for the set of quants
        sharing the combination of `product_id, location_id` if `strict` is set to False or sharing
        the *exact same characteristics* otherwise. Typically, this method is called when reserving
        a move or updating a reserved move line. When reserving a chained move, the strict flag
        should be enabled (to reserve exactly what was brought). When the move is MTS,it could take
        anything from the stock, so we disable the flag. When editing a move line, we naturally
        enable the flag, to reflect the reservation according to the edition.

        :return: a list of tuples (quant, quantity_reserved) showing on which quant the reservation
            was done and how much the system was able to reserve on it
        """
        self = self.sudo()
        rounding = product_id.uom_id.rounding
        quants = self._gather(product_id,
                              location_id,
                              lot_id=lot_id,
                              package_id=package_id,
                              owner_id=owner_id,
                              strict=strict)
        reserved_quants = []

        if float_compare(quantity, 0, precision_rounding=rounding) > 0:
            # if we want to reserve
            available_quantity = self._get_available_quantity(
                product_id,
                location_id,
                lot_id=lot_id,
                package_id=package_id,
                owner_id=owner_id,
                strict=strict)
            if float_compare(quantity,
                             available_quantity,
                             precision_rounding=rounding) > 0:
                raise UserError(
                    _('It is not possible to reserve more products of %s than you have in stock.'
                      ) % product_id.display_name)
        elif float_compare(quantity, 0, precision_rounding=rounding) < 0:
            # if we want to unreserve
            available_quantity = sum(quants.mapped('reserved_quantity'))
            if float_compare(abs(quantity),
                             available_quantity,
                             precision_rounding=rounding) > 0:
                raise UserError(
                    _('It is not possible to unreserve more products of %s than you have in stock.'
                      ) % product_id.display_name)
        else:
            return reserved_quants

        for quant in quants:
            if float_compare(quantity, 0, precision_rounding=rounding) > 0:
                max_quantity_on_quant = quant.quantity - quant.reserved_quantity
                if float_compare(max_quantity_on_quant,
                                 0,
                                 precision_rounding=rounding) <= 0:
                    continue
                max_quantity_on_quant = min(max_quantity_on_quant, quantity)
                quant.reserved_quantity += max_quantity_on_quant
                reserved_quants.append((quant, max_quantity_on_quant))
                quantity -= max_quantity_on_quant
                available_quantity -= max_quantity_on_quant
            else:
                max_quantity_on_quant = min(quant.reserved_quantity,
                                            abs(quantity))
                quant.reserved_quantity -= max_quantity_on_quant
                reserved_quants.append((quant, -max_quantity_on_quant))
                quantity += max_quantity_on_quant
                available_quantity += max_quantity_on_quant

            if float_is_zero(
                    quantity, precision_rounding=rounding) or float_is_zero(
                        available_quantity, precision_rounding=rounding):
                break
        return reserved_quants
Exemplo n.º 22
0
 def _check_model_transience(self):
     if any(self.env[rule.model_id.model].is_transient() for rule in self):
         raise ValidationError(_('Rules can not be applied on Transient models.'))
Exemplo n.º 23
0
class QuantPackage(models.Model):
    """ Packages containing quants and/or other packages """
    _name = "stock.quant.package"
    _description = "Packages"
    _order = 'name'

    name = fields.Char(
        'Package Reference',
        copy=False,
        index=True,
        default=lambda self: self.env['ir.sequence'].next_by_code(
            'stock.quant.package') or _('Unknown Pack'))
    quant_ids = fields.One2many(
        'stock.quant',
        'package_id',
        'Bulk Content',
        readonly=True,
        domain=['|', ('quantity', '!=', 0), ('reserved_quantity', '!=', 0)])
    packaging_id = fields.Many2one('product.packaging',
                                   'Package Type',
                                   index=True,
                                   check_company=True)
    location_id = fields.Many2one('stock.location',
                                  'Location',
                                  compute='_compute_package_info',
                                  index=True,
                                  readonly=True,
                                  store=True)
    company_id = fields.Many2one('res.company',
                                 'Company',
                                 compute='_compute_package_info',
                                 index=True,
                                 readonly=True,
                                 store=True)
    owner_id = fields.Many2one('res.partner',
                               'Owner',
                               compute='_compute_package_info',
                               search='_search_owner',
                               index=True,
                               readonly=True,
                               compute_sudo=True)

    @api.depends('quant_ids.package_id', 'quant_ids.location_id',
                 'quant_ids.company_id', 'quant_ids.owner_id',
                 'quant_ids.quantity', 'quant_ids.reserved_quantity')
    def _compute_package_info(self):
        for package in self:
            values = {'location_id': False, 'owner_id': False}
            if package.quant_ids:
                values['location_id'] = package.quant_ids[0].location_id
                if all(q.owner_id == package.quant_ids[0].owner_id
                       for q in package.quant_ids):
                    values['owner_id'] = package.quant_ids[0].owner_id
                if all(q.company_id == package.quant_ids[0].company_id
                       for q in package.quant_ids):
                    values['company_id'] = package.quant_ids[0].company_id
            package.location_id = values['location_id']
            package.company_id = values.get('company_id')
            package.owner_id = values['owner_id']

    def name_get(self):
        return list(self._compute_complete_name().items())

    def _compute_complete_name(self):
        """ Forms complete name of location from parent location to child location. """
        res = {}
        for package in self:
            name = package.name
            res[package.id] = name
        return res

    def _search_owner(self, operator, value):
        if value:
            packs = self.search([('quant_ids.owner_id', operator, value)])
        else:
            packs = self.search([('quant_ids', operator, value)])
        if packs:
            return [('id', 'parent_of', packs.ids)]
        else:
            return [('id', '=', False)]

    def unpack(self):
        for package in self:
            move_line_to_modify = self.env['stock.move.line'].search([
                ('package_id', '=', package.id),
                ('state', 'in', ('assigned', 'partially_available')),
                ('product_qty', '!=', 0),
            ])
            move_line_to_modify.write({'package_id': False})
            package.mapped('quant_ids').sudo().write({'package_id': False})

    def action_view_picking(self):
        action = self.env.ref('stock.action_picking_tree_all').read()[0]
        domain = [
            '|', ('result_package_id', 'in', self.ids),
            ('package_id', 'in', self.ids)
        ]
        pickings = self.env['stock.move.line'].search(domain).mapped(
            'picking_id')
        action['domain'] = [('id', 'in', pickings.ids)]
        return action

    def view_content_package(self):
        action = self.env['ir.actions.act_window'].for_xml_id(
            'stock', 'quantsact')
        action['domain'] = [('id', 'in', self._get_contained_quants().ids)]
        return action

    def _get_contained_quants(self):
        return self.env['stock.quant'].search([('package_id', 'in', self.ids)])

    def _get_all_products_quantities(self):
        '''This function computes the different product quantities for the given package
        '''
        # TDE CLEANME: probably to move somewhere else, like in pack op
        res = {}
        for quant in self._get_contained_quants():
            if quant.product_id not in res:
                res[quant.product_id] = 0
            res[quant.product_id] += quant.quantity
        return res
Exemplo n.º 24
0
 def _check_model_name(self):
     # Don't allow rules on rules records (this model).
     if any(rule.model_id.model == self._name for rule in self):
         raise ValidationError(_('Rules can not be applied on the Record Rules model.'))
Exemplo n.º 25
0
    def portal_my_timesheets(self,
                             page=1,
                             sortby=None,
                             filterby=None,
                             search=None,
                             search_in='all',
                             groupby='project',
                             **kw):
        Timesheet_sudo = request.env['account.analytic.line'].sudo()
        values = self._prepare_portal_layout_values()
        domain = request.env[
            'account.analytic.line']._timesheet_get_portal_domain()

        searchbar_sortings = {
            'date': {
                'label': _('Newest'),
                'order': 'date desc'
            },
            'name': {
                'label': _('Name'),
                'order': 'name'
            },
        }

        searchbar_inputs = {
            'all': {
                'input': 'all',
                'label': _('Search in All')
            },
        }

        searchbar_groupby = {
            'none': {
                'input': 'none',
                'label': _('None')
            },
            'project': {
                'input': 'project',
                'label': _('Project')
            },
        }

        today = fields.Date.today()
        quarter_start, quarter_end = date_utils.get_quarter(today)
        last_week = today + relativedelta(weeks=-1)
        last_month = today + relativedelta(months=-1)
        last_year = today + relativedelta(years=-1)

        searchbar_filters = {
            'all': {
                'label': _('All'),
                'domain': []
            },
            'today': {
                'label': _('Today'),
                'domain': [("date", "=", today)]
            },
            'week': {
                'label':
                _('This week'),
                'domain': [('date', '>=', date_utils.start_of(today, "week")),
                           ('date', '<=', date_utils.end_of(today, 'week'))]
            },
            'month': {
                'label':
                _('This month'),
                'domain': [('date', '>=', date_utils.start_of(today, 'month')),
                           ('date', '<=', date_utils.end_of(today, 'month'))]
            },
            'year': {
                'label':
                _('This year'),
                'domain': [('date', '>=', date_utils.start_of(today, 'year')),
                           ('date', '<=', date_utils.end_of(today, 'year'))]
            },
            'quarter': {
                'label':
                _('This Quarter'),
                'domain': [('date', '>=', quarter_start),
                           ('date', '<=', quarter_end)]
            },
            'last_week': {
                'label':
                _('Last week'),
                'domain':
                [('date', '>=', date_utils.start_of(last_week, "week")),
                 ('date', '<=', date_utils.end_of(last_week, 'week'))]
            },
            'last_month': {
                'label':
                _('Last month'),
                'domain':
                [('date', '>=', date_utils.start_of(last_month, 'month')),
                 ('date', '<=', date_utils.end_of(last_month, 'month'))]
            },
            'last_year': {
                'label':
                _('Last year'),
                'domain':
                [('date', '>=', date_utils.start_of(last_year, 'year')),
                 ('date', '<=', date_utils.end_of(last_year, 'year'))]
            },
        }
        # default sort by value
        if not sortby:
            sortby = 'date'
        order = searchbar_sortings[sortby]['order']
        # default filter by value
        if not filterby:
            filterby = 'all'
        domain = AND([domain, searchbar_filters[filterby]['domain']])

        if search and search_in:
            domain = AND([domain, [('name', 'ilike', search)]])

        timesheet_count = Timesheet_sudo.search_count(domain)
        # pager
        pager = portal_pager(url="/my/timesheets",
                             url_args={
                                 'sortby': sortby,
                                 'search_in': search_in,
                                 'search': search,
                                 'filterby': filterby
                             },
                             total=timesheet_count,
                             page=page,
                             step=self._items_per_page)

        if groupby == 'project':
            order = "project_id, %s" % order
        timesheets = Timesheet_sudo.search(domain,
                                           order=order,
                                           limit=self._items_per_page,
                                           offset=pager['offset'])
        if groupby == 'project':
            grouped_timesheets = [
                Timesheet_sudo.concat(*g)
                for k, g in groupbyelem(timesheets, itemgetter('project_id'))
            ]
        else:
            grouped_timesheets = [timesheets]

        values.update({
            'timesheets':
            timesheets,
            'grouped_timesheets':
            grouped_timesheets,
            'page_name':
            'timesheet',
            'default_url':
            '/my/timesheets',
            'pager':
            pager,
            'searchbar_sortings':
            searchbar_sortings,
            'search_in':
            search_in,
            'sortby':
            sortby,
            'groupby':
            groupby,
            'searchbar_inputs':
            searchbar_inputs,
            'searchbar_groupby':
            searchbar_groupby,
            'searchbar_filters':
            OrderedDict(sorted(searchbar_filters.items())),
            'filterby':
            filterby,
        })
        return request.render("hr_timesheet.portal_my_timesheets", values)
Exemplo n.º 26
0
 def _verify_pin(self):
     for employee in self:
         if employee.pin and not employee.pin.isdigit():
             raise ValidationError(
                 _("The PIN must be a sequence of digits."))
Exemplo n.º 27
0
 def _check_order_line_company_id(self):
     for order in self:
         companies = order.order_line.product_id.company_id
         if companies and companies != order.company_id:
             bad_products = order.order_line.product_id.filtered(lambda p: p.company_id and p.company_id != order.company_id)
             raise ValidationError((_("Your quotation contains products from company %s whereas your quotation belongs to company %s. \n Please change the company of your quotation or remove the products from other companies (%s).") % (', '.join(companies.mapped('display_name')), order.company_id.display_name, ', '.join(bad_products.mapped('display_name')))))
Exemplo n.º 28
0
 def get_import_templates(self):
     return [{
         'label': _('Import Template for Employees'),
         'template': '/hr/static/xls/hr_employee.xls'
     }]
Exemplo n.º 29
0
 def unlink(self):
     for line in self:
         if line.order_id.state in ['purchase', 'done']:
             raise UserError(_('Cannot delete a purchase order line which is in state \'%s\'.') % (line.state,))
     return super(PurchaseOrderLine, self).unlink()
Exemplo n.º 30
0
 def _graph_get_model(self):
     """ skeleton function defined here because it'll be called by crm and/or sale
     """
     raise UserError(
         _('Undefined graph model for Sales Team: %s') % self.name)