Exemplo n.º 1
0
    def reverse_anonymize_database(self):
        """Set the 'clear' state to defined fields"""
        self.ensure_one()
        IrModelFieldsAnonymization = self.env['ir.model.fields.anonymization']

        # check that all the defined fields are in the 'anonymized' state
        state = IrModelFieldsAnonymization._get_global_state()
        if state == 'clear':
            raise UserError(
                _("The database is not currently anonymized, you cannot reverse the anonymization."
                  ))
        elif state == 'unstable':
            raise UserError(
                _("The database anonymization is currently in an unstable state. Some fields are anonymized,"
                  " while some fields are not anonymized. You should try to solve this problem before trying to do anything."
                  ))

        if not self.file_import:
            raise UserError('%s: %s' % (
                _('Error !'),
                _("It is not possible to reverse the anonymization process without supplying the anonymization export file."
                  )))

        # reverse the anonymization:
        # load the json/pickle file content into a data structure:
        content = base64.decodestring(self.file_import)
        try:
            data = json.loads(content.decode('utf8'))
        except Exception:
            # backward-compatible mode
            data = pickle.loads(content, encoding='utf8')

        fixes = self.env[
            'ir.model.fields.anonymization.migration.fix'].search_read([
                ('target_version', '=', '.'.join(
                    str(v) for v in version_info[:2]))
            ], ['model_name', 'field_name', 'query', 'query_type', 'sequence'])
        fixes = group(fixes, ('model_name', 'field_name'))

        for line in data:
            queries = []
            table_name = self.env[line['model_id']]._table if line[
                'model_id'] in self.env else None

            # check if custom sql exists:
            key = (line['model_id'], line['field_id'])
            custom_updates = fixes.get(key)
            if custom_updates:
                custom_updates.sort(key=itemgetter('sequence'))
                queries = [(record['query'], record['query_type'])
                           for record in custom_updates
                           if record['query_type']]
            elif table_name:
                queries = [(
                    'update "%(table)s" set "%(field)s" = %%(value)s where id = %%(id)s'
                    % {
                        'table': table_name,
                        'field': line['field_id'],
                    }, 'sql')]

            for query in queries:
                if query[1] == 'sql':
                    self.env.cr.execute(query[0], {
                        'value': line['value'],
                        'id': line['id']
                    })
                elif query[1] == 'python':
                    safe_eval(query[0] % line)
                else:
                    raise Exception(
                        "Unknown query type '%s'. Valid types are: sql, python."
                        % (query['query_type'], ))

        # update the anonymization fields:
        ano_fields = IrModelFieldsAnonymization.search([('state', '!=',
                                                         'not_existing')])
        ano_fields.write({'state': 'clear'})

        # add a result message in the wizard:
        self.msg = '\n'.join(["Successfully reversed the anonymization.", ""])

        # create a new history record:
        history = self.env['ir.model.fields.anonymization.history'].create({
            'date':
            fields.Datetime.now(),
            'field_ids': [[6, 0, ano_fields.ids]],
            'msg':
            self.msg,
            'filepath':
            False,
            'direction':
            'anonymized -> clear',
            'state':
            'done'
        })

        return {
            'res_id':
            self.id,
            'view_id':
            self.env.ref(
                'anonymization.view_ir_model_fields_anonymize_wizard_form').
            ids,
            'view_type':
            'form',
            "view_mode":
            'form',
            'res_model':
            'ir.model.fields.anonymize.wizard',
            'type':
            'ir.actions.act_window',
            'context': {
                'step': 'just_desanonymized'
            },
            'target':
            'new'
        }
Exemplo n.º 2
0
    def action_repair_done(self):
        """ Creates stock move for operation and stock move for final product of repair order.
        @return: Move ids of final products

        """
        if self.filtered(lambda repair: not repair.repaired):
            raise UserError(_("Repair must be repaired in order to make the product moves."))
        res = {}
        precision = self.env['decimal.precision'].precision_get('Product Unit of Measure')
        Move = self.env['stock.move']
        for repair in self:
            # Try to create move with the appropriate owner
            owner_id = False
            available_qty_owner = self.env['stock.quant']._get_available_quantity(repair.product_id, repair.location_id, repair.lot_id, owner_id=repair.partner_id, strict=True)
            if float_compare(available_qty_owner, repair.product_qty, precision_digits=precision) >= 0:
                owner_id = repair.partner_id.id

            moves = self.env['stock.move']
            for operation in repair.operations:
                move = Move.create({
                    'name': repair.name,
                    'product_id': operation.product_id.id,
                    'product_uom_qty': operation.product_uom_qty,
                    'product_uom': operation.product_uom.id,
                    'partner_id': repair.address_id.id,
                    'location_id': operation.location_id.id,
                    'location_dest_id': operation.location_dest_id.id,
                    'move_line_ids': [(0, 0, {'product_id': operation.product_id.id,
                                           'lot_id': operation.lot_id.id, 
                                           'product_uom_qty': 0,  # bypass reservation here
                                           'product_uom_id': operation.product_uom.id,
                                           'qty_done': operation.product_uom_qty,
                                           'package_id': False,
                                           'result_package_id': False,
                                           'owner_id': owner_id,
                                           'location_id': operation.location_id.id, #TODO: owner stuff
                                           'location_dest_id': operation.location_dest_id.id,})],
                    'repair_id': repair.id,
                    'origin': repair.name,
                })
                moves |= move
                operation.write({'move_id': move.id, 'state': 'done'})
            move = Move.create({
                'name': repair.name,
                'product_id': repair.product_id.id,
                'product_uom': repair.product_uom.id or repair.product_id.uom_id.id,
                'product_uom_qty': repair.product_qty,
                'partner_id': repair.address_id.id,
                'location_id': repair.location_id.id,
                'location_dest_id': repair.location_dest_id.id,
                'move_line_ids': [(0, 0, {'product_id': repair.product_id.id,
                                           'lot_id': repair.lot_id.id, 
                                           'product_uom_qty': 0,  # bypass reservation here
                                           'product_uom_id': repair.product_uom.id or repair.product_id.uom_id.id,
                                           'qty_done': repair.product_qty,
                                           'package_id': False,
                                           'result_package_id': False,
                                           'owner_id': owner_id,
                                           'location_id': repair.location_id.id, #TODO: owner stuff
                                           'location_dest_id': repair.location_dest_id.id,})],
                'repair_id': repair.id,
                'origin': repair.name,
            })
            consumed_lines = moves.mapped('move_line_ids')
            produced_lines = move.move_line_ids
            moves |= move
            moves._action_done()
            produced_lines.write({'consume_line_ids': [(6, 0, consumed_lines.ids)]})
            res[repair.id] = move.id
        return res
Exemplo n.º 3
0
 def _check_date(self):
     if self.issued_date > self.return_date:
         raise ValidationError(
             _('Return Date cannot be set before Issued Date.'))
Exemplo n.º 4
0
 def _check_parent_id(self):
     for employee in self:
         if not employee._check_recursion():
             raise ValidationError(
                 _('Error! You cannot create recursive hierarchy of Employee(s).'
                   ))
Exemplo n.º 5
0
 def action_repair_cancel_draft(self):
     if self.filtered(lambda repair: repair.state != 'cancel'):
         raise UserError(_("Repair must be canceled in order to reset it to draft."))
     self.mapped('operations').write({'state': 'draft'})
     return self.write({'state': 'draft'})
Exemplo n.º 6
0
 def check_parent_id(self):
     if not self._check_recursion():
         raise ValueError(
             _('Error ! You cannot create recursive categories.'))
