class ResUsersRole(models.Model):
    _name = 'res.users.role'
    _inherits = {'res.groups': 'group_id'}
    _description = "User role"

    group_id = fields.Many2one(
        'res.groups', required=True, ondelete='cascade',
        readonly=True, string=u"Associated group")
    line_ids = fields.One2many(
        'res.users.role.line', 'role_id', string=u"Users")
    user_ids = fields.One2many(
        'res.users', string=u"Users", compute='_compute_user_ids')

    _defaults = {   # pylint: disable=attribute-deprecated
        'category_id': api.model(
            lambda cls: cls.env.ref(
                'base_user_role.ir_module_category_role').id),
    }

    @api.multi
    @api.depends('line_ids.user_id')
    def _compute_user_ids(self):
        for role in self:
            role.user_ids = role.line_ids.mapped('user_id')

    @api.model
    def create(self, vals):
        new_record = super(ResUsersRole, self).create(vals)
        new_record.update_users()
        return new_record

    @api.multi
    def write(self, vals):
        res = super(ResUsersRole, self).write(vals)
        self.update_users()
        return res

    @api.multi
    def unlink(self):
        users = self.mapped('user_ids')
        res = super(ResUsersRole, self).unlink()
        users.set_groups_from_roles(force=True)
        return res

    @api.multi
    def update_users(self):
        """Update all the users concerned by the roles identified by `ids`."""
        users = self.mapped('user_ids')
        users.set_groups_from_roles()
        return True

    @api.model
    def cron_update_users(self):
        logging.info(u"Update user roles")
        self.search([]).update_users()
