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()
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
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}
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
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.")
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)