Exemplo n.º 7
0
 def _validate_tag_country(self):
     for record in self:
         if record.applicability == 'taxes' and not record.country_id:
             raise ValidationError(
                 _("A tag defined to be used on taxes must always have a country set."
                   ))
Exemplo n.º 8
0
class PaymentAcquirer(models.Model):
    """ Acquirer Model. Each specific acquirer can extend the model by adding
    its own fields, using the acquirer_name as a prefix for the new fields.
    Using the required_if_provider='<name>' attribute on fields it is possible
    to have required fields that depend on a specific acquirer.

    Each acquirer has a link to an ir.ui.view record that is a template of
    a button used to display the payment form. See examples in ``payment_ogone``
    and ``payment_paypal`` modules.

    Methods that should be added in an acquirer-specific implementation:

     - ``<name>_form_generate_values(self, reference, amount, currency,
       partner_id=False, partner_values=None, tx_custom_values=None)``:
       method that generates the values used to render the form button template.
     - ``<name>_get_form_action_url(self):``: method that returns the url of
       the button form. It is used for example in ecommerce application if you
       want to post some data to the acquirer.
     - ``<name>_compute_fees(self, amount, currency_id, country_id)``: computes
       the fees of the acquirer, using generic fields defined on the acquirer
       model (see fields definition).

    Each acquirer should also define controllers to handle communication between
    OpenERP and the acquirer. It generally consists in return urls given to the
    button form and that the acquirer uses to send the customer back after the
    transaction, with transaction details given as a POST request.
    """
    _name = 'payment.acquirer'
    _description = 'Payment Acquirer'
    _order = 'website_published desc, sequence, name'

    name = fields.Char('Name', required=True, translate=True)
    description = fields.Html('Description')
    sequence = fields.Integer('Sequence',
                              default=10,
                              help="Determine the display order")
    provider = fields.Selection(selection=[('manual', 'Manual Configuration')],
                                string='Provider',
                                default='manual',
                                required=True)
    company_id = fields.Many2one(
        'res.company',
        'Company',
        default=lambda self: self.env.user.company_id.id,
        required=True)
    view_template_id = fields.Many2one('ir.ui.view',
                                       'Form Button Template',
                                       required=True)
    registration_view_template_id = fields.Many2one(
        'ir.ui.view',
        'S2S Form Template',
        domain=[('type', '=', 'qweb')],
        help="Template for method registration")
    environment = fields.Selection([('test', 'Test'), ('prod', 'Production')],
                                   string='Environment',
                                   default='test',
                                   oldname='env',
                                   required=True)
    website_published = fields.Boolean(
        'Visible in Portal / Website',
        copy=False,
        help="Make this payment acquirer available (Customer invoices, etc.)")
    # Formerly associated to `authorize` option from auto_confirm
    capture_manually = fields.Boolean(
        string="Capture Amount Manually",
        help="Capture the amount from Flectra, when the delivery is completed."
    )
    # Formerly associated to `generate_and_pay_invoice` option from auto_confirm
    journal_id = fields.Many2one(
        'account.journal',
        'Payment Journal',
        domain=[('type', 'in', ['bank', 'cash'])],
        default=lambda self: self.env['account.journal'].search(
            [('type', 'in', ['bank', 'cash'])], limit=1),
        help=
        """Payments will be registered into this journal. If you get paid straight on your bank account,
                select your bank account. If you get paid in batch for several transactions, create a specific
                payment journal for this payment acquirer to easily manage the bank reconciliation. You hold
                the amount in a temporary transfer account of your books (created automatically when you create
                the payment journal). Then when you get paid on your bank account by the payment acquirer, you
                reconcile the bank statement line with this temporary transfer account. Use reconciliation
                templates to do it in one-click.""")
    specific_countries = fields.Boolean(
        string="Specific Countries",
        help=
        "If you leave it empty, the payment acquirer will be available for all the countries."
    )
    country_ids = fields.Many2many(
        'res.country',
        'payment_country_rel',
        'payment_id',
        'country_id',
        'Countries',
        help=
        "This payment gateway is available for selected countries. If none is selected it is available for all countries."
    )

    pre_msg = fields.Html(
        'Help Message',
        translate=True,
        help='Message displayed to explain and help the payment process.')
    post_msg = fields.Html(
        'Thanks Message',
        translate=True,
        help='Message displayed after having done the payment process.')
    pending_msg = fields.Html(
        'Pending Message',
        translate=True,
        default=lambda s:
        _('<i>Pending,</i> Your online payment has been successfully processed. But your order is not validated yet.'
          ),
        help=
        'Message displayed, if order is in pending state after having done the payment process.'
    )
    done_msg = fields.Html(
        'Done Message',
        translate=True,
        default=lambda s:
        _('<i>Done,</i> Your online payment has been successfully processed. Thank you for your order.'
          ),
        help=
        'Message displayed, if order is done successfully after having done the payment process.'
    )
    cancel_msg = fields.Html(
        'Cancel Message',
        translate=True,
        default=lambda s: _('<i>Cancel,</i> Your payment has been cancelled.'),
        help='Message displayed, if order is cancel during the payment process.'
    )
    error_msg = fields.Html(
        'Error Message',
        translate=True,
        default=lambda s:
        _('<i>Error,</i> Please be aware that an error occurred during the transaction. The order has been confirmed but will not be paid. Do not hesitate to contact us if you have any questions on the status of your order.'
          ),
        help='Message displayed, if error is occur during the payment process.'
    )
    save_token = fields.Selection(
        [('none', 'Never'), ('ask', 'Let the customer decide'),
         ('always', 'Always')],
        string='Save Cards',
        default='none',
        help=
        "This option allows customers to save their credit card as a payment token and to reuse it for a later purchase. "
        "If you manage subscriptions (recurring invoicing), you need it to automatically charge the customer when you "
        "issue an invoice.")
    token_implemented = fields.Boolean('Saving Card Data supported',
                                       compute='_compute_feature_support',
                                       search='_search_is_tokenized')
    authorize_implemented = fields.Boolean('Authorize Mechanism Supported',
                                           compute='_compute_feature_support')
    fees_implemented = fields.Boolean('Fees Computation Supported',
                                      compute='_compute_feature_support')
    fees_active = fields.Boolean('Add Extra Fees')
    fees_dom_fixed = fields.Float('Fixed domestic fees')
    fees_dom_var = fields.Float('Variable domestic fees (in percents)')
    fees_int_fixed = fields.Float('Fixed international fees')
    fees_int_var = fields.Float('Variable international fees (in percents)')

    # TDE FIXME: remove that brol
    module_id = fields.Many2one('ir.module.module',
                                string='Corresponding Module')
    module_state = fields.Selection(selection=module.STATES,
                                    string='Installation State',
                                    related='module_id.state')

    image = fields.Binary(
        "Image",
        attachment=True,
        help=
        "This field holds the image used for this provider, limited to 1024x1024px"
    )
    image_medium = fields.Binary(
        "Medium-sized image",
        attachment=True,
        help="Medium-sized image of this provider. It is automatically "
        "resized as a 128x128px image, with aspect ratio preserved. "
        "Use this field in form views or some kanban views.")
    image_small = fields.Binary(
        "Small-sized image",
        attachment=True,
        help="Small-sized image of this provider. It is automatically "
        "resized as a 64x64px image, with aspect ratio preserved. "
        "Use this field anywhere a small image is required.")

    payment_icon_ids = fields.Many2many('payment.icon',
                                        string='Supported Payment Icons')
    payment_flow = fields.Selection(
        selection=[('form', 'Redirection to the acquirer website'),
                   ('s2s', 'Payment from Odoo')],
        default='form',
        required=True,
        string='Payment Flow',
        help=
        """Note: Subscriptions does not take this field in account, it uses server to server by default."""
    )

    def _search_is_tokenized(self, operator, value):
        tokenized = self._get_feature_support()['tokenize']
        if (operator, value) in [('=', True), ('!=', False)]:
            return [('provider', 'in', tokenized)]
        return [('provider', 'not in', tokenized)]

    @api.multi
    def _compute_feature_support(self):
        feature_support = self._get_feature_support()
        for acquirer in self:
            acquirer.fees_implemented = acquirer.provider in feature_support[
                'fees']
            acquirer.authorize_implemented = acquirer.provider in feature_support[
                'authorize']
            acquirer.token_implemented = acquirer.provider in feature_support[
                'tokenize']

    @api.multi
    def _check_required_if_provider(self):
        """ If the field has 'required_if_provider="<provider>"' attribute, then it
        required if record.provider is <provider>. """
        for acquirer in self:
            if any(
                    getattr(f, 'required_if_provider', None) ==
                    acquirer.provider and not acquirer[k]
                    for k, f in self._fields.items()):
                return False
        return True

    _constraints = [
        (_check_required_if_provider, 'Required fields not filled', []),
    ]

    def _get_feature_support(self):
        """Get advanced feature support by provider.

        Each provider should add its technical in the corresponding
        key for the following features:
            * fees: support payment fees computations
            * authorize: support authorizing payment (separates
                         authorization and capture)
            * tokenize: support saving payment data in a payment.tokenize
                        object
        """
        return dict(authorize=[], tokenize=[], fees=[])

    @api.model
    def create(self, vals):
        image_resize_images(vals)
        return super(PaymentAcquirer, self).create(vals)

    @api.multi
    def write(self, vals):
        image_resize_images(vals)
        return super(PaymentAcquirer, self).write(vals)

    @api.multi
    def toggle_website_published(self):
        self.write({'website_published': not self.website_published})
        return True

    @api.multi
    def get_form_action_url(self):
        """ Returns the form action URL, for form-based acquirer implementations. """
        if hasattr(self, '%s_get_form_action_url' % self.provider):
            return getattr(self, '%s_get_form_action_url' % self.provider)()
        return False

    def _get_available_payment_input(self, partner=None, company=None):
        """ Generic (model) method that fetches available payment mechanisms
        to use in all portal / eshop pages that want to use the payment form.

        It contains

         * form_acquirers: record set of acquirers based on a local form that
                           sends customer to the acquirer website;
         * s2s_acquirers: reset set of acquirers that send customer data to
                          acquirer without redirecting to any other website;
         * pms: record set of stored credit card data (aka payment.token)
                connected to a given partner to allow customers to reuse them """
        if not company:
            company = self.env.user.company_id
        if not partner:
            partner = self.env.user.partner_id
        active_acquirers = self.sudo().search([
            ('website_published', '=', True), ('company_id', '=', company.id)
        ])
        form_acquirers = active_acquirers.filtered(
            lambda acq: acq.payment_flow == 'form' and acq.view_template_id)
        s2s_acquirers = active_acquirers.filtered(
            lambda acq: acq.payment_flow == 's2s' and acq.
            registration_view_template_id)
        return {
            'form_acquirers':
            form_acquirers,
            's2s_acquirers':
            s2s_acquirers,
            'pms':
            self.env['payment.token'].search([('partner_id', '=', partner.id),
                                              ('acquirer_id', 'in',
                                               s2s_acquirers.ids)]),
        }

    @api.multi
    def render(self,
               reference,
               amount,
               currency_id,
               partner_id=False,
               values=None):
        """ Renders the form template of the given acquirer as a qWeb template.
        :param string reference: the transaction reference
        :param float amount: the amount the buyer has to pay
        :param currency_id: currency id
        :param dict partner_id: optional partner_id to fill values
        :param dict values: a dictionary of values for the transction that is
        given to the acquirer-specific method generating the form values

        All templates will receive:

         - acquirer: the payment.acquirer browse record
         - user: the current user browse record
         - currency_id: id of the transaction currency
         - amount: amount of the transaction
         - reference: reference of the transaction
         - partner_*: partner-related values
         - partner: optional partner browse record
         - 'feedback_url': feedback URL, controler that manage answer of the acquirer (without base url) -> FIXME
         - 'return_url': URL for coming back after payment validation (wihout base url) -> FIXME
         - 'cancel_url': URL if the client cancels the payment -> FIXME
         - 'error_url': URL if there is an issue with the payment -> FIXME
         - context: Flectra context

        """
        if values is None:
            values = {}

        # reference and amount
        values.setdefault('reference', reference)
        amount = float_round(amount, 2)
        values.setdefault('amount', amount)

        # currency id
        currency_id = values.setdefault('currency_id', currency_id)
        if currency_id:
            currency = self.env['res.currency'].browse(currency_id)
        else:
            currency = self.env.user.company_id.currency_id
        values['currency'] = currency

        # Fill partner_* using values['partner_id'] or partner_id argument
        partner_id = values.get('partner_id', partner_id)
        billing_partner_id = values.get('billing_partner_id', partner_id)
        if partner_id:
            partner = self.env['res.partner'].browse(partner_id)
            if partner_id != billing_partner_id:
                billing_partner = self.env['res.partner'].browse(
                    billing_partner_id)
            else:
                billing_partner = partner
            values.update({
                'partner':
                partner,
                'partner_id':
                partner_id,
                'partner_name':
                partner.name,
                'partner_lang':
                partner.lang,
                'partner_email':
                partner.email,
                'partner_zip':
                partner.zip,
                'partner_city':
                partner.city,
                'partner_address':
                _partner_format_address(partner.street, partner.street2),
                'partner_country_id':
                partner.country_id.id,
                'partner_country':
                partner.country_id,
                'partner_phone':
                partner.phone,
                'partner_state':
                partner.state_id,
                'billing_partner':
                billing_partner,
                'billing_partner_id':
                billing_partner_id,
                'billing_partner_name':
                billing_partner.name,
                'billing_partner_commercial_company_name':
                billing_partner.commercial_company_name,
                'billing_partner_lang':
                billing_partner.lang,
                'billing_partner_email':
                billing_partner.email,
                'billing_partner_zip':
                billing_partner.zip,
                'billing_partner_city':
                billing_partner.city,
                'billing_partner_address':
                _partner_format_address(billing_partner.street,
                                        billing_partner.street2),
                'billing_partner_country_id':
                billing_partner.country_id.id,
                'billing_partner_country':
                billing_partner.country_id,
                'billing_partner_phone':
                billing_partner.phone,
                'billing_partner_state':
                billing_partner.state_id,
            })
        if values.get('partner_name'):
            values.update({
                'partner_first_name':
                _partner_split_name(values.get('partner_name'))[0],
                'partner_last_name':
                _partner_split_name(values.get('partner_name'))[1],
            })
        if values.get('billing_partner_name'):
            values.update({
                'billing_partner_first_name':
                _partner_split_name(values.get('billing_partner_name'))[0],
                'billing_partner_last_name':
                _partner_split_name(values.get('billing_partner_name'))[1],
            })

        # Fix address, country fields
        if not values.get('partner_address'):
            values['address'] = _partner_format_address(
                values.get('partner_street', ''),
                values.get('partner_street2', ''))
        if not values.get('partner_country') and values.get(
                'partner_country_id'):
            values['country'] = self.env['res.country'].browse(
                values.get('partner_country_id'))
        if not values.get('billing_partner_address'):
            values['billing_address'] = _partner_format_address(
                values.get('billing_partner_street', ''),
                values.get('billing_partner_street2', ''))
        if not values.get('billing_partner_country') and values.get(
                'billing_partner_country_id'):
            values['billing_country'] = self.env['res.country'].browse(
                values.get('billing_partner_country_id'))

        # compute fees
        fees_method_name = '%s_compute_fees' % self.provider
        if hasattr(self, fees_method_name):
            fees = getattr(self,
                           fees_method_name)(values['amount'],
                                             values['currency_id'],
                                             values.get('partner_country_id'))
            values['fees'] = float_round(fees, 2)

        # call <name>_form_generate_values to update the tx dict with acqurier specific values
        cust_method_name = '%s_form_generate_values' % (self.provider)
        if hasattr(self, cust_method_name):
            method = getattr(self, cust_method_name)
            values = method(values)

        values.update({
            'tx_url':
            self._context.get('tx_url', self.get_form_action_url()),
            'submit_class':
            self._context.get('submit_class', 'btn btn-link'),
            'submit_txt':
            self._context.get('submit_txt'),
            'acquirer':
            self,
            'user':
            self.env.user,
            'context':
            self._context,
            'type':
            values.get('type') or 'form',
        })
        values.setdefault('return_url', False)

        return self.view_template_id.render(values, engine='ir.qweb')

    def get_s2s_form_xml_id(self):
        if self.registration_view_template_id:
            model_data = self.env['ir.model.data'].search([
                ('model', '=', 'ir.ui.view'),
                ('res_id', '=', self.registration_view_template_id.id)
            ])
            return ('%s.%s') % (model_data.module, model_data.name)
        return False

    @api.multi
    def s2s_process(self, data):
        cust_method_name = '%s_s2s_form_process' % (self.provider)
        if not self.s2s_validate(data):
            return False
        if hasattr(self, cust_method_name):
            # As this method may be called in JSON and overriden in various addons
            # let us raise interesting errors before having stranges crashes
            if not data.get('partner_id'):
                raise ValueError(
                    _('Missing partner reference when trying to create a new payment token'
                      ))
            method = getattr(self, cust_method_name)
            return method(data)
        return True

    @api.multi
    def s2s_validate(self, data):
        cust_method_name = '%s_s2s_form_validate' % (self.provider)
        if hasattr(self, cust_method_name):
            method = getattr(self, cust_method_name)
            return method(data)
        return True

    @api.multi
    def toggle_environment_value(self):
        prod = self.filtered(lambda acquirer: acquirer.environment == 'prod')
        prod.write({'environment': 'test'})
        (self - prod).write({'environment': 'prod'})

    @api.multi
    def button_immediate_install(self):
        # TDE FIXME: remove that brol
        if self.module_id and self.module_state != 'installed':
            self.module_id.button_immediate_install()
            return {
                'type': 'ir.actions.client',
                'tag': 'reload',
            }