Exemple #2
0
class res_partner(osv.Model, format_address):
    _description = 'Partner'
    _name = "res.partner"

    def _address_display(self, cr, uid, ids, name, args, context=None):
        res = {}
        for partner in self.browse(cr, uid, ids, context=context):
            res[partner.id] = self._display_address(cr,
                                                    uid,
                                                    partner,
                                                    context=context)
        return res

    @api.multi
    def _get_tz_offset(self, name, args):
        return dict((
            p.id,
            datetime.datetime.now(pytz.timezone(p.tz or 'GMT')).strftime('%z'))
                    for p in self)

    @api.multi
    def _get_image(self, name, args):
        return dict(
            (p.id, tools.image_get_resized_images(p.image)) for p in self)

    @api.one
    def _set_image(self, name, value, args):
        return self.write({'image': tools.image_resize_image_big(value)})

    def _commercial_partner_compute(self,
                                    cr,
                                    uid,
                                    ids,
                                    name,
                                    args,
                                    context=None):
        """ Returns the partner that is considered the commercial
        entity of this partner. The commercial entity holds the master data
        for all commercial fields (see :py:meth:`~_commercial_fields`) """
        result = dict.fromkeys(ids, False)
        for partner in self.browse(cr, uid, ids, context=context):
            current_partner = partner
            while not current_partner.is_company and current_partner.parent_id:
                current_partner = current_partner.parent_id
            result[partner.id] = current_partner.id
        return result

    def _display_name_compute(self, cr, uid, ids, name, args, context=None):
        context = dict(context or {})
        context.pop('show_address', None)
        context.pop('show_address_only', None)
        context.pop('show_email', None)
        return dict(self.name_get(cr, uid, ids, context=context))

    # indirections to avoid passing a copy of the overridable method when declaring the function field
    _commercial_partner_id = lambda self, *args, **kwargs: self._commercial_partner_compute(
        *args, **kwargs)
    _display_name = lambda self, *args, **kwargs: self._display_name_compute(
        *args, **kwargs)

    _commercial_partner_store_triggers = {
        'res.partner': (lambda self, cr, uid, ids, context=None: self.search(
            cr,
            uid, [('id', 'child_of', ids)],
            context=dict(active_test=False)), ['parent_id', 'is_company'], 10)
    }
    _display_name_store_triggers = {
        'res.partner': (lambda self, cr, uid, ids, context=None: self.search(
            cr,
            uid, [('id', 'child_of', ids)],
            context=dict(active_test=False)),
                        ['parent_id', 'is_company', 'name'], 10)
    }

    _order = "display_name"
    _columns = {
        'name': fields.char('Name', required=True, select=True),
        'display_name': fields.function(_display_name, type='char', string='Name', store=_display_name_store_triggers, select=True),
        'date': fields.date('Date', select=1),
        'title': fields.many2one('res.partner.title', 'Title'),
        'parent_id': fields.many2one('res.partner', 'Related Company', select=True),
        'parent_name': fields.related('parent_id', 'name', type='char', readonly=True, string='Parent name'),
        'child_ids': fields.one2many('res.partner', 'parent_id', 'Contacts', domain=[('active','=',True)]), # force "active_test" domain to bypass _search() override
        'ref': fields.char('Internal Reference', select=1),
        'lang': fields.selection(_lang_get, 'Language',
            help="If the selected language is loaded in the system, all documents related to this contact will be printed in this language. If not, it will be English."),
        'tz': fields.selection(_tz_get,  'Timezone', size=64,
            help="The partner's timezone, used to output proper date and time values inside printed reports. "
                 "It is important to set a value for this field. You should use the same timezone "
                 "that is otherwise used to pick and render date and time values: your computer's timezone."),
        'tz_offset': fields.function(_get_tz_offset, type='char', size=5, string='Timezone offset', invisible=True),
        'user_id': fields.many2one('res.users', 'Salesperson', help='The internal user that is in charge of communicating with this contact if any.'),
        'vat': fields.char('TIN', help="Tax Identification Number. Fill it if the company is subjected to taxes. Used by the some of the legal statements."),
        'bank_ids': fields.one2many('res.partner.bank', 'partner_id', 'Banks'),
        'website': fields.char('Website', help="Website of Partner or Company"),
        'comment': fields.text('Notes'),
        'category_id': fields.many2many('res.partner.category', id1='partner_id', id2='category_id', string='Categories'),
        'credit_limit': fields.float(string='Credit Limit'),
        'barcode': fields.char('Barcode', oldname='ean13'),
        'active': fields.boolean('Active'),
        'customer': fields.boolean('Is a Customer', help="Check this box if this contact is a customer."),
        'supplier': fields.boolean('Is a Vendor', help="Check this box if this contact is a vendor. If it's not checked, purchase people will not see it when encoding a purchase order."),
        'employee': fields.boolean('Employee', help="Check this box if this contact is an Employee."),
        'function': fields.char('Job Position'),
        'type': fields.selection([('default', 'Default'), ('invoice', 'Invoice'),
                                   ('delivery', 'Shipping'), ('contact', 'Contact'),
                                   ('other', 'Other')], 'Address Type',
            help="Used to select automatically the right address according to the context in sales and purchases documents."),
        'street': fields.char('Street'),
        'street2': fields.char('Street2'),
        'zip': fields.char('Zip', size=24, change_default=True),
        'city': fields.char('City'),
        'state_id': fields.many2one("res.country.state", 'State', ondelete='restrict'),
        'country_id': fields.many2one('res.country', 'Country', ondelete='restrict'),
        'email': fields.char('Email'),
        'phone': fields.char('Phone'),
        'fax': fields.char('Fax'),
        'mobile': fields.char('Mobile'),
        'birthdate': fields.char('Birthdate'),
        'is_company': fields.boolean('Is a Company', help="Check if the contact is a company, otherwise it is a person"),
        'use_parent_address': fields.boolean('Use Company Address', help="Select this if you want to set company's address information  for this contact"),
        # image: all image fields are base64 encoded and PIL-supported
        'image': fields.binary("Image",
            help="This field holds the image used as avatar for this contact, limited to 1024x1024px"),
        'image_medium': fields.function(_get_image, fnct_inv=_set_image,
            string="Medium-sized image", type="binary", multi="_get_image",
            store={
                'res.partner': (lambda self, cr, uid, ids, c={}: ids, ['image'], 10),
            },
            help="Medium-sized image of this contact. 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.function(_get_image, fnct_inv=_set_image,
            string="Small-sized image", type="binary", multi="_get_image",
            store={
                'res.partner': (lambda self, cr, uid, ids, c={}: ids, ['image'], 10),
            },
            help="Small-sized image of this contact. It is automatically "\
                 "resized as a 64x64px image, with aspect ratio preserved. "\
                 "Use this field anywhere a small image is required."),
        'company_id': fields.many2one('res.company', 'Company', select=1),
        'color': fields.integer('Color Index'),
        'user_ids': fields.one2many('res.users', 'partner_id', 'Users'),
        'contact_address': fields.function(_address_display,  type='char', string='Complete Address'),

        # technical field used for managing commercial fields
        'commercial_partner_id': fields.function(_commercial_partner_id, type='many2one', relation='res.partner', string='Commercial Entity', store=_commercial_partner_store_triggers)
    }

    @api.model
    def _default_category(self):
        category_id = self.env.context.get('category_id', False)
        return [category_id] if category_id else False

    @api.model
    def _get_default_image(self, is_company, colorize=False):
        img_path = openerp.modules.get_module_resource(
            'base', 'static/src/img',
            'company_image.png' if is_company else 'avatar.png')
        with open(img_path, 'rb') as f:
            image = f.read()

        # colorize user avatars
        if not is_company:
            image = tools.image_colorize(image)

        return tools.image_resize_image_big(image.encode('base64'))

    def fields_view_get(self,
                        cr,
                        user,
                        view_id=None,
                        view_type='form',
                        context=None,
                        toolbar=False,
                        submenu=False):
        if (not view_id) and (view_type == 'form') and context and context.get(
                'force_email', False):
            view_id = self.pool['ir.model.data'].get_object_reference(
                cr, user, 'base', 'view_partner_simple_form')[1]
        res = super(res_partner, self).fields_view_get(cr,
                                                       user,
                                                       view_id=view_id,
                                                       view_type=view_type,
                                                       context=context,
                                                       toolbar=toolbar,
                                                       submenu=submenu)
        if view_type == 'form':
            res['arch'] = self.fields_view_get_address(cr,
                                                       user,
                                                       res['arch'],
                                                       context=context)
        return res

    @api.model
    def _default_company(self):
        return self.env['res.company']._company_default_get('res.partner')

    _defaults = {
        'active': True,
        'lang': api.model(lambda self: self.env.lang),
        'tz': api.model(lambda self: self.env.context.get('tz', False)),
        'customer': True,
        'category_id': _default_category,
        'company_id': _default_company,
        'color': 0,
        'is_company': False,
        'type': 'contact',  # type 'default' is wildcard and thus inappropriate
        'use_parent_address': False,
        'image': False,
    }

    _constraints = [
        (osv.osv._check_recursion,
         'You cannot create recursive Partner hierarchies.', ['parent_id']),
    ]

    @api.one
    def copy(self, default=None):
        default = dict(default or {})
        default['name'] = _('%s (copy)') % self.name
        return super(res_partner, self).copy(default)

    @api.multi
    def onchange_type(self, is_company):
        value = {'title': False}
        if is_company:
            value['use_parent_address'] = False
            domain = {'title': [('domain', '=', 'partner')]}
        else:
            domain = {'title': [('domain', '=', 'contact')]}
        return {'value': value, 'domain': domain}

    def onchange_address(self,
                         cr,
                         uid,
                         ids,
                         use_parent_address,
                         parent_id,
                         context=None):
        def value_or_id(val):
            """ return val or val.id if val is a browse record """
            return val if isinstance(val, (bool, int, long, float,
                                           basestring)) else val.id

        result = {}
        if parent_id:
            if ids:
                partner = self.browse(cr, uid, ids[0], context=context)
                if partner.parent_id and partner.parent_id.id != parent_id:
                    result['warning'] = {
                        'title':
                        _('Warning'),
                        'message':
                        _('Changing the company of a contact should only be done if it '
                          'was never correctly set. If an existing contact starts working for a new '
                          'company then a new contact should be created under that new '
                          'company. You can use the "Discard" button to abandon this change.'
                          )
                    }
            if use_parent_address:
                parent = self.browse(cr, uid, parent_id, context=context)
                address_fields = self._address_fields(cr, uid, context=context)
                result['value'] = dict(
                    (key, value_or_id(parent[key])) for key in address_fields)
        else:
            result['value'] = {'use_parent_address': False}
        return result

    @api.multi
    def onchange_state(self, state_id):
        if state_id:
            state = self.env['res.country.state'].browse(state_id)
            return {'value': {'country_id': state.country_id.id}}
        return {}

    def _update_fields_values(self, cr, uid, partner, fields, context=None):
        """ Returns dict of write() values for synchronizing ``fields`` """
        values = {}
        for fname in fields:
            field = self._fields[fname]
            if field.type == 'one2many':
                raise AssertionError(
                    'One2Many fields cannot be synchronized as part of `commercial_fields` or `address fields`'
                )
            if field.type == 'many2one':
                values[fname] = partner[fname].id if partner[fname] else False
            elif field.type == 'many2many':
                values[fname] = [(6, 0, [r.id for r in partner[fname] or []])]
            else:
                values[fname] = partner[fname]
        return values

    def _address_fields(self, cr, uid, context=None):
        """ Returns the list of address fields that are synced from the parent
        when the `use_parent_address` flag is set. """
        return list(ADDRESS_FIELDS)

    def update_address(self, cr, uid, ids, vals, context=None):
        address_fields = self._address_fields(cr, uid, context=context)
        addr_vals = dict(
            (key, vals[key]) for key in address_fields if key in vals)
        if addr_vals:
            return super(res_partner, self).write(cr, uid, ids, addr_vals,
                                                  context)

    def _commercial_fields(self, cr, uid, context=None):
        """ Returns the list of fields that are managed by the commercial entity
        to which a partner belongs. These fields are meant to be hidden on
        partners that aren't `commercial entities` themselves, and will be
        delegated to the parent `commercial entity`. The list is meant to be
        extended by inheriting classes. """
        return ['vat', 'credit_limit']

    def _commercial_sync_from_company(self, cr, uid, partner, context=None):
        """ Handle sync of commercial fields when a new parent commercial entity is set,
        as if they were related fields """
        commercial_partner = partner.commercial_partner_id
        if not commercial_partner:
            # On child partner creation of a parent partner,
            # the commercial_partner_id is not yet computed
            commercial_partner_id = self._commercial_partner_compute(
                cr,
                uid, [partner.id],
                'commercial_partner_id', [],
                context=context)[partner.id]
            commercial_partner = self.browse(cr,
                                             uid,
                                             commercial_partner_id,
                                             context=context)
        if commercial_partner != partner:
            commercial_fields = self._commercial_fields(cr,
                                                        uid,
                                                        context=context)
            sync_vals = self._update_fields_values(cr,
                                                   uid,
                                                   commercial_partner,
                                                   commercial_fields,
                                                   context=context)
            partner.write(sync_vals)

    def _commercial_sync_to_children(self, cr, uid, partner, context=None):
        """ Handle sync of commercial fields to descendants """
        commercial_fields = self._commercial_fields(cr, uid, context=context)
        commercial_partner = partner.commercial_partner_id
        if not commercial_partner:
            # On child partner creation of a parent partner,
            # the commercial_partner_id is not yet computed
            commercial_partner_id = self._commercial_partner_compute(
                cr,
                uid, [partner.id],
                'commercial_partner_id', [],
                context=context)[partner.id]
            commercial_partner = self.browse(cr,
                                             uid,
                                             commercial_partner_id,
                                             context=context)
        sync_vals = self._update_fields_values(cr,
                                               uid,
                                               commercial_partner,
                                               commercial_fields,
                                               context=context)
        sync_children = [c for c in partner.child_ids if not c.is_company]
        for child in sync_children:
            self._commercial_sync_to_children(cr, uid, child, context=context)
        return self.write(cr,
                          uid, [c.id for c in sync_children],
                          sync_vals,
                          context=context)

    def _fields_sync(self, cr, uid, partner, update_values, context=None):
        """ Sync commercial fields and address fields from company and to children after create/update,
        just as if those were all modeled as fields.related to the parent """
        # 1. From UPSTREAM: sync from parent
        if update_values.get('parent_id') or update_values.get(
                'use_parent_address'):
            # 1a. Commercial fields: sync if parent changed
            if update_values.get('parent_id'):
                self._commercial_sync_from_company(cr,
                                                   uid,
                                                   partner,
                                                   context=context)
            # 1b. Address fields: sync if parent or use_parent changed *and* both are now set
            if partner.parent_id and partner.use_parent_address:
                onchange_vals = self.onchange_address(
                    cr,
                    uid, [partner.id],
                    use_parent_address=partner.use_parent_address,
                    parent_id=partner.parent_id.id,
                    context=context).get('value', {})
                partner.update_address(onchange_vals)

        # 2. To DOWNSTREAM: sync children
        if partner.child_ids:
            # 2a. Commercial Fields: sync if commercial entity
            if partner.commercial_partner_id == partner:
                commercial_fields = self._commercial_fields(cr,
                                                            uid,
                                                            context=context)
                if any(field in update_values for field in commercial_fields):
                    self._commercial_sync_to_children(cr,
                                                      uid,
                                                      partner,
                                                      context=context)
            # 2b. Address fields: sync if address changed
            address_fields = self._address_fields(cr, uid, context=context)
            if any(field in update_values for field in address_fields):
                domain_children = [('parent_id', '=', partner.id),
                                   ('use_parent_address', '=', True)]
                update_ids = self.search(cr,
                                         uid,
                                         domain_children,
                                         context=context)
                self.update_address(cr,
                                    uid,
                                    update_ids,
                                    update_values,
                                    context=context)

    def _handle_first_contact_creation(self, cr, uid, partner, context=None):
        """ On creation of first contact for a company (or root) that has no address, assume contact address
        was meant to be company address """
        parent = partner.parent_id
        address_fields = self._address_fields(cr, uid, context=context)
        if parent and (parent.is_company or not parent.parent_id) and len(parent.child_ids) == 1 and \
            any(partner[f] for f in address_fields) and not any(parent[f] for f in address_fields):
            addr_vals = self._update_fields_values(cr,
                                                   uid,
                                                   partner,
                                                   address_fields,
                                                   context=context)
            parent.update_address(addr_vals)
            if not parent.is_company:
                parent.write({'is_company': True})

    def unlink(self, cr, uid, ids, context=None):
        orphan_contact_ids = self.search(cr,
                                         uid,
                                         [('parent_id', 'in', ids),
                                          ('id', 'not in', ids),
                                          ('use_parent_address', '=', True)],
                                         context=context)
        if orphan_contact_ids:
            # no longer have a parent address
            self.write(cr,
                       uid,
                       orphan_contact_ids, {'use_parent_address': False},
                       context=context)
        return super(res_partner, self).unlink(cr, uid, ids, context=context)

    def _clean_website(self, website):
        (scheme, netloc, path, params, query,
         fragment) = urlparse.urlparse(website)
        if not scheme:
            if not netloc:
                netloc, path = path, ''
            website = urlparse.urlunparse(
                ('http', netloc, path, params, query, fragment))
        return website

    @api.multi
    def write(self, vals):
        # res.partner must only allow to set the company_id of a partner if it
        # is the same as the company of all users that inherit from this partner
        # (this is to allow the code from res_users to write to the partner!) or
        # if setting the company_id to False (this is compatible with any user
        # company)
        if vals.get('website'):
            vals['website'] = self._clean_website(vals['website'])
        if vals.get('company_id'):
            company = self.env['res.company'].browse(vals['company_id'])
            for partner in self:
                if partner.user_ids:
                    companies = set(user.company_id
                                    for user in partner.user_ids)
                    if len(companies) > 1 or company not in companies:
                        raise UserError(
                            _("You can not change the company as the partner/user has multiple user linked with different companies."
                              ))

        result = super(res_partner, self).write(vals)
        for partner in self:
            self._fields_sync(partner, vals)
        return result

    @api.model
    def create(self, vals):
        if vals.get('website'):
            vals['website'] = self._clean_website(vals['website'])
        partner = super(res_partner, self).create(vals)
        self._fields_sync(partner, vals)
        self._handle_first_contact_creation(partner)
        return partner

    def open_commercial_entity(self, cr, uid, ids, context=None):
        """ Utility method used to add an "Open Company" button in partner views """
        partner = self.browse(cr, uid, ids[0], context=context)
        return {
            'type': 'ir.actions.act_window',
            'res_model': 'res.partner',
            'view_mode': 'form',
            'res_id': partner.commercial_partner_id.id,
            'target': 'current',
            'flags': {
                'form': {
                    'action_buttons': True
                }
            }
        }

    def open_parent(self, cr, uid, ids, context=None):
        """ Utility method used to add an "Open Parent" button in partner views """
        partner = self.browse(cr, uid, ids[0], context=context)
        return {
            'type': 'ir.actions.act_window',
            'res_model': 'res.partner',
            'view_mode': 'form',
            'res_id': partner.parent_id.id,
            'target': 'new',
            'flags': {
                'form': {
                    'action_buttons': True
                }
            }
        }

    def name_get(self, cr, uid, ids, context=None):
        if context is None:
            context = {}
        if isinstance(ids, (int, long)):
            ids = [ids]
        res = []
        for record in self.browse(cr, uid, ids, context=context):
            name = record.name
            if record.parent_id and not record.is_company:
                name = "%s, %s" % (record.parent_name, name)
            if context.get('show_address_only'):
                name = self._display_address(cr,
                                             uid,
                                             record,
                                             without_company=True,
                                             context=context)
            if context.get('show_address'):
                name = name + "\n" + self._display_address(
                    cr, uid, record, without_company=True, context=context)
            name = name.replace('\n\n', '\n')
            name = name.replace('\n\n', '\n')
            if context.get('show_email') and record.email:
                name = "%s <%s>" % (name, record.email)
            res.append((record.id, name))
        return res

    def _parse_partner_name(self, text, context=None):
        """ Supported syntax:
            - 'Raoul <*****@*****.**>': will find name and email address
            - otherwise: default, everything is set as the name """
        emails = tools.email_split(text.replace(' ', ','))
        if emails:
            email = emails[0]
            name = text[:text.index(email)].replace('"',
                                                    '').replace('<',
                                                                '').strip()
        else:
            name, email = text, ''
        return name, email

    def name_create(self, cr, uid, name, context=None):
        """ Override of orm's name_create method for partners. The purpose is
            to handle some basic formats to create partners using the
            name_create.
            If only an email address is received and that the regex cannot find
            a name, the name will have the email value.
            If 'force_email' key in context: must find the email address. """
        if context is None:
            context = {}
        name, email = self._parse_partner_name(name, context=context)
        if context.get('force_email') and not email:
            raise UserError(
                _("Couldn't create contact without email address!"))
        if not name and email:
            name = email
        rec_id = self.create(cr,
                             uid, {
                                 self._rec_name: name or email,
                                 'email': email or False
                             },
                             context=context)
        return self.name_get(cr, uid, [rec_id], context)[0]

    def _search(self,
                cr,
                user,
                args,
                offset=0,
                limit=None,
                order=None,
                context=None,
                count=False,
                access_rights_uid=None):
        """ Override search() to always show inactive children when searching via ``child_of`` operator. The ORM will
        always call search() with a simple domain of the form [('parent_id', 'in', [ids])]. """
        # a special ``domain`` is set on the ``child_ids`` o2m to bypass this logic, as it uses similar domain expressions
        if len(args) == 1 and len(args[0]) == 3 and args[0][:2] == ('parent_id','in') \
                and args[0][2] != [False]:
            context = dict(context or {}, active_test=False)
        return super(res_partner,
                     self)._search(cr,
                                   user,
                                   args,
                                   offset=offset,
                                   limit=limit,
                                   order=order,
                                   context=context,
                                   count=count,
                                   access_rights_uid=access_rights_uid)

    def name_search(self,
                    cr,
                    uid,
                    name,
                    args=None,
                    operator='ilike',
                    context=None,
                    limit=100):
        if not args:
            args = []
        if name and operator in ('=', 'ilike', '=ilike', 'like', '=like'):

            self.check_access_rights(cr, uid, 'read')
            where_query = self._where_calc(cr, uid, args, context=context)
            self._apply_ir_rules(cr, uid, where_query, 'read', context=context)
            from_clause, where_clause, where_clause_params = where_query.get_sql(
            )
            where_str = where_clause and (" WHERE %s AND " %
                                          where_clause) or ' WHERE '

            # search on the name of the contacts and of its company
            search_name = name
            if operator in ('ilike', 'like'):
                search_name = '%%%s%%' % name
            if operator in ('=ilike', '=like'):
                operator = operator[1:]

            unaccent = get_unaccent_wrapper(cr)

            query = """SELECT id
                         FROM res_partner
                      {where} ({email} {operator} {percent}
                           OR {display_name} {operator} {percent}
                           OR {reference} {operator} {percent})
                           -- don't panic, trust postgres bitmap
                     ORDER BY {display_name} {operator} {percent} desc,
                              {display_name}
                    """.format(where=where_str,
                               operator=operator,
                               email=unaccent('email'),
                               display_name=unaccent('display_name'),
                               reference=unaccent('ref'),
                               percent=unaccent('%s'))

            where_clause_params += [search_name] * 4
            if limit:
                query += ' limit %s'
                where_clause_params.append(limit)
            cr.execute(query, where_clause_params)
            ids = map(lambda x: x[0], cr.fetchall())

            if ids:
                return self.name_get(cr, uid, ids, context)
            else:
                return []
        return super(res_partner, self).name_search(cr,
                                                    uid,
                                                    name,
                                                    args,
                                                    operator=operator,
                                                    context=context,
                                                    limit=limit)

    def find_or_create(self, cr, uid, email, context=None):
        """ Find a partner with the given ``email`` or use :py:method:`~.name_create`
            to create one

            :param str email: email-like string, which should contain at least one email,
                e.g. ``"Raoul Grosbedon <*****@*****.**>"``"""
        assert email, 'an email is required for find_or_create to work'
        emails = tools.email_split(email)
        if emails:
            email = emails[0]
        ids = self.search(cr,
                          uid, [('email', '=ilike', email)],
                          context=context)
        if not ids:
            return self.name_create(cr, uid, email, context=context)[0]
        return ids[0]

    def _email_send(self,
                    cr,
                    uid,
                    ids,
                    email_from,
                    subject,
                    body,
                    on_error=None):
        partners = self.browse(cr, uid, ids)
        for partner in partners:
            if partner.email:
                tools.email_send(email_from, [partner.email], subject, body,
                                 on_error)
        return True

    def email_send(self, cr, uid, ids, email_from, subject, body, on_error=''):
        while len(ids):
            self.pool['ir.cron'].create(
                cr, uid, {
                    'name': 'Send Partner Emails',
                    'user_id': uid,
                    'model': 'res.partner',
                    'function': '_email_send',
                    'args': repr(
                        [ids[:16], email_from, subject, body, on_error])
                })
            ids = ids[16:]
        return True

    def address_get(self, cr, uid, ids, adr_pref=None, context=None):
        """ Find contacts/addresses of the right type(s) by doing a depth-first-search
        through descendants within company boundaries (stop at entities flagged ``is_company``)
        then continuing the search at the ancestors that are within the same company boundaries.
        Defaults to partners of type ``'default'`` when the exact type is not found, or to the
        provided partner itself if no type ``'default'`` is found either. """
        adr_pref = set(adr_pref or [])
        if 'default' not in adr_pref:
            adr_pref.add('default')
        result = {}
        visited = set()
        if isinstance(ids, (int, long)):
            ids = [ids]
        for partner in self.browse(cr, uid, filter(None, ids),
                                   context=context):
            current_partner = partner
            while current_partner:
                to_scan = [current_partner]
                # Scan descendants, DFS
                while to_scan:
                    record = to_scan.pop(0)
                    visited.add(record)
                    if record.type in adr_pref and not result.get(record.type):
                        result[record.type] = record.id
                    if len(result) == len(adr_pref):
                        return result
                    to_scan = [
                        c for c in record.child_ids if c not in visited
                        if not c.is_company
                    ] + to_scan

                # Continue scanning at ancestor if current_partner is not a commercial entity
                if current_partner.is_company or not current_partner.parent_id:
                    break
                current_partner = current_partner.parent_id

        # default to type 'default' or the partner itself
        default = result.get('default', ids and ids[0] or False)
        for adr_type in adr_pref:
            result[adr_type] = result.get(adr_type) or default
        return result

    def view_header_get(self, cr, uid, view_id, view_type, context):
        res = super(res_partner, self).view_header_get(cr, uid, view_id,
                                                       view_type, context)
        if res: return res
        if not context.get('category_id', False):
            return False
        return _('Partners: ') + self.pool['res.partner.category'].browse(
            cr, uid, context['category_id'], context).name

    @api.model
    @api.returns('self')
    def main_partner(self):
        ''' Return the main partner '''
        return self.env.ref('base.main_partner')

    def _display_address(self,
                         cr,
                         uid,
                         address,
                         without_company=False,
                         context=None):
        '''
        The purpose of this function is to build and return an address formatted accordingly to the
        standards of the country where it belongs.

        :param address: browse record of the res.partner to format
        :returns: the address formatted in a display that fit its country habits (or the default ones
            if not country is specified)
        :rtype: string
        '''

        # get the information that will be injected into the display format
        # get the address format
        address_format = address.country_id.address_format or \
              "%(street)s\n%(street2)s\n%(city)s %(state_code)s %(zip)s\n%(country_name)s"
        args = {
            'state_code': address.state_id.code or '',
            'state_name': address.state_id.name or '',
            'country_code': address.country_id.code or '',
            'country_name': address.country_id.name or '',
            'company_name': address.parent_name or '',
        }
        for field in self._address_fields(cr, uid, context=context):
            args[field] = getattr(address, field) or ''
        if without_company:
            args['company_name'] = ''
        elif address.parent_id:
            address_format = '%(company_name)s\n' + address_format
        return address_format % args