Exemplo n.º 9
0
 def action_void(self):
     if any(self.mapped(lambda tx: tx.state != 'authorized')):
         raise ValidationError(
             _('Only transactions in the Authorized status can be voided.'))
     for tx in self:
         tx.s2s_void_transaction()
Exemplo n.º 10
0
 def create(self, data):
     vehicle = super(FleetVehicle, self.with_context(mail_create_nolog=True)).create(data)
     vehicle.message_post(body=_('%s %s has been added to the fleet!') % (vehicle.model_id.name, vehicle.license_plate))
     return vehicle
Exemplo n.º 11
0
 def _compute_vehicle_name(self):
     for record in self:
         record.name = record.model_id.brand_id.name + '/' + record.model_id.name + '/' + (record.license_plate or _('No Plate'))
Exemplo n.º 12
0
 def _check_amount(self):
     if self.amount < 0:
         raise ValidationError(_('The payment amount cannot be negative.'))
Exemplo n.º 13
0
 def check_user(self, uid=None):
     if uid is None:
         uid = request.uid
     is_admin = request.env['res.users'].browse(uid)._is_admin()
     if not is_admin:
         raise AccessError(_("Only administrators can upload a module"))
Exemplo n.º 14
0
 def fields_view_get(self,
                     view_id=None,
                     view_type='form',
                     toolbar=False,
                     submenu=False):
     state = self.env['ir.model.fields.anonymization']._get_global_state()
     step = self.env.context.get('step', 'new_window')
     res = super(IrModelFieldsAnonymizeWizard,
                 self).fields_view_get(view_id=view_id,
                                       view_type=view_type,
                                       toolbar=toolbar,
                                       submenu=submenu)
     eview = etree.fromstring(res['arch'])
     placeholder = eview.xpath("group[@name='placeholder1']")
     if len(placeholder):
         placeholder = placeholder[0]
         if step == 'new_window' and state == 'clear':
             # clicked in the menu and the fields are not anonymized: warn the admin that backuping the db is very important
             placeholder.addnext(
                 etree.Element('field', {
                     'name': 'msg',
                     'colspan': '4',
                     'nolabel': '1'
                 }))
             placeholder.addnext(etree.Element('newline'))
             placeholder.addnext(
                 etree.Element('label', {'string': 'Warning'}))
             eview.remove(placeholder)
         elif step == 'new_window' and state == 'anonymized':
             # clicked in the menu and the fields are already anonymized
             placeholder.addnext(etree.Element('newline'))
             placeholder.addnext(
                 etree.Element('field', {
                     'name': 'file_import',
                     'required': "1"
                 }))
             placeholder.addnext(
                 etree.Element('label', {'string': 'Anonymization file'}))
             eview.remove(placeholder)
         elif step == 'just_anonymized':
             # we just ran the anonymization process, we need the file export field
             placeholder.addnext(etree.Element('newline'))
             placeholder.addnext(
                 etree.Element('field', {'name': 'file_export'}))
             # we need to remove the button:
             buttons = eview.xpath("button")
             for button in buttons:
                 eview.remove(button)
             # and add a message:
             placeholder.addnext(
                 etree.Element('field', {
                     'name': 'msg',
                     'colspan': '4',
                     'nolabel': '1'
                 }))
             placeholder.addnext(etree.Element('newline'))
             placeholder.addnext(
                 etree.Element('label', {'string': 'Result'}))
             # remove the placeholer:
             eview.remove(placeholder)
         elif step == 'just_desanonymized':
             # we just reversed the anonymization process, we don't need any field
             # we need to remove the button
             buttons = eview.xpath("button")
             for button in buttons:
                 eview.remove(button)
             # and add a message
             placeholder.addnext(
                 etree.Element('field', {
                     'name': 'msg',
                     'colspan': '4',
                     'nolabel': '1'
                 }))
             placeholder.addnext(etree.Element('newline'))
             placeholder.addnext(
                 etree.Element('label', {'string': 'Result'}))
             # remove the placeholer:
             eview.remove(placeholder)
         else:
             raise UserError(
                 _("The database anonymization is currently in an unstable state. Some fields are anonymized,"
                   " while some fields are not anonymized. You should try to solve this problem before trying to do anything else."
                   ))
         res['arch'] = etree.tostring(eview, encoding='unicode')
     return res
Exemplo n.º 15
0
    def execute(self):
        self.ensure_one()

        # Multi Website: Do not allow more than 1 website as default website
        if self.env['website'].search_count(
                [('is_default_website', '=', True)]) > 1:
            raise Warning(
                _('You can define only one website as default one.\n'
                  'More than one websites are not allowed '
                  'as default website.'))

        if not self.env.user._is_superuser() and not \
                self.env.user.has_group('base.group_system'):
            raise AccessError(_("Only administrators can change the settings"))

        self = self.with_context(active_test=False)
        classified = self._get_classified_fields()

        # default values fields
        IrDefault = self.env['ir.default'].sudo()
        for name, model, field in classified['default']:
            if isinstance(self[name], models.BaseModel):
                if self._fields[name].type == 'many2one':
                    value = self[name].id
                else:
                    value = self[name].ids
            else:
                value = self[name]
            IrDefault.set(model, field, value)

        # group fields: modify group / implied groups
        for name, groups, implied_group in classified['group']:
            if self[name]:
                groups.write({'implied_ids': [(4, implied_group.id)]})
            else:
                groups.write({'implied_ids': [(3, implied_group.id)]})
                implied_group.write({'users': [(3, user.id) for user in
                                               groups.mapped('users')]})

        # other fields: execute method 'set_values'
        # Methods that start with `set_` are now deprecated
        for method in dir(self):
            if method.startswith('set_') and method is not 'set_values':
                _logger.warning(_('Methods that start with `set_` '
                                  'are deprecated. Override `set_values` '
                                  'instead (Method %s)') % method)
        self.set_values()

        # module fields: install/uninstall the selected modules
        to_install = []
        to_upgrade = self.env['ir.module.module']
        to_uninstall_modules = self.env['ir.module.module']
        lm = len('module_')
        for name, module in classified['module']:
            if self[name]:
                to_install.append((name[lm:], module))
            else:
                if module and module.state in ('installed', 'to upgrade'):
                    to_uninstall_modules += module

        if 'install_theme' in classified and 'uninstall_theme' in classified:
            for theme in classified['install_theme']:
                if theme:
                    to_install.append((theme.name, theme))
                if theme.state == 'installed':
                    to_upgrade += theme
            for theme in classified['uninstall_theme']:
                if theme and theme.state in ('installed', 'to upgrade'):
                    to_uninstall_modules += theme

        if to_uninstall_modules:
            to_uninstall_modules.button_immediate_uninstall()

        if to_upgrade:
            to_upgrade.button_immediate_upgrade()

        self._install_modules(to_install)

        if to_install or to_uninstall_modules:
            # After the uninstall/install calls, the registry and environments
            # are no longer valid. So we reset the environment.
            self.env.reset()
            self = self.env()[self._name]

        # pylint: disable=next-method-called
        config = self.env['res.config'].next() or {}
        if config.get('type') not in ('ir.actions.act_window_close',):
            return config

        # force client-side reload (update user menu and current view)
        return {
            'type': 'ir.actions.client',
            'tag': 'reload',
        }