Exemple #3
0
class res_partner(osv.osv):
    _inherit = 'res.partner'

    def _get_payment_term(self, cr, uid, context=None):
        obj_payment_term = self.pool.get('account.payment.term')
        id_payment_term = obj_payment_term.search(
            cr, uid, [('name', '=', 'Immediate Payment')])
        if id_payment_term:
            return id_payment_term[0]
        return False

    def _get_default_branch(self, cr, uid, ids, context=None):
        user_obj = self.pool.get('res.users')
        user_browse = user_obj.browse(cr, uid, uid)
        branch_ids = False
        branch_ids = user_browse.branch_ids and len(
            user_browse.branch_ids
        ) == 1 and user_browse.branch_ids[0].id or False
        return branch_ids

    _columns = {
        'parent_name':
        fields.related('parent_id',
                       'name',
                       type='char',
                       readonly=True,
                       string='Parent name'),
        'default_code':
        fields.char('Partner Code'),
        'principle':
        fields.boolean('Principle'),
        'biro_jasa':
        fields.boolean('Biro Jasa'),
        'kas_negara':
        fields.boolean('Kas Negara'),
        'forwarder':
        fields.boolean('Forwarder'),
        'supplier':
        fields.boolean(
            'General Supplier',
            help=
            "Check this box if this contact is a supplier. If it's not checked, purchase people will not see it when encoding a purchase order."
        ),
        'showroom':
        fields.boolean('Showroom'),
        'ahass':
        fields.boolean('Ahass'),
        'dealer':
        fields.boolean('Dealer'),
        'finance_company':
        fields.boolean('Finance Company'),
        'vat':
        fields.related(
            'npwp',
            string="TIN",
            type="char",
            help=
            "Tax Identification Number. Check the box if this contact is subjected to taxes. Used by the some of the legal statements.",
            store=True),
        'ahm_code':
        fields.char('AHM Code'),
        'dealer_code':
        fields.char('Dealer Code'),
        'kode_pajak_id':
        fields.selection([('1', '010'), ('2', '020'), ('3', '030'),
                          ('4', '040'), ('5', '050'), ('6', '060'),
                          ('7', '070'), ('8', '080'), ('9', '090')],
                         'Kode Transaksi FP'),
        'tipe_faktur_pajak':
        fields.selection([('tanpa_fp', 'Tanpa Faktur Pajak'),
                          ('satuan', 'Satuan'), ('gabungan', 'Gabungan')],
                         'Tipe Faktur Pajak'),
        'pkp':
        fields.boolean('PKP'),
        'npwp':
        fields.char('No.NPWP'),
        'tgl_kukuh':
        fields.date('Tgl Kukuh'),
        'mobile_provider':
        fields.char('Mobile Provider'),

        #Alamat di Header
        'rt':
        fields.char('RT', size=3),
        'rw':
        fields.char('RW', size=3),
        'zip_id':
        fields.many2one(
            'dym.kelurahan',
            'ZIP Code',
            domain=
            "[('kecamatan_id','=',kecamatan_id),('state_id','=',state_id),('city_id','=',city_id)]"
        ),
        'kelurahan':
        fields.char('Kelurahan', size=100),
        'kecamatan_id':
        fields.many2one(
            'dym.kecamatan',
            'Kecamatan',
            size=128,
            domain="[('state_id','=',state_id),('city_id','=',city_id)]"),
        'kecamatan':
        fields.char('Kecamatan', size=100),
        'city_id':
        fields.many2one('dym.city',
                        'City',
                        domain="[('state_id','=',state_id)]"),

        #Alamat di Tab Customer Info
        'sama':
        fields.boolean(''),  #diberi required True
        'street_tab':
        fields.char('Address'),
        'street2_tab':
        fields.char(),
        'rt_tab':
        fields.char('RT', size=3),
        'rw_tab':
        fields.char('RW', size=3),
        'zip_tab_id':
        fields.many2one(
            'dym.kelurahan',
            'ZIP Code',
            domain=
            "[('kecamatan_id','=',kecamatan_tab_id),('state_id','=',state_tab_id),('city_id','=',city_tab_id)]"
        ),
        'kelurahan_tab':
        fields.char('Kelurahan', size=100),
        'kecamatan_tab_id':
        fields.many2one(
            'dym.kecamatan',
            'Kecamatan',
            size=128,
            domain="[('state_id','=',state_tab_id),('city_id','=',city_tab_id)]"
        ),
        'kecamatan_tab':
        fields.char('Kecamatan', size=100),
        'city_tab_id':
        fields.many2one('dym.city',
                        'City',
                        domain="[('state_id','=',state_tab_id)]"),
        'state_tab_id':
        fields.many2one('res.country.state', 'Province'),

        #Field yang ada di Tab Customer Info
        'birthday':
        fields.date('Date of Birth'),
        'hp_status':
        fields.selection([('aktif', 'Aktif'), ('TidakAktif', 'Tidak Aktif')],
                         'HP Status'),
        'gender':
        fields.selection([('lakilaki', 'Laki-laki'),
                          ('perempuan', 'Perempuan')], 'Jenis Kelamin'),
        'no_kk':
        fields.char('No. KK', 50),
        'religion':
        fields.selection([('Islam', 'Islam'), ('Kristen', 'Kristen'),
                          ('Katholik', 'Katholik'), ('Hindu', 'Hindu'),
                          ('Budha', 'Budha')], 'Religion'),
        'no_ktp':
        fields.char('No.KTP', 50),
        'property_account_payable':
        fields.property(
            type='many2one',
            relation='account.account',
            string="Account Payable",
            domain="[('type', '=', 'payable')]",
            help=
            "This account will be used instead of the default one as the payable account for the current partner",
            required=False),
        'property_account_receivable':
        fields.property(
            type='many2one',
            relation='account.account',
            string="Account Receivable",
            domain="[('type', '=', 'receivable')]",
            help=
            "This account will be used instead of the default one as the receivable account for the current partner",
            required=False),
        'property_account_rounding':
        fields.property(type='many2one',
                        relation='account.account',
                        string="Account Rounding",
                        required=False),
        'pendidikan':
        fields.selection([('noSD', 'Tidak Tamat SD'), ('sd', 'SD'),
                          ('sltp', 'SLTP/SMP'), ('slta', 'SLTA/SMA'),
                          ('akademik', 'Akademi/Diploma'),
                          ('sarjana', 'Sarjana(S1)'),
                          ('pascasarjana', 'Pasca Sarjana')], 'Pendidikan'),
        'pekerjaan':
        fields.selection([('pNegeri', 'Pegawai Negeri'),
                          ('pSwasta', 'Pegawai Swasta'), ('ojek', 'Ojek'),
                          ('pedagang', 'Pedagang/Wiraswasta'),
                          ('pelajar', 'Pelajar/Mahasiswa'),
                          ('guru', 'Guru/Dosen'), ('tni', 'TNI/Polri'),
                          ('irt', 'Ibu Rumah Tangga'),
                          ('petani/nelayan', 'Petani/Nelayan'),
                          ('pro', 'Profesional(Contoh : Dokter)'),
                          ('lain', 'Lainnya')], 'Pekerjaan'),
        'pengeluaran':
        fields.selection([('<900', '< Rp.900.000,-'),
                          ('900125', 'Rp.900.001,- s/d Rp.1.250.000,-'),
                          ('125175', 'Rp.1.250.001,- s/d Rp.1.750.000,-'),
                          ('175250', 'Rp.1.750.001,- s/d Rp.2.500.000,-'),
                          ('250400', 'Rp.2.500.001,- s/d Rp.4.000.000,-'),
                          ('400600', 'Rp.4.000.001,- s/d Rp.6.000.000,-'),
                          ('600000', '> Rp.6.000.000,-')],
                         'Pengeluaran /Bulan'),
        'rel_code':
        fields.related('default_code',
                       string='Partner Code',
                       type="char",
                       readonly="True"),
        'branch_id':
        fields.many2one('dym.branch', string='Branch'),
        'direct_customer':
        fields.boolean(string='Direct Customer'),
        'branch':
        fields.boolean(string='Branch (Boolean)'),
        'is_customer_depo':
        fields.boolean('Customer Depo'),
        'is_group_customer':
        fields.boolean('Group Customer'),
        'member':
        fields.char('Member Number'),
        'creditur_debitur':
        fields.boolean('Creditur / Debitur'),

        #Forwarder
        'driver_lines':
        fields.one2many('dym.driver.line', 'partner_id', 'Driver'),
        'plat_number_lines':
        fields.one2many('dym.plat.number.line', 'partner_id', 'Plat Number'),
    }

    _defaults = {
        'tz':
        api.model(lambda self: self.env.context.get('tz', 'Asia/Jakarta')),
        'sama': True,
        'default_code': 'BPA/',
        'branch_id': _get_default_branch,
    }

    _sql_constraints = [
        ('unique_member', 'unique(member)', 'Nomor Member sudah terdaftar!'),
    ]

    # def _unique_no_ktp(self, cr, uid, ids, context=None):
    #     for l in self.browse(cr, uid, ids, context=context):
    #         if l.no_ktp:
    #             if self.search(cr,uid,[('no_ktp','=',l.no_ktp),('id','!=',l.id)]):
    #                 return False
    #     return True

    # _constraints = [
    #     (_unique_no_ktp, 'No KTP Duplicate!', ['no_ktp']),
    # ]

    def default_get(self, cr, uid, fields, context=None):
        context = context or {}
        res = super(res_partner, self).default_get(cr,
                                                   uid,
                                                   fields,
                                                   context=context)
        if 'property_payment_term' in fields:
            res.update(
                {'property_payment_term': self._get_payment_term(cr, uid)})
        return res

    def _display_address(self,
                         cr,
                         uid,
                         address,
                         without_company=False,
                         context=None):
        '''
        The purpose of this function is to build and return an address formatted accordingly to the
        standards of the country where it belongs.

        :param address: browse record of the res.partner to format
        :returns: the address formatted in a display that fit its country habits (or the default ones
            if not country is specified)
        :rtype: string
        '''
        '''
        <xpath expr="//field[@name='city']" position="before">
            <group>
                <div>
                    <field name="street" placeholder="Street..." on_change="onchange_address(street,street2,rt,rw,state_id,city_id,kecamatan_id,kecamatan,zip_id,kelurahan)" />
                    <div>
                        <field name="street2" placeholder="Street" style="width: 50%%" on_change="onchange_address(street,street2,rt,rw,state_id,city_id,kecamatan_id,kecamatan,zip_id,kelurahan)" />
                        <field name="rt" placeholder="RT" style="width: 25%%" on_change="onchange_address(street,street2,rt,rw,state_id,city_id,kecamatan_id,kecamatan,zip_id,kelurahan)" />
                        <field name="rw" placeholder="RW" style="width: 25%%" on_change="onchange_address(street,street2,rt,rw,state_id,city_id,kecamatan_id,kecamatan,zip_id,kelurahan)" />
                        <field name="state_id" on_change="onchange_address(street,street2,rt,rw,state_id,city_id,kecamatan_id,kecamatan,zip_id,kelurahan)" class="oe_no_button" placeholder="Province" style="width: 50%%" options='{"no_open": True}' />
                        <field name="city_id" on_change="onchange_address(street,street2,rt,rw,state_id,city_id,kecamatan_id,kecamatan,zip_id,kelurahan)" placeholder="City" style="width: 50%%" attrs="{'required': ['|','|',('direct_customer','=',True),('is_group_customer','=',True),('customer','=',True)]}" />
                        <field name="kecamatan_id" on_change="onchange_address(street,street2,rt,rw,state_id,city_id,kecamatan_id,kecamatan,zip_id,kelurahan)" placeholder="Kecamatan" style="width: 50%%" />
                        <field name="kecamatan" on_change="onchange_address(street,street2,rt,rw,state_id,city_id,kecamatan_id,kecamatan,zip_id,kelurahan)" placeholder="Kecamatan" style="width: 50%%" />
                        <field name="zip_id" on_change="onchange_address(street,street2,rt,rw,state_id,city_id,kecamatan_id,kecamatan,zip_id,kelurahan)" placeholder="ZIP" style="width: 50%%" options='{"no_open": True}' />
                        <field name="kelurahan" on_change="onchange_address(street,street2,rt,rw,state_id,city_id,kecamatan_id,kecamatan,zip_id,kelurahan)" class="oe_no_button" placeholder="Kelurahan" style="width: 50%%" />
                    </div>
                </div>
            </group>
        </xpath>
        '''

        # get the information that will be injected into the display format
        # get the address format
        # address_format = address.country_id.address_format or \
        #       "%(street)s\n%(street2)s\nRT: %(rt)s RW: %(rw)s Desa/Kel:%(kelurahan)s Kec:%(kecamatan)s\nKab/Kota:%(city)s Prov:%(state_code)s %(zip)s\n%(country_name)s"

        address_format = "%(street)s\n%(street2)s\nRT: %(rt)s RW: %(rw)s Desa/Kel:%(kelurahan)s Kec:%(kecamatan)s\nKab/Kota:%(city_name)s Prov: %(state_name)s Kode Pos: %(kode_pos)s\n%(country_name)s"
        args = {
            'state_code': address.state_id.code or '',
            'state_name': address.state_id.name or '',
            'country_code': address.country_id.code or '',
            'country_name': address.country_id.name or '',
            'company_name': address.parent_name or '',
            'rt': address.rt or '-',
            'rw': address.rw or '-',
            'kelurahan': address.kelurahan or '-',
            'kecamatan': address.kecamatan or '-',
            'city_name': address.city_id and address.city_id.name or '-',
            'kode_pos': address.zip_id and address.zip_id.zip or '-',
        }
        for field in self._address_fields(cr, uid, context=context):
            args[field] = getattr(address, field) or ''
        if without_company:
            args['company_name'] = ''
        elif address.parent_id:
            address_format = '%(company_name)s\n' + address_format
        return address_format % args

    def npwp_onchange(self, cr, uid, ids, npwp, context=None):
        warning = {}
        value = {}
        result = {}
        if npwp:
            formatted_npwp = ''
            npwp_normalize = npwp.replace(' ', '').upper()
            splitted_npwp = re.findall(r'\d+', npwp_normalize)
            if len(splitted_npwp) == 6:
                if len(splitted_npwp[0]) == 2 and len(
                        splitted_npwp[1]) == 3 and len(
                            splitted_npwp[2]) == 3 and len(
                                splitted_npwp[3]) == 1 and len(
                                    splitted_npwp[4]) == 3 and len(
                                        splitted_npwp[5]) == 3:
                    formatted_npwp = splitted_npwp[0] + '.' + splitted_npwp[
                        1] + '.' + splitted_npwp[2] + '.' + splitted_npwp[
                            3] + '-' + splitted_npwp[4] + '.' + splitted_npwp[5]
                    return {'value': {'npwp': formatted_npwp}}
            elif len(splitted_npwp) == 1 and len(splitted_npwp[0]) == 15:
                formatted_npwp = splitted_npwp[0][:2] + '.' + splitted_npwp[0][
                    2:-10] + '.' + splitted_npwp[0][5:-7] + '.' + splitted_npwp[
                        0][8:-6] + '-' + splitted_npwp[0][
                            9:-3] + '.' + splitted_npwp[0][-3:]
                return {'value': {'npwp': formatted_npwp}}
            warning = {
                'title': ('Perhatian !'),
                'message':
                (('Format nomor npwp salah, mohon isi nomor npwp dengan format yang benar! (ex. 99.999.999.9-999.999)'
                  )),
            }
            value['npwp'] = self.browse(cr, uid, ids).npwp
            result['warning'] = warning
            result['value'] = value
            return result

    def onchange_mobile(self, cr, uid, ids, mobile, context=None):
        value = {}
        warning = {}

        if mobile:
            id_number = phonenumbers.parse(mobile, "ID")
            if not carrier._is_mobile(number_type(id_number)):
                warning = {
                    'title': ('Perhatian !'),
                    'message':
                    (('Masukkan nomor handphone dengan benar, misal: 0817989800'
                      )),
                }
                value['mobile'] = ''
            else:
                formatted_mobile = phonenumbers.format_number(
                    id_number, phonenumbers.PhoneNumberFormat.E164)
                provider_mobile = eval(
                    repr(carrier.name_for_number(id_number, "en")))
                value['mobile'] = formatted_mobile
                value['mobile_provider'] = provider_mobile

        return {
            'warning': warning,
            'value': value,
        }

    def onchange_customer(self, cr, uid, ids, customer):
        if not customer:
            return {
                'value': {
                    'no_ktp': False,
                    'birthday': False,
                    'gender': False,
                    'religion': False,
                    'no_kk': False,
                    'pendidikan': False,
                    'pekerjaan': False,
                    'pengeluaran': False,
                    'sama': '',
                }
            }
        return True

    def onchange_dealer(self, cr, uid, ids, dealer, finance_company, principle,
                        ahm_code, dealer_code):
        def_ahm_code = False
        def_dealer_code = False

        if dealer:
            def_ahm_code = True
            def_dealer_code = True
        if finance_company:
            def_ahm_code = True
        if principle:
            def_ahm_code = True

        return {
            'value': {
                'ahm_code': ahm_code if def_ahm_code else False,
                'dealer_code': dealer_code if def_dealer_code else False,
            }
        }

    def showroom_ahass_change(self,
                              cr,
                              uid,
                              ids,
                              showroom,
                              ahass,
                              dealer,
                              context=None):
        value = {}
        value['dealer'] = False
        if showroom or ahass:
            value['dealer'] = True
        return {'value': value}

    def onchange_pkp(self, cr, uid, ids, pkp, context=None):
        if not pkp == False:
            return {
                'value': {
                    'npwp': '',
                    'tgl_kukuh': False,
                }
            }

        return True

    def onchange_forwarder(self, cr, uid, ids, forwarder, context=None):
        if not forwarder:
            return {
                'value': {
                    'plat_number_lines': False,
                    'driver_lines': False
                }
            }
        return True

    def name_get(self, cr, uid, ids, context=None):
        if context is None:
            context = {}
        if isinstance(ids, (int, long)):
            ids = [ids]
        res = []
        for record in self.browse(cr, uid, ids, context=context):
            name = record.name
            if record.parent_id and not record.is_company:
                name = "%s, %s" % (record.parent_name, name)
            if context.get('show_address_only'):
                name = self._display_address(cr,
                                             uid,
                                             record,
                                             without_company=True,
                                             context=context)
            if context.get('show_address'):
                name = name + "\n" + self._display_address(
                    cr, uid, record, without_company=True, context=context)
            name = name.replace('\n\n', '\n')
            name = name.replace('\n\n', '\n')
            if context.get('show_email') and record.email:
                name = "%s <%s>" % (name, record.email)
            if record.default_code:
                name = "[%s] %s %s" % (record.default_code, name,
                                       '(' + record.member +
                                       ')' if record.member else '')
            res.append((record.id, name))
        return res

    def name_search(self,
                    cr,
                    uid,
                    name,
                    args=None,
                    operator='ilike',
                    context=None,
                    limit=100):
        if not args:
            args = []
        if name and operator in ('=', 'ilike', '=ilike', 'like',
                                 '=like') and len(name) >= 3:
            self.check_access_rights(cr, uid, 'read')
            where_query = self._where_calc(cr, uid, args, context=context)
            self._apply_ir_rules(cr, uid, where_query, 'read', context=context)
            from_clause, where_clause, where_clause_params = where_query.get_sql(
            )
            where_str = where_clause and (" WHERE %s AND " %
                                          where_clause) or ' WHERE '

            # search on the name of the contacts and of its company
            search_name = name
            operator = 'like'
            if operator in ('ilike', 'like'):
                search_name = '%%%s%%' % name
            if operator in ('=ilike', '=like'):
                operator = operator[1:]
            unaccent = get_unaccent_wrapper(cr)
            where_str = where_str.replace('"res_partner"', 'p')
            query = """SELECT p.id
                         FROM res_partner p
                      {where} (upper(p.{display_name}) {operator} {percent}
                           OR upper(p.{default_code}) {operator} {percent}
                           OR upper(p.{member}) {operator} {percent})
                     ORDER BY p.{display_name}, p.{default_code}
                    """.format(where=where_str,
                               operator=operator,
                               display_name=unaccent('display_name'),
                               default_code=unaccent('default_code'),
                               member=unaccent('member'),
                               percent=unaccent('%s'))
            where_clause_params += [
                search_name.upper(),
                search_name.upper(),
                search_name.upper()
            ]
            if limit:
                query += ' limit %s'
                where_clause_params.append(limit)
            cr.execute(query, where_clause_params)
            ids = map(lambda x: x[0], cr.fetchall())

            if ids:
                return self.name_get(cr, uid, ids, context)
            else:
                return []
        return []

    # def name_search(self, cr, uid, name, args=None, operator='=', context=None, limit=100):
    #     if not args:
    #         args = []
    #     operator = '='
    #     if name and operator in ('=', 'ilike', '=ilike', 'like', '=like'):
    #         self.check_access_rights(cr, uid, 'read')
    #         where_query = self._where_calc(cr, uid, args, context=context)
    #         self._apply_ir_rules(cr, uid, where_query, 'read', context=context)
    #         from_clause, where_clause, where_clause_params = where_query.get_sql()
    #         where_str = where_clause and (" WHERE %s AND " % where_clause) or ' WHERE '
    #         if '*' in name or '%' in name:
    #             operator = 'like'
    #         if '*' in name:
    #             name = name.replace('*','%')
    #         search_name = name
    #         if operator in ('=ilike', '=like'):
    #             operator = operator[1:]
    #         unaccent = get_unaccent_wrapper(cr)
    #         where_str = where_str.replace('"res_partner"','p')
    #         query = """SELECT p.id
    #                      FROM res_partner p
    #                   {where} (upper(p.{display_name}) {operator} {percent}
    #                        OR upper(p.{default_code}) {operator} {percent}
    #                        OR upper(p.{member}) {operator} {percent})
    #                  ORDER BY p.{display_name}, p.{default_code}
    #                 """.format(where=where_str, operator=operator,
    #                            display_name=unaccent('display_name'),
    #                            default_code=unaccent('default_code'),
    #                            member=unaccent('member'),
    #                            percent=unaccent('%s'))
    #         where_clause_params += [search_name.upper(), search_name.upper(), search_name.upper()]
    #         if limit:
    #             query += ' limit %s'
    #             where_clause_params.append(limit)
    #         cr.execute(query, where_clause_params)
    #         ids = map(lambda x: x[0], cr.fetchall())
    #         if ids:
    #             return self.name_get(cr, uid, ids, context)
    #         else:
    #             return []
    #     return []

    def create(self, cr, uid, vals, context=None):
        if vals.get('default_code', 'BPA/') == 'BPA/':
            vals['default_code'] = self.pool.get('ir.sequence').get_sequence(
                cr, uid, 'BPA', division=False, padding=6)
        partner_id = super(res_partner, self).create(cr,
                                                     uid,
                                                     vals,
                                                     context=context)
        self.write(cr, uid, partner_id, {'company_id': False})
        return partner_id

    def onchange_letter(self,
                        cr,
                        uid,
                        ids,
                        sama,
                        street=None,
                        street2=None,
                        rt=None,
                        rw=None,
                        state_id=None,
                        city_id=None,
                        kecamatan_id=None,
                        kecamatan=None,
                        zip_id=None,
                        kelurahan=None,
                        context=None):
        value = {}
        if not sama:
            value = {
                'street_tab': False,
                'street2_tab': False,
                'rt_tab': False,
                'rw_tab': False,
                'state_tab_id': False,
                'city_tab_id': False,
                'kecamatan_tab_id': False,
                'kecamatan_tab': False,
                'zip_tab_id': False,
                'kelurahan_tab': False,
            }
        if sama:
            value = {
                'street_tab': street,
                'street2_tab': street2,
                'rt_tab': rt,
                'rw_tab': rw,
                'state_tab_id': state_id,
                'city_tab_id': city_id,
                'kecamatan_tab_id': kecamatan_id,
                'kecamatan_tab': kecamatan,
                'zip_tab_id': zip_id,
                'kelurahan_tab': kelurahan,
            }
        return {'value': value}

    def _onchange_kecamatan_tab(self, cr, uid, ids, kecamatan_id):
        if kecamatan_id:
            kec = self.pool.get("dym.kecamatan").browse(cr, uid, kecamatan_id)
            return {'value': {'kecamatan_tab': kec.name}}
        else:
            return {'value': {'kecamatan_tab': False}}
        return True

    def _onchange_zip_tab(self, cr, uid, ids, zip_id):
        if zip_id:
            kel = self.pool.get("dym.kelurahan").browse(cr, uid, zip_id)
            return {
                'value': {
                    'kelurahan_tab': kel.name,
                }
            }
        else:
            return {
                'value': {
                    'kelurahan_tab': False,
                }
            }
        return True

    def onchange_address(self,
                         cr,
                         uid,
                         ids,
                         street=None,
                         street2=None,
                         rt=None,
                         rw=None,
                         state_id=None,
                         city_id=None,
                         kecamatan_id=None,
                         kecamatan=None,
                         zip_id=None,
                         kelurahan=None,
                         context=None):
        value = {}
        warning = {}
        if street:
            value['street_tab'] = street
        if street2:
            value['street2_tab'] = street2
        if rt:
            if len(rt) > 3:
                warning = {
                    'title': ('Perhatian !'),
                    'message': (('RT tidak boleh lebih dari 3 digit ! ')),
                }
                value = {'rt': False}
            cek = rt.isdigit()
            if not cek:
                warning = {
                    'title': ('Perhatian !'),
                    'message': (('RT hanya boleh angka ! ')),
                }
                value = {'rt': False}
            else:
                value['rt_tab'] = rt
        if rw:
            if len(rw) > 3:
                warning = {
                    'title': ('Perhatian !'),
                    'message': (('RW tidak boleh lebih dari 3 digit ! ')),
                }
                value = {'rw': False}
            cek = rw.isdigit()
            if not cek:
                warning = {
                    'title': ('Perhatian !'),
                    'message': (('RW hanya boleh angka ! ')),
                }
                value = {'rw': False}
            else:
                value['rw_tab'] = rw
        if state_id:
            value['state_tab_id'] = state_id
        if city_id:
            value['city_tab_id'] = city_id
        if kecamatan_id:
            kec = self.pool.get("dym.kecamatan").browse(cr, uid, kecamatan_id)
            value['kecamatan_tab_id'] = kecamatan_id
            value['kecamatan_tab'] = kec.name
            value['kecamatan'] = kec.name
        if zip_id:
            kel = self.pool.get("dym.kelurahan").browse(cr, uid, zip_id)
            value['zip_tab_id'] = zip_id
            value['kelurahan_tab'] = kel.name
            value['kelurahan'] = kel.name
        return {'value': value, 'warning': warning}

    def change_nomor(self, cr, uid, ids, nohp, notelp, context=None):
        value = {}
        warning = {}
        # if nohp :
        #     if len(nohp) > 13 :
        #         warning = {
        #             'title': ('Perhatian !'),
        #             'message': (('No HP tidak boleh lebih dari 13 digit ! ')),
        #         }
        #         value = {
        #                  'no_hp':False
        #                  }
        #     else :
        #         cek = nohp.isdigit()
        #         if not cek :
        #             warning = {
        #                 'title': ('Perhatian !'),
        #                 'message': (('No HP hanya boleh angka ! ')),
        #             }
        #             value = {
        #                      'no_hp':False
        #                      }
        # if notelp :
        #     if len(notelp) > 11 :
        #         warning = {
        #             'title': ('Perhatian !'),
        #             'message': (('No Telepon tidak boleh lebih dari 11 digit ! ')),
        #         }
        #         value = {
        #                  'no_telp':False
        #                  }
        #     else :
        #         cek = notelp.isdigit()
        #         if not cek :
        #             warning = {
        #                 'title': ('Perhatian !'),
        #                 'message': (('No Telepon hanya boleh angka ! ')),
        #             }
        #             value = {
        #                      'no_telp':False
        #                      }
        return {'warning': warning, 'value': value}

    def onchange_punctuation(self, cr, uid, ids, no_ktp, context=None):
        value = {}
        warning = {}
        if no_ktp:
            if no_ktp == '0':
                value = {'no_ktp': no_ktp}
            elif no_ktp != '0' and len(no_ktp) == 16:
                # if no_ktp :
                ktp = self.search(cr, uid, [('no_ktp', '=', no_ktp)])
                if ktp:
                    warning = {
                        'title': ('Perhatian !'),
                        'message':
                        (('No KTP %s sudah pernah dibuat ! ') % (no_ktp)),
                    }
                    value = {'no_ktp': False}
                if not warning:
                    no_ktp = "".join(l for l in no_ktp
                                     if l not in string.punctuation)
                    value = {'no_ktp': no_ktp}
            elif no_ktp != '0' and len(no_ktp) != '16':
                warning = {
                    'title': ('Perhatian !'),
                    'message': (('No KTP harus 16 digit ! ')),
                }
                value = {'no_ktp': False}
        return {'value': value, 'warning': warning}