Exemplo n.º 16
0
    def _get_intrastat_linekey(self, declcode, inv_line, dispatchmode,
                               extendedmode):
        IntrastatRegion = self.env['l10n_be_intrastat.region']
        company = self.company_id

        #Check type of transaction
        if inv_line.intrastat_transaction_id:
            extta = inv_line.intrastat_transaction_id.code
        else:
            extta = "1"
        #Check country
        if inv_line.invoice_id.intrastat_country_id:
            excnt = inv_line.invoice_id.intrastat_country_id.code
        else:
            excnt = inv_line.invoice_id.partner_shipping_id.country_id.code or inv_line.invoice_id.partner_id.country_id.code

        #Check region
        #If purchase, comes from purchase order, linked to a location,
        #which is linked to the warehouse
        #if sales, the sales order is linked to the warehouse
        #if sales, from a delivery order, linked to a location,
        #which is linked to the warehouse
        #If none found, get the company one.
        exreg = None
        if inv_line.invoice_id.type in ('in_invoice', 'in_refund'):
            #comes from purchase
            po_lines = self.env['purchase.order.line'].search(
                [('invoice_lines', 'in', inv_line.id)], limit=1)
            if po_lines:
                if self._is_situation_triangular(company, po_line=po_lines):
                    return
                location = self.env['stock.location'].browse(
                    po_lines.order_id._get_destination_location())
                region_id = self.env[
                    'stock.warehouse'].get_regionid_from_locationid(location)
                if region_id:
                    exreg = IntrastatRegion.browse(region_id).code
        elif inv_line.invoice_id.type in ('out_invoice', 'out_refund'):
            #comes from sales
            so_lines = self.env['sale.order.line'].search(
                [('invoice_lines', 'in', inv_line.id)], limit=1)
            if so_lines:
                if self._is_situation_triangular(company, so_line=so_lines):
                    return
                saleorder = so_lines.order_id
                if saleorder and saleorder.warehouse_id and saleorder.warehouse_id.region_id:
                    exreg = IntrastatRegion.browse(
                        saleorder.warehouse_id.region_id.id).code

        if not exreg:
            if company.region_id:
                exreg = company.region_id.code
            else:
                self._company_warning(
                    _('The Intrastat Region of the selected company is not set, '
                      'please make sure to configure it first.'))

        #Check commodity codes
        intrastat_id = inv_line.product_id.get_intrastat_recursively()
        if intrastat_id:
            exgo = self.env['report.intrastat.code'].browse(intrastat_id).name
        else:
            raise exceptions.Warning(
                _('Product "%s" has no intrastat code, please configure it') %
                inv_line.product_id.display_name)

        #In extended mode, 2 more fields required
        if extendedmode:
            #Check means of transport
            if inv_line.invoice_id.transport_mode_id:
                extpc = inv_line.invoice_id.transport_mode_id.code
            elif company.transport_mode_id:
                extpc = company.transport_mode_id.code
            else:
                self._company_warning(
                    _('The default Intrastat transport mode of your company '
                      'is not set, please make sure to configure it first.'))

            #Check incoterm
            if inv_line.invoice_id.incoterm_id:
                exdeltrm = inv_line.invoice_id.incoterm_id.code
            elif company.incoterm_id:
                exdeltrm = company.incoterm_id.code
            else:
                self._company_warning(
                    _('The default Incoterm of your company is not set, '
                      'please make sure to configure it first.'))
        else:
            extpc = ""
            exdeltrm = ""
        intrastatkey = namedtuple(
            "intrastatkey",
            ['EXTRF', 'EXCNT', 'EXTTA', 'EXREG', 'EXGO', 'EXTPC', 'EXDELTRM'])
        return intrastatkey(EXTRF=declcode,
                            EXCNT=excnt,
                            EXTTA=extta,
                            EXREG=exreg,
                            EXGO=exgo,
                            EXTPC=extpc,
                            EXDELTRM=exdeltrm)
Exemplo n.º 17
0
 def _check_date(self):
     if self.date_from > self.date_to:
         raise ValueError(_("End Date must be greater than " "Start Date!"))
Exemplo n.º 18
0
 def _company_warning(self, translated_msg):
     """ Raise a error with custom message, asking user to configure company settings """
     raise exceptions.RedirectWarning(
         translated_msg,
         self.env.ref('base.action_res_company_form').id,
         _('Go to company configuration screen'))