Exemple #4
0
class electronic_biller(models.Model):
    _name = 'electronic.biller.cr'
    _description = 'Electronic Biller CR'

    def _get_providers(self):
        return [('base', 'Default'), ('medical_cr', 'Medical Costa Rica')]

    # indirection to ease inheritance
    _provider_selection = lambda self, *args, **kwargs: self._get_providers(
        *args, **kwargs)
    active = fields.Boolean('Activo', default=True)
    version = fields.Char('Documentation version', select=True)
    current_regulation_rs_number = fields.Char(
        'Current Regulations - Resolution Number', size=13, select=True)
    current_regulation_rs_date = fields.Char(
        'Current Regulations - Resolution Date', size=20, select=True)
    current_regulation_rs_date_text = fields.Char(
        'Current Regulations - Resolution Date Text', select=True)
    name = fields.Char('Name')
    environment = fields.Selection([('test', 'Test'), ('prod', 'Production')],
                                   string='Environment',
                                   index=True,
                                   default='test')
    provider = fields.Selection(_provider_selection,
                                string='Provider',
                                index=True,
                                default='base',
                                select=True)
    company_id = fields.Many2one(
        'res.company',
        'Company',
        default=api.model(lambda self: self.env.user.company_id))
    company_name = fields.Char('Company Name')
    host = fields.Char('Servidor', required=True, copy=False)
    port = fields.Integer('Puerto', copy=False)
    host_test = fields.Char('Servidor test', copy=False)
    port_test = fields.Integer('Puerto test', copy=False)
    username = fields.Char('Usuario', copy=False)
    client_id_test = fields.Char('Id Cliente', copy=False, default='api-prod')
    client_id = fields.Char('Id Cliente', copy=False, default='api-prod')
    client_password = fields.Char(string='Client Password')
    password = fields.Char('Contraseña', copy=False)
    file_mode = fields.Selection([('path', 'Path del archivo'),
                                  ('base64', 'Binario'), ('text', 'Texto')],
                                 'Forma del archivo',
                                 required=True,
                                 default='text')
    invoice_template = fields.Text('Invoices')
    refund_template = fields.Text('Credit Notes')
    debit_note_template = fields.Text('Debit Notes')
    electronic_ticket_template = fields.Text('Electronic Ticket')
    accuse_acceptance_template = fields.Text('Accuse Acceptance')
    rejection_of_document_template = fields.Text('Rejection of Documents')
    library = fields.Char(
        'Librería',
        required=False,
        help='Define la librería python que usará como canal de conexión.')
    send_doc = fields.Text('Método para enviar un documento', required=False)
    check_doc = fields.Text('Método para comprobar un documento',
                            required=False)
    auth_endpoint_test = fields.Char(
        string='URL de Autenticación Pruebas(Token)',
        default=
        'https://idp.comprobanteselectronicos.go.cr/auth/realms/rut-stag/protocol/openid-connect/token'
    )
    auth_endpoint_prod = fields.Char(
        string='URL de Autenticación Producción(Token)',
        default=
        'https://idp.comprobanteselectronicos.go.cr/auth/realms/rut/protocol/openid-connect/token'
    )

    scope = fields.Char()  # OAUth user data desired to access
    validation_endpoint = fields.Char(
        string='Validation URL')  # OAuth provider URL to validate tokens
    data_endpoint = fields.Char(string='Data URL')
    key_store_file = fields.Binary(string='Archivo LLave', attachment=True)
    key_store_pswd = fields.Char(string='Password de la Llave',
                                 size=300,
                                 required=False)
    key_store_path = fields.Char(string='Ruta de la llave',
                                 size=300,
                                 required=False)
    use_jar = fields.Boolean('Use Jar', default=True)
    jar_path = fields.Char('Jar Path')
    emission_type = fields.Selection(EMISSION_TYPE,
                                     'Tipo de Emision',
                                     default='1',
                                     required=True)
    mh_receipt_test_wsdl = fields.Char(
        'URL Recepción - Pruebas',
        size=300,
        default=
        'https://api.comprobanteselectronicos.go.cr/recepcion-sandbox/v1/',
        required=False)
    mh_receipt_prod_wsdl = fields.Char(
        'URL Recepción - Producción',
        size=300,
        default='https://api.comprobanteselectronicos.go.cr/recepcion/v1/',
        required=False)
    mh_check_test_wsdl = fields.Char(
        'URL Chequeo - Pruebas',
        size=300,
        default=
        'https://api.comprobanteselectronicos.go.cr/recepcion-sandbox/v1/',
        required=False)

    mh_check_prod_wsdl = fields.Char(
        'URL Chequeo - Producción',
        size=300,
        default='https://api.comprobanteselectronicos.go.cr/recepcion/v1/',
        required=False)
    version_doc = fields.Char('Versión del Documento', default='Version 4.2')

    def _display_address(self, address, without_company=False):

        if hasattr(self, '%s_display_address' % self.provider):
            return getattr(self, '%s_display_address' % self.provider)(
                address, without_company=without_company)

        address_format = address.country_id and address.country_id.address_format or \
              "%(street)s\n%(street2)s\n%(city)s %(state_code)s %(zip)s\n%(country_name)s"
        args = {
            'state_code': address.state_id and address.state_id.code or '',
            'state_name': address.state_id and address.state_id.name or '',
            'country_code': address.country_id and address.country_id.code
            or '',
            'country_name': address.country_id and address.country_id.name
            or '',
        }
        for field in address._address_fields():
            args[field] = getattr(address, field) or ''
        if without_company:
            args['company_name'] = ''
        return address_format % args

    def get_dict_data(self, vals):
        if hasattr(self, '%s_get_dict_data' % vals['provider']):
            return getattr(self, '%s_get_dict_data' % vals['provider'])(vals)

        return {
            'name': vals['name'],
            'environment': vals['environment'],
            'provider': vals['provider'],
            'context_type': 'center',
        }

    def get_wdict_data(self, vals):
        self.ensure_one()
        if hasattr(self, '%s_get_wdict_data' % self.provider):
            return getattr(self, '%s_get_wdict_data' % self.provider)(vals)

        return {}

    def base_get_dict_data(self, vals):
        base_url = self.env['ir.config_parameter'].get_param('web.base.url')
        return {
            'name': vals['name'],
            'environment': vals['environment'],
            'provider': vals['provider'],
            'context_type': 'center',
            'instance': base_url,
            'company_name': self.env.user.company_id.name,
        }

    def base_get_wdict_data(self, vals):
        base_url = self.env['ir.config_parameter'].get_param('web.base.url')
        return {
            'name': vals.get('name', self.name),
            'environment': vals.get('environment', self.environment),
            'provider': vals.get('provider', self.provider),
            'context_type': 'center',
            'instance': base_url,
            'company_name': self.env.user.company_id.name,
        }

    @api.model
    def create(self, vals):
        biller_id = super(electronic_biller, self).create(vals)
        return biller_id

    @api.multi
    def write(self, vals):
        res = super(electronic_biller, self).write(vals)
        secret = self.env['ir.config_parameter'].sudo().get_param(
            'database.secret')
        return res

    @api.multi
    def send_document(self, doc, number):
        description = doc._name + (
            ('.%s' % doc.type) if hasattr(doc, 'type') else '')
        description += (('.%s' % doc.tipo_factura) if hasattr(
            doc, 'tipo_factura') else '')
        messages = []
        safe_dict = {
            self.library: __import__(self.library),
            'datetime': datetime,
            'server': self,
            'doc': doc,
            'ebi_doc': self.generate_doc(doc),
            'doc_name': number,
            'description': description,
            'ValidationError': ValidationError,
            'hasattr': hasattr,
            'messages': messages
        }
        if self.file_mode == 'path':
            with tempfile.NamedTemporaryFile() as f:
                f.write(safe_dict['ebi_doc'].encode('utf-8'))
                f.flush()
                safe_dict['ebi_doc'] = f.name
                try:
                    exec(self.send_doc, {"__builtins__": None}, safe_dict)
                except Exception as e:
                    raise e
        else:
            try:
                exec(self.send_doc, {"__builtins__": None}, safe_dict)
            except Exception as e:
                raise e
        for message in messages:
            message.update(res_id=doc.id, model_name=doc._name)
            self.env['ebi.doc.message'].create(message)

    @api.multi
    def check_document(self, doc, number):
        description = doc._name + (
            ('.%s' % doc.type) if hasattr(doc, 'type') else '')
        description += (('.%s' % doc.ebi_voucher_type) if hasattr(
            doc, 'tipo_factura') else '')
        attachments, messages = [], []
        safe_dict = {
            self.library: __import__(self.library),
            'datetime': datetime,
            'server': self,
            'doc': doc,
            'doc_name': number,
            'description': description,
            'documents': attachments,
            'ValidationError': ValidationError,
            'hasattr': hasattr,
            'messages': messages
        }
        try:
            exec(self.check_doc, {"__builtins__": None}, safe_dict)
        except Exception as e:
            raise e
        for attach_name, attachment in attachments:
            if attach_name.split('.')[-1] == 'xml':
                vals = self.xml_vals(attachment)
                if vals.get('ebi_state', '').upper(
                ) == 'AUTORIZADO' and vals.get('ebi_auth_key'):
                    doc.write(dict(vals, **{'ebi_state': 'auth'}))
                else:
                    return
            self.env['ir.attachment'].create({
                'name':
                attach_name,
                'type':
                'binary',
                'datas':
                base64.encodestring(attachment),
                'datas_fname':
                attach_name,
                'res_model':
                doc._name,
                'res_id':
                doc.id
            })
        for message in messages:
            message.update(res_id=doc.id,
                           model_name=doc._name,
                           access_key=doc.ebi_access_key)
            self.env['ebi.doc.message'].create(message)

    @api.model
    def xml_vals(self, xml, doc_string=True):
        if doc_string:
            dom = minidom.parseString(xml)
        else:
            dom = minidom.parse(xml)

        def get_xml_value(dom, completeName):
            res = None
            tagName, sep, completeName = completeName.partition('.')
            elements = dom.getElementsByTagName(tagName)
            if len(elements) != 0:
                res = elements[0].childNodes[0].nodeValue
                if completeName:
                    res = get_xml_value(elements[0], completeName)
            return res

        return {
            'ebi_access_key':
            get_xml_value(dom, 'claveAccesoConsultada'),
            # 'num_comp': get_xml_value(dom, 'numeroComprobantes'),
            'ebi_state':
            get_xml_value(dom, 'autorizaciones.autorizacion.estado'),
            'ebi_auth_key':
            get_xml_value(dom,
                          'autorizaciones.autorizacion.numeroAutorizacion'),
            'ebi_auth_date':
            get_xml_value(dom,
                          'autorizaciones.autorizacion.fechaAutorizacion'),
            'ebi_environment':
            get_xml_value(dom, 'autorizaciones.autorizacion.ambiente'),
            # 'ebi_document': get_xml_value(dom, 'autorizaciones.autorizacion.comprobante'),
        }

    @api.model
    def oaut2_autenthication_cr(self, document_generic):
        logging.basicConfig()
        logging.getLogger().setLevel(logging.DEBUG)
        requests_log = logging.getLogger("requests.packages.urllib2")
        requests_log.setLevel(logging.DEBUG)
        requests_log.propagate = True

        user_api = document_generic.company_id.mh_oauth_username
        user_pass = document_generic.company_id.mh_oauth_password
        ACCESS_TOKEN_URL = self.auth_endpoint_test

        if self.environment == 'prod':
            ACCESS_TOKEN_URL = self.auth_endpoint_prod

        cl_id = self.client_id_test

        if self.environment == 'prod':
            cl_id = self.client_id

        headers = {'Content-Type': 'application/x-www-form-urlencoded'}

        data_send = {
            'grant_type': 'password',
            'client_id': cl_id,
            'client_secret': '',
            'username': user_api,
            'password': user_pass,
            'scope': ''
        }

        # print "data de token ", data_send

        data_send['content'] = data_send

        response = requests.post(ACCESS_TOKEN_URL,
                                 data=data_send,
                                 headers=headers)
        # print response.content

        # aqui poner exception que hubo error
        if response.status_code != 200:
            _logger.error("---->A ocurrido un error en la autenticacion: " +
                          response.content)
            if response.status_code == 401:
                raise UserError(
                    _("A ocurrido un error en la autenticación, las credenciales no están autorizadas."
                      ))
            raise UserError(
                _("A ocurrido un error en la autenticación: ") +
                response.content)

        # print "respuesta ", response.json()
        response_data = response.json()
        cr_token = response_data['access_token']

        return cr_token

    @api.model
    def _filestore(self):
        return config.filestore(self._cr.dbname)

    @api.model
    def _full_path(self, path):
        path = re.sub('[.]', '.', path)
        path = path.strip('/\\')
        return os.path.join(self._filestore(), path)

    @api.model
    def query_cr(self, document_generic):
        self.ensure_one()
        logging.basicConfig()
        logging.getLogger().setLevel(logging.DEBUG)
        requests_log = logging.getLogger("requests.packages.urllib2")
        requests_log.setLevel(logging.DEBUG)
        requests_log.propagate = True

        clave = document_generic.mh_access_key
        QUERY_URL = self.mh_check_test_wsdl

        if self.environment == 'prod':
            QUERY_URL = self.mh_check_prod_wsdl

        token_cr = self.oaut2_autenthication_cr(document_generic)

        headers = {
            'Content-Type': 'application/x-www-form-urlencoded',
            'Authorization': "bearer " + token_cr
        }

        url_last = QUERY_URL + "recepcion/" + clave
        # print "query ", url_last, token_cr

        response = requests.get(url_last, headers=headers)
        # print response.content

        if response.status_code != 200:
            _logger.error(
                "---->A ocurrido un error en la consulta del comprobante: " +
                response.content)
            if response.status_code in [400, 401]:
                text_excp_ = str(response.headers._store['x-error-cause'][1])
                message_title = "ERROR"
                message_msg = text_excp_
                state = 'ERROR'
                res_id = document_generic.obj_id
                model_name = document_generic.obj_model
                sequence_msg = len(document_generic.env[model_name].browse(
                    res_id).ebi_messages_ids) + 1
                message_data = {
                    'title': message_title,
                    'message': message_msg,
                    'state': state,
                    'access_key': document_generic.mh_access_key,
                    'res_id': res_id,
                    'model_name': model_name,
                    'type': 'interno',
                    'sequence': sequence_msg,
                    'edig_id': document_generic.id,
                    'invoice_id': document_generic.invoice_id.id,
                }
                self.env['ebi.doc.message'].create(message_data)
                document_generic.message_post(body=message_title + ' - ' +
                                              message_msg)
                document_generic.invoice_id.message_post(body=message_title +
                                                         ' - ' + message_msg)
                return self.env['cr.wizard.message'].generated_message(
                    message_msg, name=message_title)

            text_excp_ = "Estado " + str(response.status_code) + " - " + str(
                response.headers)
            message_title = "ERROR"
            message_msg = text_excp_
            state = 'ERROR'
            res_id = document_generic.obj_id
            model_name = document_generic.obj_model
            sequence_msg = len(document_generic.env[model_name].browse(
                res_id).ebi_messages_ids) + 1
            message_data = {
                'title': message_title,
                'message': message_msg,
                'state': state,
                'access_key': document_generic.mh_access_key,
                'res_id': res_id,
                'model_name': model_name,
                'type': 'interno',
                'sequence': sequence_msg,
                'edig_id': document_generic.id,
                'invoice_id': document_generic.invoice_id.id,
            }
            self.env['ebi.doc.message'].create(message_data)
            document_generic.message_post(body=message_title + ' - ' +
                                          message_msg)
            document_generic.invoice_id.message_post(body=message_title +
                                                     ' - ' + message_msg)
            return self.env['cr.wizard.message'].generated_message(
                message_msg, name=message_title)
            # raise UserError(_("A ocurrido un error en la consulta del comprobante: ") + response.content)

        # print "respuesta ", response.json()
        _logger.error("---->Respuesta de la consulta del comprobante: " +
                      str(response) + " ->>> Contenido: " +
                      str(response.content))
        response_data = False
        res = False
        if response.json():
            response_data = response.json()
            if response_data.get('ind-estado', False):
                if response_data['ind-estado'] in ['rechazado', 'aceptado']:
                    respues_xml = response_data['respuesta-xml']
                    res = base64.b64decode(respues_xml)
                    document_generic.response_xml = res
                    message_doc = etree.XML(str(res))
                    # print "xml complet", message_doc

                    # root = message_doc.getroot()
                    mensaje_text_extract = res
                    message_type = ''
                    for child in message_doc.iter():
                        # print "contenido tag ", str(child.tag).split('}')
                        if 'DetalleMensaje' in str(child.tag):
                            mensaje_text_extract = child.text
                        if 'Mensaje' == str(child.tag).split('}')[1]:
                            message_type = child.text

                    text_excp_ = mensaje_text_extract
                    message_title = "ERROR"
                    message_msg = text_excp_
                    status = 'ERROR'
                    date_auth = ''

                    if int(message_type) == 1:
                        message_title = "HACIENDA: COMPROBANTE ACEPTADO"
                        status = 'ACEPTADO'
                        date_auth = ''

                    if int(message_type) == 3:
                        message_title = "HACIENDA: COMPROBANTE RECHAZADO"
                        status = 'RECHAZADO'

                    document_generic.update_status(status, date_auth,
                                                   message_msg)
                else:
                    if response_data['ind-estado'] == 'error':
                        text_excp_ = "HACIENDA DEVOLVIO ERROR EN EL ENVIO, SIN DETALLE"
                        message_title = "ERROR"
                        message_msg = text_excp_
                        status = 'ERROR'
                        date_auth = ''
                        message_title = "HACIENDA: COMPROBANTE RECHAZADO CON ERROR"
                        # status = 'RECHAZADO'
                        res = document_generic.update_status(
                            status, date_auth, message_msg)
                        # return self.env['cr.wizard.message'].generated_message(message_msg, name=message_title)

        return res

    @api.model
    def date_format_doc(self, date_t, format='%Y-%m-%dT%H:%M:%S'):
        # date = datetime.strptime(date_t, '%Y-%m-%d')
        tzinfo = timezone('America/Costa_Rica')
        start_date = datetime.strptime(date_t, '%Y-%m-%d %H:%M:%S')
        diffHoraria = int(tzinfo.localize(start_date).strftime('%z')) / 100
        start_date = start_date + timedelta(hours=diffHoraria)
        return start_date.strftime(format)

    @api.multi
    def send_signed_xml(self, document_generic, raise_error=False):
        self.ensure_one()
        fname_signed_xml = str(self.env.user.id) + '_' + str(
            document_generic.invoice_id.ebi_voucher_type) + '_' + str(
                document_generic.invoice_id.id) + '_byuser_signed.xml'
        full_path_signed_xml = self._full_path(fname_signed_xml)

        REDIRECT_URI = self.mh_receipt_test_wsdl
        if self.environment == 'prod':
            REDIRECT_URI = self.mh_receipt_prod_wsdl

        token_cr = self.oaut2_autenthication_cr(document_generic)

        try:
            byt_file = False
            with open(full_path_signed_xml) as file:
                f = file.read()
                byt_file = bytearray(f)

            headers = {
                'Content-Type': 'application/json',
                'Authorization': "bearer " + token_cr
            }

            # date_doc = document_generic.invoice_id.date_invoice
            date_doc = document_generic.invoice_id.ebi_send_date
            date_convert = self.date_format_doc(date_doc)
            # print "fecha de doc", date_convert

            jsonString = {}
            jsonString['clave'] = document_generic.invoice_id.ebi_access_key
            jsonString['fecha'] = date_convert
            jsonString['emisor'] = {}
            jsonString['emisor'][
                'tipoIdentificacion'] = document_generic.invoice_id.company_id.partner_id.identification_type
            jsonString['emisor'][
                'numeroIdentificacion'] = document_generic.invoice_id.company_id.partner_id.identification_cr
            jsonString['receptor'] = {}
            jsonString['receptor'][
                'tipoIdentificacion'] = document_generic.invoice_id.identification_type
            jsonString['receptor'][
                'numeroIdentificacion'] = document_generic.invoice_id.identification_cr
            jsonString['comprobanteXml'] = base64.b64encode(byt_file)

            response = requests.post(REDIRECT_URI + 'recepcion',
                                     json=jsonString,
                                     headers=headers)
        except (Exception, ) as e:
            _logger.error(serialize_exception(e))
            _logger.info('Error %s' % str(e))
            raise UserError(_("A ocurrido un error enviando el xml "))

        print 'POST /service {}'.format(response.status_code)
        _logger.info('Respuesta Valiacion %s' % str(response))
        _logger.info('Contenido Respuesta Valiacion %s' %
                     str(response.content))

        # aqui poner exception que hubo error
        if response.status_code != 200:
            _logger.error(
                "---->A ocurrido un error enviando el comprobante: " +
                response.content)
            if response.status_code in [400, 401]:
                text_excp_ = str(response.headers._store['x-error-cause'][1])
                message_title = "ERROR"
                message_msg = text_excp_
                state = 'ERROR'
                res_id = document_generic.obj_id
                model_name = document_generic.obj_model
                sequence_msg = len(document_generic.env[model_name].browse(
                    res_id).ebi_messages_ids) + 1
                message_data = {
                    'title': message_title,
                    'message': message_msg,
                    'state': state,
                    'access_key': document_generic.mh_access_key,
                    'res_id': res_id,
                    'model_name': model_name,
                    'type': 'interno',
                    'sequence': sequence_msg,
                    'edig_id': document_generic.id,
                    'invoice_id': document_generic.invoice_id.id,
                }
                self.env['ebi.doc.message'].create(message_data)
                document_generic.message_post(body=message_title + ' - ' +
                                              message_msg)
                document_generic.invoice_id.message_post(body=message_title +
                                                         ' - ' + message_msg)
                return self.env['cr.wizard.message'].generated_message(
                    message_msg, name=message_title)
                # raise ValidationError(
                #     _("A ocurrido un error enviando el comprobante: ") + str(response.headers._store['x-error-cause'][1]))

            # if response.status_code in [202]:
            #     raise ValidationError(
            #         _("A ocurrido un error enviando el comprobante: ") + str(
            #             response.headers))
            if response.status_code not in [202]:
                text_excp_ = "Estado " + str(
                    response.status_code) + " - " + str(response.headers)
                message_title = "ERROR"
                message_msg = text_excp_
                state = 'ERROR'
                res_id = document_generic.obj_id
                model_name = document_generic.obj_model
                sequence_msg = len(document_generic.env[model_name].browse(
                    res_id).ebi_messages_ids) + 1
                message_data = {
                    'title': message_title,
                    'message': message_msg,
                    'state': state,
                    'access_key': document_generic.mh_access_key,
                    'res_id': res_id,
                    'model_name': model_name,
                    'type': 'interno',
                    'sequence': sequence_msg,
                    'edig_id': document_generic.id,
                    'invoice_id': document_generic.invoice_id.id,
                }
                self.env['ebi.doc.message'].create(message_data)
                document_generic.message_post(body=message_title + ' - ' +
                                              message_msg)
                document_generic.invoice_id.message_post(body=message_title +
                                                         ' - ' + message_msg)
                return self.env['cr.wizard.message'].generated_message(
                    message_msg, name=message_title)
                # raise ValidationError(
                #     _("A ocurrido un error enviando el comprobante: ") + str(response.headers))

        _logger.error("Respuesta " + ustr(response.content))

        _logger.info('LLAMANDO SERVICIO verificar estado %s' %
                     str(datetime.now()))
        count = 0
        timeout = False
        time.sleep(50L)
        response_query = self.query_cr(document_generic)
        if type(response_query) is dict:
            return dict(response_query)
        # print "respuesta del estado", response_query
        return response_query

    @api.multi
    def resend_document(self, document_generic, raise_error=False):
        self.ensure_one()
        response_query = self.query_cr(document_generic)
        print "respuesta del estado", response_query
        return response_query

    @api.multi
    def get_status_documents(self):
        self.ensure_one()
        response_query = self.query_cr('')
        print "respuesta del estado", response_query
        return response_query
Exemple #5
0
class Project(models.Model):
    _name = 'bestja.project'
    _inherit = ['message_template.mixin']

    def current_members(self):
        """
        Limit to members of the current organization only.
        """
        try:
            # try to use organization configured in the current project
            project = self.browse([self.env.context['params']['id']])
            organization = project.organization
        except KeyError:
            # most likely a new project, use organization the user coordinates
            organization = self.env.user.coordinated_org
        return [
            '|',  # noqa odoo-domain indent
            ('id', 'in', organization.volunteers.ids),
            ('coordinated_org', '=', organization.id),
        ]

    name = fields.Char(required=True, string="Nazwa")
    organization = fields.Many2one(
        'organization',
        default=api.model(lambda self: self.env.user.coordinated_org),
        required=True,
        string="Organizacja",
        domain=lambda self: [('coordinator', '=', self.env.user.id)],
    )
    manager = fields.Many2one(
        'res.users',
        domain=current_members,
        string="Menadżer projektu",
    )
    responsible_user = fields.Many2one('res.users',
                                       string="Osoba odpowiedzialna",
                                       compute='_responsible_user')
    date_start = fields.Datetime(
        required=True,
        string="od dnia",
    )
    date_stop = fields.Datetime(
        required=True,
        string="do dnia",
    )
    members = fields.Many2many('res.users',
                               relation='project_members_rel',
                               column1='project',
                               column2='member',
                               domain=current_members,
                               string="Zespół")
    tasks = fields.One2many('bestja.task', 'project', string="Zadania")
    tasks_count = fields.Integer(compute='_tasks_count', string="Liczba zadań")
    done_tasks_count = fields.Integer(compute='_tasks_count',
                                      string="Liczba skończonych zadań")

    @api.one
    @api.depends('manager', 'organization.coordinator')
    def _responsible_user(self):
        if self.manager:
            self.responsible_user = self.manager
        else:
            self.responsible_user = self.organization.coordinator

    @api.one
    @api.depends('tasks')
    def _tasks_count(self):
        self.tasks_count = len(self.tasks)
        self.done_tasks_count = self.tasks.search_count([
            ('project', '=', self.id), ('state', '=', 'done')
        ])

    @api.model
    def create(self, vals):
        record = super(Project, self).create(vals)
        record.send(
            template='bestja_project.msg_manager',
            recipients=record.manager,
        )
        record.manager.sync_manager_groups()
        return record

    @api.multi
    def write(self, vals):
        old_manager = None
        if 'manager' in vals:
            # Manager changed. Keep the old one.
            old_manager = self.manager
        val = super(Project, self).write(vals)
        if old_manager:
            self.send(
                template='bestja_project.msg_manager',
                recipients=self.manager,
            )
            self.send(
                template='bestja_project.msg_manager_changed',
                recipients=old_manager,
            )
            self.manager.sync_manager_groups()
            old_manager.sync_manager_groups()
        return val

    @api.one
    @api.constrains('date_start', 'date_stop')
    def _check_project_dates(self):
        """
            Date of the beginning of the project needs to be
            before the end
        """
        if (self.date_start > self.date_stop):
            raise exceptions.ValidationError(
                "Data rozpoczęcia projektu musi być przed datą zakończenia.")