Exemplo n.º 19
0
    def _sale_create_reinvoice_sale_line(self):

        sale_order_map = self._sale_determine_order()

        sale_line_values_to_create = [
        ]  # the list of creation values of sale line to create.
        existing_sale_line_cache = {
        }  # in the sales_price-delivery case, we can reuse the same sale line. This cache will avoid doing a search each time the case happen
        # `map_move_sale_line` is map where
        #   - key is the move line identifier
        #   - value is either a sale.order.line record (existing case), or an integer representing the index of the sale line to create in
        #     the `sale_line_values_to_create` (not existing case, which will happen more often than the first one).
        map_move_sale_line = {}

        for move_line in self:
            sale_order = sale_order_map.get(move_line.id)

            # no reinvoice as no sales order was found
            if not sale_order:
                continue

            # raise if the sale order is not currenlty open
            if sale_order.state != 'sale':
                message_unconfirmed = _(
                    'The Sales Order %s linked to the Analytic Account %s must be validated before registering expenses.'
                )
                messages = {
                    'draft':
                    message_unconfirmed,
                    'sent':
                    message_unconfirmed,
                    'done':
                    _('The Sales Order %s linked to the Analytic Account %s is currently locked. You cannot register an expense on a locked Sales Order. Please create a new SO linked to this Analytic Account.'
                      ),
                    'cancel':
                    _('The Sales Order %s linked to the Analytic Account %s is cancelled. You cannot register an expense on a cancelled Sales Order.'
                      ),
                }
                raise UserError(
                    messages[sale_order.state] %
                    (sale_order.name, sale_order.analytic_account_id.name))

            price = move_line._sale_get_invoice_price(sale_order)

            # find the existing sale.line or keep its creation values to process this in batch
            sale_line = None
            if move_line.product_id.expense_policy == 'sales_price' and move_line.product_id.invoice_policy == 'delivery':  # for those case only, we can try to reuse one
                map_entry_key = (sale_order.id, move_line.product_id.id, price
                                 )  # cache entry to limit the call to search
                sale_line = existing_sale_line_cache.get(map_entry_key)
                if sale_line:  # already search, so reuse it. sale_line can be sale.order.line record or index of a "to create values" in `sale_line_values_to_create`
                    map_move_sale_line[move_line.id] = sale_line
                    existing_sale_line_cache[map_entry_key] = sale_line
                else:  # search for existing sale line
                    sale_line = self.env['sale.order.line'].search([
                        ('order_id', '=', sale_order.id),
                        ('price_unit', '=', price),
                        ('product_id', '=', move_line.product_id.id),
                        ('is_expense', '=', True),
                    ],
                                                                   limit=1)
                    if sale_line:  # found existing one, so keep the browse record
                        map_move_sale_line[
                            move_line.id] = existing_sale_line_cache[
                                map_entry_key] = sale_line
                    else:  # should be create, so use the index of creation values instead of browse record
                        # save value to create it
                        sale_line_values_to_create.append(
                            move_line._sale_prepare_sale_line_values(
                                sale_order, price))
                        # store it in the cache of existing ones
                        existing_sale_line_cache[map_entry_key] = len(
                            sale_line_values_to_create
                        ) - 1  # save the index of the value to create sale line
                        # store it in the map_move_sale_line map
                        map_move_sale_line[move_line.id] = len(
                            sale_line_values_to_create
                        ) - 1  # save the index of the value to create sale line

            else:  # save its value to create it anyway
                sale_line_values_to_create.append(
                    move_line._sale_prepare_sale_line_values(
                        sale_order, price))
                map_move_sale_line[move_line.id] = len(
                    sale_line_values_to_create
                ) - 1  # save the index of the value to create sale line

        # create the sale lines in batch
        new_sale_lines = self.env['sale.order.line'].create(
            sale_line_values_to_create)
        new_sale_lines._onchange_discount()

        # build result map by replacing index with newly created record of sale.order.line
        result = {}
        for move_line_id, unknown_sale_line in map_move_sale_line.items():
            if isinstance(unknown_sale_line,
                          int):  # index of newly created sale line
                result[move_line_id] = new_sale_lines[unknown_sale_line]
            elif isinstance(
                    unknown_sale_line,
                    models.BaseModel):  # already record of sale.order.line
                result[move_line_id] = unknown_sale_line
        return result
Exemplo n.º 20
0
    def create_xml(self):
        """Creates xml that is to be exported and sent to estate for partner vat intra.
        :return: Value for next action.
        :rtype: dict
        """
        self.ensure_one()
        company = self.company_id
        if not (company.partner_id and company.partner_id.country_id
                and company.partner_id.country_id.id):
            self._company_warning(
                _('The country of your company is not set, '
                  'please make sure to configure it first.'))
        if not company.company_registry:
            self._company_warning(
                _('The registry number of your company is not set, '
                  'please make sure to configure it first.'))
        if len(self.year) != 4:
            raise exceptions.Warning(_('Year must be 4 digits number (YYYY)'))

        #Create root declaration
        decl = ET.Element('DeclarationReport')
        decl.set('xmlns', INTRASTAT_XMLNS)

        #Add Administration elements
        admin = ET.SubElement(decl, 'Administration')
        fromtag = ET.SubElement(admin, 'From')
        fromtag.text = company.company_registry
        fromtag.set('declarerType', 'KBO')
        ET.SubElement(admin, 'To').text = "NBB"
        ET.SubElement(admin, 'Domain').text = "SXX"
        if self.arrivals == 'be-standard':
            decl.append(self.sudo()._get_lines(dispatchmode=False,
                                               extendedmode=False))
        elif self.arrivals == 'be-extended':
            decl.append(self.sudo()._get_lines(dispatchmode=False,
                                               extendedmode=True))
        if self.dispatches == 'be-standard':
            decl.append(self.sudo()._get_lines(dispatchmode=True,
                                               extendedmode=False))
        elif self.dispatches == 'be-extended':
            decl.append(self.sudo()._get_lines(dispatchmode=True,
                                               extendedmode=True))

        #Get xml string with declaration
        data_file = ET.tostring(decl, encoding='UTF-8', method='xml')

        #change state of the wizard
        self.write({
            'name': 'intrastat_%s%s.xml' % (self.year, self.month),
            'file_save': base64.encodestring(data_file),
            'state': 'download'
        })
        return {
            'name': _('Save'),
            'view_type': 'form',
            'view_mode': 'form',
            'res_model': 'l10n_be_intrastat_xml.xml_decl',
            'type': 'ir.actions.act_window',
            'target': 'new',
            'res_id': self.id,
        }
Exemplo n.º 21
0
 def copy(self, default=None):
     self.ensure_one()
     default = dict(default or {})
     if 'name' not in default:
         default['name'] = _("%s (copy)") % (self.name)
     return super(Job, self).copy(default=default)
Exemplo n.º 22
0
 def write(self, values):
     if ('date_planned_start' in values or 'date_planned_finished'
             in values) and any(workorder.state == 'done'
                                for workorder in self):
         raise UserError(_('You can not change the finished work order.'))
     return super(MrpWorkorder, self).write(values)
Exemplo n.º 23
0
 def _check_parent_id(self):
     if not self._check_recursion():
         raise ValidationError(
             _('Error! You cannot create recursive departments.'))
Exemplo n.º 24
0
    def record_production(self):
        self.ensure_one()
        if self.qty_producing <= 0:
            raise UserError(
                _('Please set the quantity you are currently producing. It should be different from zero.'
                  ))

        if (self.production_id.product_id.tracking !=
                'none') and not self.final_lot_id:
            raise UserError(
                _('You should provide a lot/serial number for the final product'
                  ))

        # Update quantities done on each raw material line
        # For each untracked component without any 'temporary' move lines,
        # (the new workorder tablet view allows registering consumed quantities for untracked components)
        # we assume that only the theoretical quantity was used
        for move in self.move_raw_ids:
            if move.has_tracking == 'none' and (move.state not in ('done', 'cancel')) and move.bom_line_id\
                        and move.unit_factor and not move.move_line_ids.filtered(lambda ml: not ml.done_wo):
                rounding = move.product_uom.rounding
                if self.product_id.tracking != 'none':
                    qty_to_add = float_round(self.qty_producing *
                                             move.unit_factor,
                                             precision_rounding=rounding)
                    move._generate_consumed_move_line(qty_to_add,
                                                      self.final_lot_id)
                else:
                    move.quantity_done += float_round(
                        self.qty_producing * move.unit_factor,
                        precision_rounding=rounding)

        # Transfer quantities from temporary to final move lots or make them final
        for move_line in self.active_move_line_ids:
            # Check if move_line already exists
            if move_line.qty_done <= 0:  # rounding...
                move_line.sudo().unlink()
                continue
            if move_line.product_id.tracking != 'none' and not move_line.lot_id:
                raise UserError(
                    _('You should provide a lot/serial number for a component')
                )
            # Search other move_line where it could be added:
            lots = self.move_line_ids.filtered(
                lambda x: (x.lot_id.id == move_line.lot_id.id) and
                (not x.lot_produced_id) and (not x.done_move) and
                (x.product_id == move_line.product_id))
            if lots:
                lots[0].qty_done += move_line.qty_done
                lots[0].lot_produced_id = self.final_lot_id.id
                move_line.sudo().unlink()
            else:
                move_line.lot_produced_id = self.final_lot_id.id
                move_line.done_wo = True

        # One a piece is produced, you can launch the next work order
        if self.next_work_order_id.state == 'pending':
            self.next_work_order_id.state = 'ready'

        self.move_line_ids.filtered(
            lambda move_line: not move_line.done_move and not move_line.
            lot_produced_id and move_line.qty_done > 0).write({
                'lot_produced_id':
                self.final_lot_id.id,
                'lot_produced_qty':
                self.qty_producing
            })

        # If last work order, then post lots used
        # TODO: should be same as checking if for every workorder something has been done?
        if not self.next_work_order_id:
            production_move = self.production_id.move_finished_ids.filtered(
                lambda x: (x.product_id.id == self.production_id.product_id.id
                           ) and (x.state not in ('done', 'cancel')))
            if production_move.has_tracking != 'none':
                move_line = production_move.move_line_ids.filtered(
                    lambda x: x.lot_id.id == self.final_lot_id.id)
                if move_line:
                    move_line.product_uom_qty += self.qty_producing
                else:
                    move_line.create({
                        'move_id':
                        production_move.id,
                        'product_id':
                        production_move.product_id.id,
                        'lot_id':
                        self.final_lot_id.id,
                        'product_uom_qty':
                        self.qty_producing,
                        'product_uom_id':
                        production_move.product_uom.id,
                        'qty_done':
                        self.qty_producing,
                        'workorder_id':
                        self.id,
                        'location_id':
                        production_move.location_id.id,
                        'location_dest_id':
                        production_move.location_dest_id.id,
                    })
            else:
                production_move.quantity_done += self.qty_producing
        # Update workorder quantity produced
        self.qty_produced += self.qty_producing

        if self.final_lot_id:
            self.final_lot_id.use_next_on_work_order_id = self.next_work_order_id
            self.final_lot_id = False

        # Set a qty producing
        if self.qty_produced >= self.production_id.product_qty:
            self.qty_producing = 0
        elif self.production_id.product_id.tracking == 'serial':
            self._assign_default_final_lot_id()
            self.qty_producing = 1.0
            self._generate_lot_ids()
        else:
            self.qty_producing = self.production_id.product_qty - self.qty_produced
            self._generate_lot_ids()

        if self.qty_produced >= self.production_id.product_qty:
            if self.next_work_order_id and self.production_id.product_id.tracking != 'none':
                self.next_work_order_id._assign_default_final_lot_id()
            self.button_finish()
        return True
Exemplo n.º 25
0
    def action_invoice_create(self, group=False):
        """ Creates invoice(s) for repair order.
        @param group: It is set to true when group invoice is to be generated.
        @return: Invoice Ids.
        """
        res = dict.fromkeys(self.ids, False)
        invoices_group = {}
        InvoiceLine = self.env['account.invoice.line']
        Invoice = self.env['account.invoice']
        for repair in self.filtered(lambda repair: repair.state not in ('draft', 'cancel') and not repair.invoice_id):
            if not repair.partner_id.id and not repair.partner_invoice_id.id:
                raise UserError(_('You have to select a Partner Invoice Address in the repair form!'))
            comment = repair.quotation_notes
            if repair.invoice_method != 'none':
                if group and repair.partner_invoice_id.id in invoices_group:
                    invoice = invoices_group[repair.partner_invoice_id.id]
                    invoice.write({
                        'name': invoice.name + ', ' + repair.name,
                        'origin': invoice.origin + ', ' + repair.name,
                        'comment': (comment and (invoice.comment and invoice.comment + "\n" + comment or comment)) or (invoice.comment and invoice.comment or ''),
                    })
                else:
                    if not repair.partner_id.property_account_receivable_id:
                        raise UserError(_('No account defined for partner "%s".') % repair.partner_id.name)
                    invoice = Invoice.create({
                        'name': repair.name,
                        'origin': repair.name,
                        'type': 'out_invoice',
                        'account_id': repair.partner_id.property_account_receivable_id.id,
                        'partner_id': repair.partner_invoice_id.id or repair.partner_id.id,
                        'currency_id': repair.pricelist_id.currency_id.id,
                        'comment': repair.quotation_notes,
                        'fiscal_position_id': repair.partner_id.property_account_position_id.id
                    })
                    invoices_group[repair.partner_invoice_id.id] = invoice
                repair.write({'invoiced': True, 'invoice_id': invoice.id})

                for operation in repair.operations:
                    if operation.type == 'add':
                        if group:
                            name = repair.name + '-' + operation.name
                        else:
                            name = operation.name

                        if operation.product_id.property_account_income_id:
                            account_id = operation.product_id.property_account_income_id.id
                        elif operation.product_id.categ_id.property_account_income_categ_id:
                            account_id = operation.product_id.categ_id.property_account_income_categ_id.id
                        else:
                            raise UserError(_('No account defined for product "%s".') % operation.product_id.name)

                        invoice_line = InvoiceLine.create({
                            'invoice_id': invoice.id,
                            'name': name,
                            'origin': repair.name,
                            'account_id': account_id,
                            'quantity': operation.product_uom_qty,
                            'invoice_line_tax_ids': [(6, 0, [x.id for x in operation.tax_id])],
                            'uom_id': operation.product_uom.id,
                            'price_unit': operation.price_unit,
                            'price_subtotal': operation.product_uom_qty * operation.price_unit,
                            'product_id': operation.product_id and operation.product_id.id or False
                        })
                        operation.write({'invoiced': True, 'invoice_line_id': invoice_line.id})
                for fee in repair.fees_lines:
                    if group:
                        name = repair.name + '-' + fee.name
                    else:
                        name = fee.name
                    if not fee.product_id:
                        raise UserError(_('No product defined on Fees!'))

                    if fee.product_id.property_account_income_id:
                        account_id = fee.product_id.property_account_income_id.id
                    elif fee.product_id.categ_id.property_account_income_categ_id:
                        account_id = fee.product_id.categ_id.property_account_income_categ_id.id
                    else:
                        raise UserError(_('No account defined for product "%s".') % fee.product_id.name)

                    invoice_line = InvoiceLine.create({
                        'invoice_id': invoice.id,
                        'name': name,
                        'origin': repair.name,
                        'account_id': account_id,
                        'quantity': fee.product_uom_qty,
                        'invoice_line_tax_ids': [(6, 0, [x.id for x in fee.tax_id])],
                        'uom_id': fee.product_uom.id,
                        'product_id': fee.product_id and fee.product_id.id or False,
                        'price_unit': fee.price_unit,
                        'price_subtotal': fee.product_uom_qty * fee.price_unit
                    })
                    fee.write({'invoiced': True, 'invoice_line_id': invoice_line.id})
                invoice.compute_taxes()
                res[repair.id] = invoice.id
        return res
Exemplo n.º 26
0
 def button_done(self):
     if any([x.state in ('done', 'cancel') for x in self]):
         raise UserError(
             _('A Manufacturing Order is already done or cancelled!'))
     self.end_all()
     return self.write({'state': 'done', 'date_finished': datetime.now()})