Exemple #6
0
                                        help="Used During Inventory Synchronization From Magento to Odoo."),
        'location_id': fields.related('warehouse_id', 'lot_stock_id', type='many2one', relation='stock.location', string='Location'),

        'create_date': fields.datetime('Created Date'),
        'correct_mapping': fields.boolean('Correct Mapping'),
        'route_id': fields.many2one('stock.location.route', 'Route',
                                    help="Used During Sale Order From Magento to Odoo."),
    }
    _defaults = {
        'correct_mapping': True,
        'instance_name': _default_instance_name,
        'active': lambda *a: 1,
        'auto_ship': lambda *a: 1,
        'auto_invoice': lambda *a: 1,
        'credential': lambda *a: 1,
        'language': api.model(lambda self: self.env.lang),
        'category': _default_category,
        'state': 'enable',
        'inventory_sync': 'enable',
        'notify': lambda *a: 1,
        'warehouse_id': lambda self, cr, uid, c: self.pool.get('sale.order')._get_default_warehouse(cr, uid, context=c),
    }

    def create(self, cr, uid, vals, context=None):
        context = dict(context or {})
        active_ids = self.pool.get('magento.configure').search(
            cr, uid, [('active', '=', True)])
        if vals['active']:
            if active_ids:
                raise osv.except_osv(_('Warning'), _(
                    "Sorry, Only one active connection is allowed."))
									help="Used During Inventory Synchronization From Magento to Odoo."),
		'location_id': fields.related('warehouse_id', 'lot_stock_id', type='many2one', relation='stock.location', string='Location'),
		
		'create_date':fields.datetime('Created Date'),
		'correct_mapping':fields.boolean('Correct Mapping'),
		'route_id':fields.many2one('stock.location.route','Route', 
									help="Used During Sale Order From Magento to Odoo."),
	}
	_defaults = {
		'correct_mapping':True,
		'instance_name':_default_instance_name,
		'active':lambda *a: 1,	
		'auto_ship':lambda *a: 1,
		'auto_invoice':lambda *a: 1,
		'credential':lambda *a: 1,
		'language': api.model(lambda self: self.env.lang),
		'category':_default_category,
		'state':'enable',
		'inventory_sync':'enable',
		'notify':lambda *a: 1,
		'warehouse_id':lambda self, cr, uid, c: self.pool.get('sale.order')._get_default_warehouse(cr, uid, context=c),
	}
	
	def create(self, cr, uid, vals, context=None):
		active_ids = self.pool.get('magento.configure').search(cr, uid, [('active','=',True)])		
		if vals['active']:
			if active_ids:
				raise osv.except_osv(_('Warning'), _("Sorry, Only one active connection is allowed."))
		#vals['instance_name'] = self.pool.get('ir.sequence').get(cr, uid, 'magento.configure')
		return super(magento_configure, self).create(cr, uid, vals, context=context)