Exemplo n.º 27
0
 def constrain_lot_id(self):
     for line in self.filtered(lambda x: x.product_id.tracking != 'none' and not x.lot_id):
         raise ValidationError(_("Serial number is required for operation line with product '%s'") % (line.product_id.name))
Exemplo n.º 28
0
 def get_bban(self):
     if self.acc_type != 'iban':
         raise UserError(_("Cannot compute the BBAN because the account number is not an IBAN."))
     return get_bban_from_iban(self.acc_number)
Exemplo n.º 29
0
    def _send(self,
              auto_commit=False,
              raise_exception=False,
              smtp_session=None):
        IrMailServer = self.env['ir.mail_server']
        for mail_id in self.ids:
            try:
                mail = self.browse(mail_id)
                if mail.state != 'outgoing':
                    if mail.state != 'exception' and mail.auto_delete and \
                                    mail.keep_days < 0:
                        mail.sudo().unlink()
                    continue
                # TDE note: remove me when model_id field is present on mail.message - done here to avoid doing it multiple times in the sub method
                if mail.model:
                    model = self.env['ir.model']._get(mail.model)[0]
                else:
                    model = None
                if model:
                    mail = mail.with_context(model_name=model.name)

                # 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['datas_fname'], base64.b64decode(a['datas']),
                                a['mimetype'])
                               for a in mail.attachment_ids.sudo().read(
                                   ['datas_fname', '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_get_email_dict())
                for partner in mail.recipient_ids:
                    email_list.append(
                        mail.send_get_email_dict(partner=partner))

                # 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.model and mail.res_id:
                        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.'
                      ),
                })
                mail_sent = False

                # 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([
                    ('is_email', '=', True),
                    ('mail_message_id', 'in',
                     mail.mapped('mail_message_id').ids),
                    ('res_partner_id', 'in', mail.mapped('recipient_ids').ids),
                    ('email_status', 'not in', ('sent', 'canceled'))
                ])
                if notifs:
                    notifs.sudo().write({
                        'email_status': 'exception',
                    })

                # 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)
                    try:
                        res = IrMailServer.send_email(
                            msg,
                            mail_server_id=mail.mail_server_id.id,
                            smtp_session=smtp_session)
                    except AssertionError as error:
                        if str(error) == IrMailServer.NO_VALID_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.write({
                        'state': 'sent',
                        'message_id': res,
                        'failure_reason': False
                    })
                    mail_sent = True

                # /!\ can't use mail.state here, as mail.refresh() will cause an error
                # see revid:[email protected] in 6.1
                if mail_sent:
                    _logger.info(
                        'Mail with ID %r and Message-Id %r successfully sent',
                        mail.id, mail.message_id)
                mail._postprocess_sent_message(mail_sent=mail_sent)
            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)
                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(mail_sent=False)
                if raise_exception:
                    if isinstance(e, AssertionError):
                        # 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.º 30
0
    def anonymize_database(self):
        """Sets the 'anonymized' state to defined fields"""
        self.ensure_one()

        # create a new history record:
        history = self.env['ir.model.fields.anonymization.history'].create({
            'date':
            fields.Datetime.now(),
            'state':
            'started',
            'direction':
            'clear -> anonymized'
        })

        # check that all the defined fields are in the 'clear' state
        state = self.env['ir.model.fields.anonymization']._get_global_state()
        error_type = _('Error !')
        if state == 'anonymized':
            raise UserError('%s: %s' % (
                error_type,
                _("The database is currently anonymized, you cannot anonymize it again."
                  )))
        elif state == 'unstable':
            raise UserError('%s: %s' % (
                error_type,
                _("The database anonymization is currently in an unstable state. Some fields are anonymized,"
                  " while some fields are not anonymized. You should try to solve this problem before trying to do anything."
                  )))

        # do the anonymization:
        dirpath = os.environ.get('HOME') or os.getcwd()
        rel_filepath = 'field_anonymization_%s_%s.json' % (self.env.cr.dbname,
                                                           history.id)
        abs_filepath = os.path.abspath(os.path.join(dirpath, rel_filepath))

        ano_fields = self.env['ir.model.fields.anonymization'].search([
            ('state', '!=', 'not_existing')
        ])
        if not ano_fields:
            raise UserError(
                '%s: %s' %
                (error_type, _("No fields are going to be anonymized.")))

        data = []

        for field in ano_fields:
            model_name = field.model_id.model
            field_name = field.field_id.name
            field_type = field.field_id.ttype
            table_name = self.env[model_name]._table

            # get the current value
            self.env.cr.execute('select id, "%s" from "%s"' %
                                (field_name, table_name))
            for record in self.env.cr.dictfetchall():
                data.append({
                    "model_id": model_name,
                    "field_id": field_name,
                    "id": record['id'],
                    "value": record[field_name]
                })

                # anonymize the value:
                anonymized_value = None

                sid = str(record['id'])
                if field_type == 'char':
                    anonymized_value = 'xxx' + sid
                elif field_type == 'selection':
                    anonymized_value = 'xxx' + sid
                elif field_type == 'text':
                    anonymized_value = 'xxx' + sid
                elif field_type == 'html':
                    anonymized_value = 'xxx' + sid
                elif field_type == 'boolean':
                    anonymized_value = random.choice([True, False])
                elif field_type == 'date':
                    anonymized_value = '2011-11-11'
                elif field_type == 'datetime':
                    anonymized_value = '2011-11-11 11:11:11'
                elif field_type in ('float', 'monetary'):
                    anonymized_value = 0.0
                elif field_type == 'integer':
                    anonymized_value = 0
                elif field_type in [
                        'binary', 'many2many', 'many2one', 'one2many',
                        'reference'
                ]:  # cannot anonymize these kind of fields
                    raise UserError('%s: %s' % (
                        error_type,
                        _("Cannot anonymize fields of these types: binary, many2many, many2one, one2many, reference."
                          )))

                if anonymized_value is None:
                    raise UserError(
                        '%s: %s' %
                        (error_type, _("Anonymized value can not be empty.")))

                sql = 'update "%(table)s" set "%(field)s" = %%(anonymized_value)s where id = %%(id)s' % {
                    'table': table_name,
                    'field': field_name,
                }
                self.env.cr.execute(sql, {
                    'anonymized_value': anonymized_value,
                    'id': record['id']
                })

        # save json file:
        with open(abs_filepath, 'w') as fn:
            json.dump(data, fn)

        # update the anonymization fields:
        ano_fields.write({'state': 'anonymized'})

        # add a result message in the wizard:
        msgs = [
            "Anonymization successful.", "",
            "Donot forget to save the resulting file to a safe place because you will not be able to revert the anonymization without this file.",
            "",
            "This file is also stored in the %s directory. The absolute file path is: %s."
        ]
        msg = '\n'.join(msgs) % (dirpath, abs_filepath)

        with open(abs_filepath, 'rb') as fn:
            self.write({
                'msg': msg,
                'file_export': base64.encodestring(fn.read()),
            })

        # update the history record:
        history.write({
            'field_ids': [[6, 0, ano_fields.ids]],
            'msg': msg,
            'filepath': abs_filepath,
            'state': 'done',
        })

        return {
            'res_id':
            self.id,
            'view_id':
            self.env.ref(
                'anonymization.view_ir_model_fields_anonymize_wizard_form').
            ids,
            'view_type':
            'form',
            "view_mode":
            'form',
            'res_model':
            'ir.model.fields.anonymize.wizard',
            'type':
            'ir.actions.act_window',
            'context': {
                'step': 'just_anonymized'
            },
            'target':
            'new'
        }