class RetailMachine(geo_model.GeoModel): """GEO OSV SAMPLE""" _name = "geoengine.demo.automatic.retailing.machine" the_point = geo_fields.GeoPoint('Coordinate') the_line = geo_fields.GeoLine('Power supply line', index=True) total_sales = fields.Float('Total sale', index=True) money_level = fields.Char('Money level', size=32, index=True) state = fields.Selection([('hs', 'HS'), ('ok', 'OK')], 'State', index=True) name = fields.Char('Serial number', size=64, required=True) zip_id = fields.Many2one('dummy.zip') @api.onchange('the_point') def onchange_geo_point(self): """ Exemple of on change on the point Lookup in zips if the code is within an area. Change the zip_id field accordingly """ if self.the_point: zip_match = self.env['dummy.zip'].geo_search( geo_domain=[('the_geom', 'geo_contains', self.the_point)], limit=1 ) if zip_match: self.zip_id = zip_match[0]
class ProjectProject(geo_model.GeoModel): """Add geo_point to project.project""" _inherit = 'project.project' geo_point = fields.GeoPoint('Addresses coordinate', related='partner_id.geo_point')
class ResPartner(geo_model.GeoModel): """Add geo_point to partner using a function field""" _inherit = "res.partner" @api.one def geocode_address(self): """Get the latitude and longitude by requesting "mapquestapi" see http://open.mapquestapi.com/geocoding/ """ url = 'http://nominatim.openstreetmap.org/search' pay_load = { 'limit': 1, 'format': 'json', 'street': self.street or '', 'postalCode': self.zip or '', 'city': self.city or '', 'state': self.state_id and self.state_id.name or '', 'country': self.country_id and self.country_id.name or '', 'countryCodes': self.country_id and self.country_id.code or ''} request_result = requests.get(url, params=pay_load) try: request_result.raise_for_status() except Exception as e: _logger.exception('Geocoding error') raise exceptions.Warning(_( 'Geocoding error. \n %s') % e.message) vals = request_result.json() vals = vals and vals[0] or {} self.write({ 'partner_latitude': vals.get('lat'), 'partner_longitude': vals.get('lon'), 'date_localization': fields.Date.today()}) @api.one def geo_localize(self): self.geocode_address() return True @api.one @api.depends('partner_latitude', 'partner_longitude') def _get_geo_point(self): """ Set the `geo_point` of the partner depending of its `partner_latitude` and its `partner_longitude` **Notes** If one of those parameters is not set then reset the partner's geo_point and do not recompute it """ if not self.partner_latitude or not self.partner_longitude: self.geo_point = False else: self.geo_point = geo_fields.GeoPoint.from_latlon( self.env.cr, self.partner_latitude, self.partner_longitude) geo_point = geo_fields.GeoPoint( readonly=True, store=True, compute='_get_geo_point')
class company(geo_model.GeoModel): _name = 'bedit_ecoles.company' _order = 'name asc' _description = 'Company' name = fields.Char(string='Name', required=True) address = fields.Char(string='Address', required=False) polnum = fields.Char(string='Polnum', required=False) the_geom = geo_fields.GeoPoint(required=True, srid=31370, gist_index=True) postcode_id = fields.Many2one('bedit_ecoles.postcode', string="Postcode") municipality_id = fields.Many2one('bedit_ecoles.municipality', string="Municipality")
class activity(geo_model.GeoModel): _name = 'bedit_ecoles.activity' _order = 'name asc' _description = 'Activity' description = fields.Char(string='Description', size=50) @api.model def _year_between(self): years = [] year_min = 2010 year_max = (date.today().year) + 1 for x in range(year_min, year_max): years.append((str(x), str(x))) return years def _get_this_year(self): return str(date.today().year) year = fields.Selection('_year_between', string="Year", default=lambda self: self._get_this_year(), required=True) #TODO add min/max val school_id = fields.Many2one('bedit_ecoles.school', string="School", required=True) company_id = fields.Many2one('bedit_ecoles.company', string="Company", required=True) number = fields.Integer(string='Number of participant') @api.depends('school_id', 'company_id', 'year') def _get_name(self): for record in self: name = '' if (record.year and record.school_id and record.company_id): name = '%s / %s (%s)' % (record.school_id.name, record.company_id.name, record.year) record.name = name name = fields.Char(compute='_get_name') school_name = fields.Char(related='school_id.name', store=False) school_muni = fields.Char(related='school_id.municipality_id.name', store=False) school_adr = fields.Char(related='school_id.address', store=False) school_type = fields.Char(related='school_id.stype_id.name', store=False) the_geom = geo_fields.GeoPoint(related='school_id.the_geom', store=False)
class DummyModel(GeoModel): _name = 'test.dummy' name = fields.Char(string="Zip", size=64, required=True) the_geom = geo_fields.GeoMultiPolygon(string="NPA Shape") geo_point = geo_fields.GeoPoint(string="Point")
class ResPartner(geo_model.GeoModel): """Auto geo coding of addresses""" _inherit = "res.partner" geo_point = fields.GeoPoint('Addresses coordinate', readonly=True) def _can_geocode(self): usr = self.env['res.users'] return usr.browse(self.env.uid).company_id.enable_geocoding def _get_point_from_reply(self, answer): """Parse geoname answer code inspired by geopy library""" def get_first_text(node, tag_names, strip=None): """Get the text value of the first child of ``node`` with tag ``tag_name``. The text is stripped using the value of ``strip``.""" if isinstance(tag_names, basestring): tag_names = [tag_names] if node: while tag_names: nodes = node.getElementsByTagName(tag_names.pop(0)) if nodes: child = nodes[0].firstChild return child and child.nodeValue.strip(strip) def parse_code(code): latitude = get_first_text(code, 'lat') or None longitude = get_first_text(code, 'lng') or None latitude = latitude and float(latitude) longitude = longitude and float(longitude) return latitude, longitude res = answer.read() if not isinstance(res, basestring): return False doc = xml.dom.minidom.parseString(res) codes = doc.getElementsByTagName('code') if len(codes) < 1: return False latitude, longitude = parse_code(codes[0]) return fields.GeoPoint.from_latlon(self.env.cr, latitude, longitude) @api.multi def geocode_from_geonames(self, srid='900913', strict=True, context=None): context = context or {} base_url = u'http://ws.geonames.org/postalCodeSearch?' config_parameter_obj = self.env['ir.config_parameter'] username = config_parameter_obj.get_param( 'geoengine_geonames_username') if not username: raise ValidationError( _('A username is required to access ' 'http://ws.geonames.org/ \n' 'Please provides a valid one by setting a ' 'value in System Paramter for the key ' '"geoengine_geonames_username"')) filters = {} for add in self: logger.info('geolocalize %s', add.name) if add.country_id.code and (add.city or add.zip): filters[u'country'] = add.country_id.code.encode('utf-8') filters[u'username'] = username.encode('utf-8') if add.city: filters[u'placename'] = add.city.encode('utf-8') if add.zip: filters[u'postalcode'] = add.zip.encode('utf-8') filters[u'maxRows'] = u'1' try: url = base_url + urlencode(filters) answer = urlopen(url) add.geo_point = self._get_point_from_reply(answer) except Exception, exc: logger.exception('error while updating geocodes') if strict: raise except_orm(_('Geoencoding fails'), str(exc))
class TmsPlace(geo_model.GeoModel): _name = 'tms.place' _description = 'Cities / Places' name = fields.Char('Place', size=64, required=True, index=True) complete_name = fields.Char(compute='_compute_complete_name') state_id = fields.Many2one('res.country.state', string='State Name') country_id = fields.Many2one('res.country', related='state_id.country_id', string='Country') latitude = fields.Float(required=False, digits=(20, 10), help='GPS Latitude') longitude = fields.Float(required=False, digits=(20, 10), help='GPS Longitude') point = geo_fields.GeoPoint( string='Coordinate', compute='_compute_point', inverse='_set_lat_long', ) @api.multi def get_coordinates(self): for rec in self: address = (rec.name + "," + rec.state_id.name + "," + rec.country_id.name) google_url = ('http://maps.googleapis.com/maps/api/geocode/json?' + 'address=' + address.encode('utf-8') + '&sensor=false') try: result = json.load(my_urllib.urlopen(google_url)) if result['status'] == 'OK': location = result['results'][0]['geometry']['location'] self.latitude = location['lat'] self.longitude = location['lng'] except: raise UserError(_("Google Maps is not available.")) @api.multi def open_in_google(self): for place in self: url = ("/tms/static/src/googlemaps/get_place_from_coords.html?" + str(place.latitude) + ',' + str(place.longitude)) return { 'type': 'ir.actions.act_url', 'url': url, 'nodestroy': True, 'target': 'new' } @api.depends('name', 'state_id') def _compute_complete_name(self): for rec in self: if rec.state_id: rec.complete_name = rec.name + ', ' + rec.state_id.name else: rec.complete_name = rec.name @api.depends('latitude', 'longitude') def _compute_point(self): for rec in self: rec.point = geo_fields.GeoPoint.from_latlon( self.env.cr, rec.latitude, rec.longitude).wkb_hex def set_lang_long(self): point_x, point_y = geojson.loads(self.point).coordinates inproj = Proj(init='epsg:3857') outproj = Proj(init='epsg:4326') longitude, latitude = transform(inproj, outproj, point_x, point_y) self.latitude = latitude self.longitude = longitude @api.onchange('point') def onchange_geo_point(self): if self.point: self.set_lang_long() @api.depends('point') def _set_lat_long(self): for rec in self: rec.set_lang_long()
class FSMLocation(geo_model.GeoModel): _name = 'fsm.location' _inherits = {'res.partner': 'partner_id'} _description = 'Field Service Location' @api.model def _tz_get(self): return [(tz, tz) for tz in sorted(pytz.all_timezones, key=lambda tz: tz if not tz.startswith('Etc/') else '_')] ref = fields.Char(string='Internal Reference') direction = fields.Char(string='Directions') partner_id = fields.Many2one('res.partner', string='Related Partner', required=True, ondelete='restrict', delegate=True, auto_join=True) owner_id = fields.Many2one('res.partner', string='Related Owner', required=True, ondelete='restrict', auto_join=True) customer_id = fields.Many2one('res.partner', string='Billed Customer', required=True, ondelete='restrict', auto_join=True) contact_id = fields.Many2one('res.partner', string='Primary Contact', domain="[('is_company', '=', False)," " ('fsm_location', '=', False)]", index=True) tag_ids = fields.Many2many('fsm.tag', string='Tags') description = fields.Char(string='Description') territory_id = fields.Many2one('fsm.territory', string='Territory') branch_id = fields.Many2one('fsm.branch', string='Branch') district_id = fields.Many2one('fsm.district', string='District') region_id = fields.Many2one('fsm.region', string='Region') territory_manager_id = fields.Many2one(string='Primary Assignment', related='territory_id.person_id') district_manager_id = fields.Many2one(string='District Manager', related='district_id.partner_id') region_manager_id = fields.Many2one(string='Region Manager', related='region_id.partner_id') branch_manager_id = fields.Many2one(string='Branch Manager', related='branch_id.partner_id') timezone = fields.Selection(_tz_get, string='Timezone') parent_id = fields.Many2one('fsm.location', string='Parent') notes = fields.Text(string="Notes") person_ids = fields.Many2many('fsm.person', 'partner_id', string='Preferred Workers') # Geometry Field shape = geo_fields.GeoPoint(string='Coordinate') _sql_constraints = [('fsm_location_ref_uniq', 'unique (ref)', 'This internal reference already exists!')] @api.model def create(self, vals): vals.update({'fsm_location': True}) return super(FSMLocation, self).create(vals) @api.onchange('territory_id') def _onchange_territory_id(self): if self.territory_id: # assign manager self.territory_manager_id = self.territory_id.person_id # get territory preffered person list if available self.person_ids = self.territory_id.person_ids if self.territory_id.branch_id: self.branch_id = self.territory_id.branch_id self.branch_manager_id = self.territory_id.branch_id.partner_id @api.onchange('branch_id') def _onchange_branch_id(self): if self.branch_id and self.branch_id.district_id: self.district_id = self.branch_id.district_id self.district_manager_id = self.branch_id.district_id.partner_id @api.onchange('district_id') def _onchange_district_id(self): if self.district_id and self.district_id.region_id: self.region_id = self.district_id.region_id self.region_manager_id = self.district_id.region_id.partner_id @api.multi def name_get(self): return [ (location.id, '%s%s' % (location.ref and '[%s] ' % location.ref or '', location.name)) for location in self ]
class FSMOrder(geo_model.GeoModel): _name = 'fsm.order' _description = 'Field Service Order' _inherit = ['mail.thread', 'mail.activity.mixin'] def _default_stage_id(self): return self.env.ref('fieldservice.fsm_stage_new') stage_id = fields.Many2one('fsm.stage', string='Stage', track_visibility='onchange', index=True, group_expand='_read_group_stage_ids', default=lambda self: self._default_stage_id()) priority = fields.Selection(fsm_stage.AVAILABLE_PRIORITIES, string='Priority', index=True, default=fsm_stage.AVAILABLE_PRIORITIES[0][0]) tag_ids = fields.Many2many('fsm.tag', 'fsm_order_tag_rel', 'fsm_order_id', 'tag_id', string='Tags', help="Classify and analyze your orders") color = fields.Integer('Color Index', default=0) # Request name = fields.Char(string='Name', required=True, default=lambda self: _('New')) customer_id = fields.Many2one('res.partner', string='Customer', domain=[('customer', '=', True)], change_default=True, index=True, track_visibility='always') location_id = fields.Many2one('fsm.location', string='Location', index=True) request_early = fields.Datetime(string='Earliest Request Date') request_late = fields.Datetime(string='Latest Request Date') description = fields.Text(string='Description') person_ids = fields.Many2many('fsm.person', string='Field Service Workers') # Planning person_id = fields.Many2one('fsm.person', string='Assigned To', index=True) route_id = fields.Many2one('fsm.route', string='Route', index=True) scheduled_date_start = fields.Datetime(string='Scheduled Start (ETA)') scheduled_duration = fields.Float(string='Duration in hours', help='Scheduled duration of the work in' ' hours') scheduled_date_end = fields.Datetime(string="Scheduled End") sequence = fields.Integer(string='Sequence', default=10) todo = fields.Text(string='Instructions') # Execution log = fields.Text(string='Log') date_start = fields.Datetime(string='Actual Start') date_end = fields.Datetime(string='Actual End') # Location branch_id = fields.Many2one('fsm.branch', string='Branch') district_id = fields.Many2one('fsm.district', string='District') region_id = fields.Many2one('fsm.region', string='Region') # Geometry Field shape = geo_fields.GeoPoint(string='Coordinate') # Fields for Geoengine Identify display_name = fields.Char(related="location_id.display_name", string="Location") street = fields.Char(related="location_id.street") street2 = fields.Char(related="location_id.street2") zip = fields.Char(related="location_id.zip") city = fields.Char(related="location_id.city") state_name = fields.Char(related="location_id.state_id.name", string='State', ondelete='restrict') country_name = fields.Char(related="location_id.country_id.name", string='Country', ondelete='restrict') phone = fields.Char(related="location_id.phone") mobile = fields.Char(related="location_id.mobile") stage_name = fields.Char(related="stage_id.name", string="Stage") # Template template_id = fields.Many2one('fsm.template', string="Template") category_ids = fields.Many2many('fsm.category', string="Categories") @api.model def _read_group_stage_ids(self, stages, domain, order): stage_ids = self.env['fsm.stage'].search([]) return stage_ids @api.model def create(self, vals): if vals.get('name', _('New')) == _('New'): vals['name'] = self.env['ir.sequence'].next_by_code('fsm.order') \ or _('New') return super(FSMOrder, self).create(vals) @api.multi def write(self, vals): if 'scheduled_date_end' in vals: date_to_with_delta = fields.Datetime.from_string( vals.get('scheduled_date_end')) -\ timedelta(hours=self.scheduled_duration) vals['scheduled_date_start'] = str(date_to_with_delta) if 'scheduled_duration' in vals: date_to_with_delta = fields.Datetime.from_string( vals.get('scheduled_date_start', self.scheduled_date_start)) +\ timedelta(hours=vals.get('scheduled_duration')) vals['scheduled_date_end'] = str(date_to_with_delta) if 'scheduled_date_end' not in vals and 'scheduled_date_start' in vals: date_to_with_delta = fields.Datetime.from_string( vals.get('scheduled_date_start')) +\ timedelta(hours=self.scheduled_duration) vals['scheduled_date_end'] = str(date_to_with_delta) return super(FSMOrder, self).write(vals) def action_confirm(self): return self.write( {'stage_id': self.env.ref('fieldservice.fsm_stage_confirmed').id}) def action_schedule(self): return self.write( {'stage_id': self.env.ref('fieldservice.fsm_stage_scheduled').id}) def action_assign(self): return self.write( {'stage_id': self.env.ref('fieldservice.fsm_stage_assigned').id}) def action_plan(self): return self.write( {'stage_id': self.env.ref('fieldservice.fsm_stage_planned').id}) def action_enroute(self): return self.write( {'stage_id': self.env.ref('fieldservice.fsm_stage_enroute').id}) def action_start(self): return self.write( {'stage_id': self.env.ref('fieldservice.fsm_stage_started').id}) def action_complete(self): return self.write( {'stage_id': self.env.ref('fieldservice.fsm_stage_completed').id}) def action_cancel(self): return self.write( {'stage_id': self.env.ref('fieldservice.fsm_stage_cancelled').id}) @api.onchange('scheduled_date_start') def onchange_scheduled_date_start(self): if self.person_id and self.scheduled_date_start: print(self.person_id) print(self.person_id) print("person") # self.stage_id = 'Planned' self.stage_id = 5 elif not self.person_id and self.scheduled_date_start: print("hello") # self.stage_id = 'Scheduled' self.stage_id = 3 @api.onchange('person_id') def onchange_person_id(self): if self.person_id and self.scheduled_date_start: print(self.scheduled_date_start) print("date") # self.stage_id = 'Planned' self.stage_id = 5 elif self.person_id and not self.scheduled_date_start: print("hello") # self.stage_id = 'Assigned' self.stage_id = 4 @api.onchange('scheduled_date_end') def onchange_scheduled_date_end(self): if self.scheduled_date_end: date_to_with_delta = fields.Datetime.from_string( self.scheduled_date_end) -\ timedelta(hours=self.scheduled_duration) self.date_start = str(date_to_with_delta) @api.onchange('scheduled_duration') def onchange_scheduled_duration(self): if self.scheduled_duration: date_to_with_delta = fields.Datetime.from_string( self.scheduled_date_start) +\ timedelta(hours=self.scheduled_duration) self.scheduled_date_end = str(date_to_with_delta) @api.onchange('location_id') def onchange_location_id(self): if self.location_id: self.branch_id = self.location_id.branch_id or False self.district_id = self.location_id.district_id or False self.region_id = self.location_id.region_id or False @api.onchange('template_id') def _onchange_template_id(self): if self.template_id: self.category_ids = self.template_id.category_ids def geo_localize(self): for order in self: if order.location_id.partner_id: order.location_id.partner_id.geo_localize() lat = order.location_id.partner_latitude lng = order.location_id.partner_longitude point = geo_fields.GeoPoint.from_latlon(cr=order.env.cr, latitude=lat, longitude=lng) order.shape = point
class FSMOrder(geo_model.GeoModel): _name = 'fsm.order' _description = 'Field Service Order' _inherit = ['mail.thread', 'mail.activity.mixin'] def _default_stage_id(self): return self.env.ref('fieldservice.fsm_stage_new') def _default_team_id(self): return self.env.ref('fieldservice.fsm_team_default') @api.depends('date_start', 'date_end') def _compute_duration(self): if self.date_start and self.date_end: start = fields.Datetime.from_string(self.date_start) end = fields.Datetime.from_string(self.date_end) delta = end - start self.duration = delta.total_seconds() / 3600 @api.depends('stage_id') def _get_stage_color(self): """ Get stage color""" self.custom_color = self.stage_id.custom_color or '#FFFFFF' stage_id = fields.Many2one('fsm.stage', string='Stage', track_visibility='onchange', index=True, copy=False, group_expand='_read_group_stage_ids', default=lambda self: self._default_stage_id()) priority = fields.Selection(fsm_stage.AVAILABLE_PRIORITIES, string='Priority', index=True, default=fsm_stage.AVAILABLE_PRIORITIES[0][0]) tag_ids = fields.Many2many('fsm.tag', 'fsm_order_tag_rel', 'fsm_order_id', 'tag_id', string='Tags', help="Classify and analyze your orders") color = fields.Integer('Color Index', default=0) team_id = fields.Many2one('fsm.team', string='Team', default=_default_team_id, index=True, required=True, track_visibility='onchange') # Request name = fields.Char(string='Name', required=True, index=True, copy=False, default=lambda self: _('New')) customer_id = fields.Many2one('res.partner', string='Contact', domain=[('customer', '=', True)], change_default=True, index=True, track_visibility='always') location_id = fields.Many2one('fsm.location', string='Location', index=True, required=True) request_early = fields.Datetime(string='Earliest Request Date', default=datetime.now()) request_late = fields.Datetime(string='Latest Request Date', compute='_compute_request_late') def _compute_request_late(self): if not self.request_late: if self.priority == '0': if self.request_early: self.request_late = fields.Datetime.from_string( self.request_early) + timedelta(days=3) else: self.request_late = datetime.now() + timedelta(days=3) elif self.priority == '1': self.request_late = fields.Datetime.from_string( self.request_early) + timedelta(days=2) elif self.priority == '2': self.request_late = fields.Datetime.from_string( self.request_early) + timedelta(days=1) elif self.priority == '3': self.request_late = fields.Datetime.from_string( self.request_early) + timedelta(hours=8) description = fields.Text(string='Description') person_ids = fields.Many2many('fsm.person', string='Field Service Workers') @api.onchange('location_id') def _onchange_location_id_customer(self): if self.location_id: return { 'domain': { 'customer_id': [('service_location_id', '=', self.location_id.name)] } } else: return {'domain': {'customer_id': [('id', '!=', None)]}} @api.onchange('customer_id') def _onchange_customer_id_location(self): if self.customer_id: self.location_id = self.customer_id.service_location_id # Planning person_id = fields.Many2one('fsm.person', string='Assigned To', index=True) route_id = fields.Many2one('fsm.route', string='Route', index=True) scheduled_date_start = fields.Datetime(string='Scheduled Start (ETA)') scheduled_duration = fields.Float(string='Scheduled duration', help='Scheduled duration of the work in' ' hours') scheduled_date_end = fields.Datetime(string="Scheduled End") sequence = fields.Integer(string='Sequence', default=10) todo = fields.Text(string='Instructions') # Execution resolution = fields.Text(string='Resolution', placeholder="Resolution of the order") date_start = fields.Datetime(string='Actual Start') date_end = fields.Datetime(string='Actual End') duration = fields.Float(string='Actual duration', compute=_compute_duration, help='Actual duration in hours') # Location branch_id = fields.Many2one('fsm.branch', string='Branch') district_id = fields.Many2one('fsm.district', string='District') region_id = fields.Many2one('fsm.region', string='Region') # Geometry Field shape = geo_fields.GeoPoint(string='Coordinate') # Fields for Geoengine Identify display_name = fields.Char(related="name", string="Order") street = fields.Char(related="location_id.street") street2 = fields.Char(related="location_id.street2") zip = fields.Char(related="location_id.zip") city = fields.Char(related="location_id.city") state_name = fields.Char(related="location_id.state_id.name", string='State', ondelete='restrict') country_name = fields.Char(related="location_id.country_id.name", string='Country', ondelete='restrict') phone = fields.Char(related="location_id.phone") mobile = fields.Char(related="location_id.mobile") stage_name = fields.Char(related="stage_id.name", string="Stage") # Field for Stage Color custom_color = fields.Char(related="stage_id.custom_color", string='Stage Color') # Template template_id = fields.Many2one('fsm.template', string="Template") category_ids = fields.Many2many('fsm.category', string="Categories") # Equipment equipment_id = fields.Many2one('fsm.equipment', string='Equipment') type = fields.Selection([], string='Type') @api.model def _read_group_stage_ids(self, stages, domain, order): stage_ids = self.env['fsm.stage'].search([('stage_type', '=', 'order') ]) return stage_ids @api.model def create(self, vals): if vals.get('name', _('New')) == _('New'): vals['name'] = self.env['ir.sequence'].next_by_code('fsm.order') \ or _('New') if vals['request_early'] is not False and\ vals['scheduled_date_start'] is False: req_date = fields.Datetime.from_string(vals['request_early']) # Round scheduled date start req_date = req_date.replace(minute=0, second=0) vals.update({ 'scheduled_date_start': str(req_date), 'request_early': str(req_date) }) res = super(FSMOrder, self).create(vals) res.create_geometry() return res @api.multi def write(self, vals): if 'scheduled_date_end' in vals: date_to_with_delta = fields.Datetime.from_string( vals.get('scheduled_date_end')) - \ timedelta(hours=self.scheduled_duration) vals['scheduled_date_start'] = str(date_to_with_delta) if 'scheduled_duration' in vals: date_to_with_delta = fields.Datetime.from_string( vals.get('scheduled_date_start', self.scheduled_date_start))\ + timedelta(hours=vals.get('scheduled_duration')) vals['scheduled_date_end'] = str(date_to_with_delta) if 'scheduled_date_end' not in vals and 'scheduled_date_start' in vals: date_to_with_delta = fields.Datetime.from_string( vals.get('scheduled_date_start')) + \ timedelta(hours=self.scheduled_duration) vals['scheduled_date_end'] = str(date_to_with_delta) if 'customer_id' not in vals: vals['customer_id'] = self.location_id.customer_id.id return super(FSMOrder, self).write(vals) def action_confirm(self): return self.write( {'stage_id': self.env.ref('fieldservice.fsm_stage_confirmed').id}) def action_request(self): return self.write( {'stage_id': self.env.ref('fieldservice.fsm_stage_requested').id}) def action_assign(self): if self.person_id: return self.write({ 'stage_id': self.env.ref('fieldservice.fsm_stage_assigned').id }) else: raise ValidationError( _("Cannot move to Assigned " + "until 'Assigned To' is filled in")) def action_schedule(self): return self.write( {'stage_id': self.env.ref('fieldservice.fsm_stage_scheduled').id}) def action_enroute(self): return self.write( {'stage_id': self.env.ref('fieldservice.fsm_stage_enroute').id}) def action_start(self): return self.write( {'stage_id': self.env.ref('fieldservice.fsm_stage_started').id}) def action_complete(self): return self.write( {'stage_id': self.env.ref('fieldservice.fsm_stage_completed').id}) def action_cancel(self): return self.write( {'stage_id': self.env.ref('fieldservice.fsm_stage_cancelled').id}) @api.onchange('scheduled_date_end') def onchange_scheduled_date_end(self): if self.scheduled_date_end: date_to_with_delta = fields.Datetime.from_string( self.scheduled_date_end) - \ timedelta(hours=self.scheduled_duration) self.date_start = str(date_to_with_delta) @api.onchange('scheduled_duration') def onchange_scheduled_duration(self): if (self.scheduled_duration and self.scheduled_date_start): date_to_with_delta = fields.Datetime.from_string( self.scheduled_date_start) + \ timedelta(hours=self.scheduled_duration) self.scheduled_date_end = str(date_to_with_delta) def copy_notes(self): self.description = "" if self.equipment_id: if self.equipment_id.notes is not False: if self.description is not False: self.description = (self.description + self.equipment_id.notes + '\n ') else: self.description = (self.equipment_id.notes + '\n ') if self.location_id: if self.location_id.direction is not False: if self.description is not False: self.description = (self.description + self.location_id.direction + '\n ') else: self.description = (self.location_id.direction + '\n ') @api.onchange('location_id') def onchange_location_id(self): if self.location_id: self.branch_id = self.location_id.branch_id or False self.district_id = self.location_id.district_id or False self.region_id = self.location_id.region_id or False self.create_geometry() self.copy_notes() @api.onchange('equipment_id') def onchange_equipment_id(self): self.copy_notes() @api.onchange('template_id') def _onchange_template_id(self): if self.template_id: self.category_ids = self.template_id.category_ids self.scheduled_duration = self.template_id.hours self.todo = (self.todo or '') + \ ('<p>' + self.template_id.instructions or '' + '</p>') def create_geometry(self): for order in self: lat = order.location_id.partner_latitude lng = order.location_id.partner_longitude point = geo_fields.GeoPoint.from_latlon(cr=order.env.cr, latitude=lat, longitude=lng) order.shape = point
class SaleOrder(geo_model.GeoModel): """Add geo_point to sale.order""" _inherit = "sale.order" geo_point = fields.GeoPoint('Addresses coordinate', related='partner_invoice_id.geo_point')
class AdvocateDetails(models.Model, geo_model.GeoModel): _name = "advocate.details" _description = "Advocate Details" _rec_name = "partner_id" _inherit = "mail.thread" partner_id = fields.Many2one( 'res.partner', 'Partner', required=True, ondelete='cascade') description = fields.Text(translate=False) quote = fields.Text(translate=False) picture_large = fields.Binary( string='Large picture', attachment=True, help='Optional large picture for your profile page' ) picture_filename = fields.Char(compute='_compute_filename') thank_you_quote = fields.Html( compute='_compute_thank_you_quote', help='Used in thank you letters for donations linked to an event ' 'and to this partner.', ) mail_copy_when_donation = fields.Boolean() number_surveys = fields.Integer(related='partner_id.survey_input_count') # Advocacy fields ################# active_since = fields.Date() end_date = fields.Date() last_event = fields.Date(compute='_compute_events') state = fields.Selection([ ('new', 'New advocate'), ('active', 'Active'), ('on_break', 'On break'), ('inactive', 'Inactive'), ], default='new', required=True, track_visibility='onchange') break_end = fields.Date() advocacy_source = fields.Text( help='Describe how this advocate has partnered with us.' ) has_car = fields.Selection('_yes_no_selection', 'Has a car') formation_ids = fields.Many2many( 'calendar.event', string='Formation taken', compute='_compute_formation', inverse='_inverse_formation', groups="base.group_user" ) engagement_ids = fields.Many2many( 'advocate.engagement', 'advocate_engagement_rel', 'advocate_details_id', 'engagement_id', 'Engagement type' ) t_shirt_size = fields.Selection([ ('S', 'S'), ('M', 'M'), ('L', 'L'), ('XL', 'XL'), ('XXL', 'XXL') ]) t_shirt_type = fields.Selection([ ('shirt', 'Shirt'), ('bikeshirt', 'Bikeshirt'), ]) event_ids = fields.Many2many( 'crm.event.compassion', string='Events', compute='_compute_events' ) event_type_formation = fields.Integer(compute='_compute_formation') number_events = fields.Integer(compute='_compute_events') # Partner related fields ######################## birthdate = fields.Date( related='partner_id.birthdate_date', store=True, readonly=True) lang = fields.Selection( related='partner_id.lang', store=True, readonly=True) zip = fields.Char( related='partner_id.zip', store=True, readonly=True) city = fields.Char( related='partner_id.city', store=True, readonly=True) email = fields.Char( related='partner_id.email', store=True, readonly=True) geo_point = geo_fields.GeoPoint(readonly=True) _sql_constraints = [ ('details_unique', 'unique(partner_id)', 'Only one details per ambassador is allowed!') ] @api.model def _yes_no_selection(self): return [ ('yes', 'Yes'), ('no', 'No') ] @api.multi def _compute_filename(self): for details in self: partner_name = details.display_name details.picture_filename = partner_name + '-large.jpg' @api.multi def _compute_thank_you_quote(self): html_file = file_open( 'partner_compassion/static/src/html/thank_you_quote_template.html') template_html = unicode(html_file.read()) for details in self: html_vals = { u'img_alt': details.display_name, u'image_data': details.partner_id.with_context( bin_size=False).image, u'text': details.quote or '', u'attribution': _('Quote from {firstname} {lastname}').format( firstname=details.partner_id.firstname, lastname=details.partner_id.lastname) if details.quote else '', } details.thank_you_quote = template_html.format(**html_vals) @api.multi def _compute_events(self): for details in self: details.event_ids = self.env['crm.event.compassion'].search([ ('staff_ids', '=', details.partner_id.id), ('end_date', '<', fields.Datetime.now()) ]) details.number_events = len(details.event_ids) details.last_event = details.event_ids[:1].end_date @api.multi def _compute_formation(self): formation_cated_id = self.env.ref( 'partner_compassion.event_type_formation').id for details in self: details.formation_ids = self.env['calendar.event'].search([ ('partner_ids', '=', details.partner_id.id), ('categ_ids', '=', formation_cated_id) ]) details.event_type_formation = formation_cated_id @api.multi def _inverse_formation(self): # Allows to create formation event from ambassador details return True @api.multi def set_geo_point(self): for advocate in self: advocate.geo_point = advocate.partner_id.geo_point @api.model def create(self, vals): # Link partner to the advocate details advocate = super(AdvocateDetails, self).create(vals) advocate.partner_id.advocate_details_id = advocate advocate.set_geo_point() return advocate @api.multi def open_events(self): return { 'name': _('Events'), 'type': 'ir.actions.act_window', 'view_type': 'form', 'view_mode': 'tree,form', 'res_model': 'crm.event.compassion', 'target': 'current', 'domain': [('id', 'in', self.event_ids.ids)], } @api.multi def open_surveys(self): return { 'name': _('Surveys'), 'type': 'ir.actions.act_window', 'view_type': 'form', 'view_mode': 'tree,form', 'res_model': 'survey.user_input', 'target': 'current', 'domain': [('partner_id', '=', self.partner_id.id)], } def set_on_break(self): self.env.user.notify_info( _("Please don't forget to put a break end date"), sticky=True) return self.write({'state': 'on_break'}) def set_inactive(self): return self.write({ 'state': 'inactive', 'end_date': fields.Date.today() }) def set_active(self): return self.write({ 'state': 'active', 'end_date': False, 'break_end': False }) @api.model def advocate_cron(self): three_open_days = datetime.today() + BDay(3) birthday_advocates = self.search([ ('state', 'in', ['active', 'on_break']), ('birthdate', 'like', three_open_days.strftime('%m-%d')) ]) birthday_advocates = birthday_advocates.filtered( lambda a: a.engagement_ids != self.env.ref( "partner_compassion.engagement_sport")) for advocate in birthday_advocates: notify_partner_id = self.env['staff.notification.settings'].\ get_param( 'advocate_birthday_{}_id'.format(advocate.partner_id.lang[:2]) ) advocate.message_post( body=_(u"This is a reminder that {} will have birthday on {}.") .format(advocate.partner_id.preferred_name, advocate.partner_id.get_date( 'birthdate_date', '%d %B')), subject=_(u"[{}] Advocate birthday reminder").format( advocate.display_name), partner_ids=[notify_partner_id], type='comment', subtype='mail.mt_comment', content_subtype='html' ) break_advocates = self.search([ ('state', '=', 'on_break'), ('break_end', '<', fields.Date.today()), ('break_end', '!=', False) ]) break_advocates.set_active()
class FSMLocation(geo_model.GeoModel): _name = 'fsm.location' _inherits = {'res.partner': 'partner_id'} _description = 'Field Service Location' @api.model def _tz_get(self): return [(tz, tz) for tz in sorted(pytz.all_timezones, key=lambda tz: tz if not tz.startswith('Etc/') else '_')] ref = fields.Char(string='Internal Reference') direction = fields.Char(string='Directions') partner_id = fields.Many2one('res.partner', string='Related Partner', required=True, ondelete='restrict', delegate=True, auto_join=True) owner_id = fields.Many2one('res.partner', string='Related Owner', required=True, ondelete='restrict', auto_join=True) customer_id = fields.Many2one('res.partner', string='Billed Customer', required=True, ondelete='restrict', auto_join=True) contact_id = fields.Many2one('res.partner', string='Primary Contact', domain="[('is_company', '=', False)," " ('fsm_location', '=', False)]", index=True) tag_ids = fields.Many2many('fsm.tag', string='Tags') description = fields.Char(string='Description') territory_id = fields.Many2one('fsm.territory', string='Territory') branch_id = fields.Many2one('fsm.branch', string='Branch') district_id = fields.Many2one('fsm.district', string='District') region_id = fields.Many2one('fsm.region', string='Region') territory_manager_id = fields.Many2one(string='Primary Assignment', related='territory_id.person_id') district_manager_id = fields.Many2one(string='District Manager', related='district_id.partner_id') region_manager_id = fields.Many2one(string='Region Manager', related='region_id.partner_id') branch_manager_id = fields.Many2one(string='Branch Manager', related='branch_id.partner_id') timezone = fields.Selection(_tz_get, string='Timezone') parent_id = fields.Many2one('fsm.location', string='Parent') notes = fields.Text(string="Notes") person_ids = fields.Many2many('fsm.person', 'partner_id', string='Preferred Workers') contact_count = fields.Integer(string='Contacts', compute='_compute_contact_ids') equipment_count = fields.Integer(string='Equipment', compute='_compute_equipment_ids') # Geometry Field shape = geo_fields.GeoPoint(string='Coordinate') _sql_constraints = [('fsm_location_ref_uniq', 'unique (ref)', 'This internal reference already exists!')] @api.model def create(self, vals): vals.update({'fsm_location': True}) res = super(FSMLocation, self).create(vals) lat = self.partner_id.partner_latitude lng = self.partner_id.partner_longitude if lat == 0.0 and lng == 0.0: res.geo_localize() else: point = geo_fields.GeoPoint.from_latlon(cr=self.env.cr, latitude=lat, longitude=lng) self.shape = point return res @api.onchange('territory_id') def _onchange_territory_id(self): if self.territory_id: # assign manager self.territory_manager_id = self.territory_id.person_id # get territory preffered person list if available self.person_ids = self.territory_id.person_ids if self.territory_id.branch_id: self.branch_id = self.territory_id.branch_id self.branch_manager_id = self.territory_id.branch_id.partner_id @api.onchange('branch_id') def _onchange_branch_id(self): if self.branch_id and self.branch_id.district_id: self.district_id = self.branch_id.district_id self.district_manager_id = self.branch_id.district_id.partner_id @api.onchange('district_id') def _onchange_district_id(self): self.region_id = self.district_id.region_id def comp_count(self, contact, equipment, loc): if equipment: for child in loc: child_locs = self.env['fsm.location'].\ search([('parent_id', '=', child.id)]) equip = self.env['fsm.equipment'].\ search_count([('location_id', '=', child.id)]) if child_locs: for loc in child_locs: equip += loc.comp_count(0, 1, loc) return equip elif contact: for child in loc: child_locs = self.env['fsm.location'].\ search([('parent_id', '=', child.id)]) con = self.env['res.partner'].\ search_count([('service_location_id', '=', child.id)]) if child_locs: for loc in child_locs: con += loc.comp_count(1, 0, loc) return con def get_action_views(self, contact, equipment, loc): if equipment: for child in loc: child_locs = self.env['fsm.location'].\ search([('parent_id', '=', child.id)]) equip = self.env['fsm.equipment'].\ search([('location_id', '=', child.id)]) if child_locs: for loc in child_locs: equip += loc.get_action_views(0, 1, loc) return equip elif contact: for child in loc: child_locs = self.env['fsm.location'].\ search([('parent_id', '=', child.id)]) con = self.env['res.partner'].\ search([('service_location_id', '=', child.id)]) if child_locs: for loc in child_locs: con += loc.get_action_views(1, 0, loc) return con @api.multi def action_view_contacts(self): ''' This function returns an action that display existing contacts of given fsm location id and its child locations. It can either be a in a list or in a form view, if there is only one contact to show. ''' for location in self: action = self.env.ref('contacts.action_contacts').\ read()[0] contacts = self.get_action_views(1, 0, location) if len(contacts) > 1: action['domain'] = [('id', 'in', contacts.ids)] elif contacts: action['views'] = [(self.env.ref('base.view_partner_form').id, 'form')] action['res_id'] = contacts.id return action @api.multi def _compute_contact_ids(self): for loc in self: contacts = self.comp_count(1, 0, loc) loc.contact_count = contacts @api.multi def action_view_equipment(self): ''' This function returns an action that display existing equipment of given fsm location id. It can either be a in a list or in a form view, if there is only one equipment to show. ''' for location in self: action = self.env.ref('fieldservice.action_fsm_equipment').\ read()[0] equipment = self.get_action_views(0, 1, location) if len(equipment) > 1: action['domain'] = [('id', 'in', equipment.ids)] elif equipment: action['views'] = [(self.env.ref('fieldservice.' + 'fsm_equipment_form_view').id, 'form')] action['res_id'] = equipment.id return action @api.multi def _compute_equipment_ids(self): for loc in self: equipment = self.comp_count(0, 1, loc) loc.equipment_count = equipment for location in self: child_locs = self.env['fsm.location'].\ search([('parent_id', '=', location.id)]) equipment = (self.env['fsm.equipment'].search_count([ ('location_id', 'in', child_locs.ids) ]) + self.env['fsm.equipment'].search_count( [('location_id', '=', location.id)])) location.equipment_count = equipment or 0 def geo_localize(self): for loc in self: if loc.partner_id: loc.partner_id.geo_localize() lat = loc.partner_latitude lng = loc.partner_longitude point = geo_fields.GeoPoint.from_latlon(cr=loc.env.cr, latitude=lat, longitude=lng) loc.shape = point def _update_order_geometries(self): for loc in self: orders = loc.env['fsm.order'].search([('location_id', '=', loc.id) ]) for order in orders: order.create_geometry() @api.multi def write(self, vals): res = super(FSMLocation, self).write(vals) if ('partner_latitude' in vals) and ('partner_longitude' in vals): self.shape = geo_fields.GeoPoint.from_latlon( cr=self.env.cr, latitude=vals['partner_latitude'], longitude=vals['partner_longitude']) self._update_order_geometries() return res
class FSMLocation(geo_model.GeoModel): _name = 'fsm.location' _inherits = {'res.partner': 'partner_id'} _description = 'Field Service Location' @api.model def _tz_get(self): return [(tz, tz) for tz in sorted(pytz.all_timezones, key=lambda tz: tz if not tz.startswith('Etc/') else '_')] ref = fields.Char(string='Internal Reference', copy=False) direction = fields.Char(string='Directions') partner_id = fields.Many2one('res.partner', string='Related Partner', required=True, ondelete='restrict', delegate=True, auto_join=True) owner_id = fields.Many2one('res.partner', string='Related Owner', required=True, ondelete='restrict', auto_join=True) customer_id = fields.Many2one('res.partner', string='Billed Customer', required=True, ondelete='restrict', auto_join=True) contact_id = fields.Many2one('res.partner', string='Primary Contact', domain="[('is_company', '=', False)," " ('fsm_location', '=', False)]", index=True) tag_ids = fields.Many2many('fsm.tag', string='Tags') description = fields.Char(string='Description') territory_id = fields.Many2one('fsm.territory', string='Territory') branch_id = fields.Many2one('fsm.branch', string='Branch') district_id = fields.Many2one('fsm.district', string='District') region_id = fields.Many2one('fsm.region', string='Region') territory_manager_id = fields.Many2one(string='Primary Assignment', related='territory_id.person_id') district_manager_id = fields.Many2one(string='District Manager', related='district_id.partner_id') region_manager_id = fields.Many2one(string='Region Manager', related='region_id.partner_id') branch_manager_id = fields.Many2one(string='Branch Manager', related='branch_id.partner_id') timezone = fields.Selection(_tz_get, string='Timezone') fsm_parent_id = fields.Many2one('fsm.location', string='Parent') notes = fields.Text(string="Notes") person_ids = fields.Many2many('fsm.person', 'fsm_person_location_rel', 'fsm_location_id', 'fsm_person_id', string='Preferred Workers') contact_count = fields.Integer(string='Contacts', compute='_compute_contact_ids') equipment_count = fields.Integer(string='Equipment', compute='_compute_equipment_ids') sublocation_count = fields.Integer(string='Sub Locations', compute='_compute_sublocation_ids') complete_name = fields.Char(string='Complete Name', compute='_compute_complete_name', stored='_compute_complete_name') stage_id = fields.Many2one('fsm.stage', string='Stage', track_visibility='onchange', index=True, copy=False, group_expand='_read_group_stage_ids', default=lambda self: self._default_stage_id()) hide = fields.Boolean(default=False) company_id = fields.Many2one('res.company', 'Company', default=lambda self: self.env.user.company_id) @api.depends('name', 'fsm_parent_id.complete_name') def _compute_complete_name(self): for loc in self: if loc.fsm_parent_id: loc.complete_name = '%s / %s' % ( loc.fsm_parent_id.complete_name, loc.name) else: loc.complete_name = loc.name @api.multi def name_get(self): results = [] for rec in self: results.append((rec.id, rec.complete_name)) return results # Geometry Field shape = geo_fields.GeoPoint(string='Coordinate') _sql_constraints = [('fsm_location_ref_uniq', 'unique (ref)', 'This internal reference already exists!')] @api.model def _read_group_stage_ids(self, stages, domain, order): stage_ids = self.env['fsm.stage'].search([('stage_type', '=', 'location')]) return stage_ids def _default_stage_id(self): return self.env['fsm.stage'].search([('stage_type', '=', 'location'), ('sequence', '=', '1')]) def advance_stage(self): seq = self.stage_id.sequence next_stage = self.env['fsm.stage'].search([('stage_type', '=', 'location'), ('sequence', '=', seq + 1)]) self.stage_id = next_stage self._onchange_stage_id() @api.onchange('stage_id') def _onchange_stage_id(self): # get last stage heighest_stage = self.env['fsm.stage'].search( [('stage_type', '=', 'location')], order='sequence desc', limit=1) if self.stage_id.name == heighest_stage.name: self.hide = True else: self.hide = False @api.model def create(self, vals): vals.update({'fsm_location': True}) res = super(FSMLocation, self).create(vals) lat = self.partner_id.partner_latitude lng = self.partner_id.partner_longitude if lat == 0.0 and lng == 0.0: res.geo_localize() else: point = geo_fields.GeoPoint.from_latlon(cr=self.env.cr, latitude=lat, longitude=lng) self.shape = point return res @api.onchange('fsm_parent_id') def _onchange_fsm_parent_id(self): self.owner_id = self.fsm_parent_id.owner_id or False self.customer_id = self.fsm_parent_id.customer_id or False self.contact_id = self.fsm_parent_id.contact_id or False self.direction = self.fsm_parent_id.direction or False self.street = self.fsm_parent_id.street or False self.street2 = self.fsm_parent_id.street2 or False self.city = self.fsm_parent_id.city or False self.zip = self.fsm_parent_id.zip or False self.state_id = self.fsm_parent_id.state_id or False self.country_id = self.fsm_parent_id.country_id or False self.timezone = self.fsm_parent_id.timezone or False self.territory_id = self.fsm_parent_id.territory_id or False @api.onchange('territory_id') def _onchange_territory_id(self): self.territory_manager_id = self.territory_id.person_id or False self.person_ids = self.territory_id.person_ids or False self.branch_id = self.territory_id.branch_id or False @api.onchange('branch_id') def _onchange_branch_id(self): self.branch_manager_id = \ self.territory_id.branch_id.partner_id or False self.district_id = self.branch_id.district_id or False @api.onchange('district_id') def _onchange_district_id(self): self.district_manager_id = \ self.branch_id.district_id.partner_id or False self.region_id = self.district_id.region_id or False @api.onchange('region_id') def _onchange_region_id(self): self.region_manager_id = self.region_id.partner_id or False def comp_count(self, contact, equipment, loc): if equipment: for child in loc: child_locs = self.env['fsm.location'].\ search([('fsm_parent_id', '=', child.id)]) equip = self.env['fsm.equipment'].\ search_count([('location_id', '=', child.id)]) if child_locs: for loc in child_locs: equip += loc.comp_count(0, 1, loc) return equip elif contact: for child in loc: child_locs = self.env['fsm.location'].\ search([('fsm_parent_id', '=', child.id)]) con = self.env['res.partner'].\ search_count([('service_location_id', '=', child.id)]) if child_locs: for loc in child_locs: con += loc.comp_count(1, 0, loc) return con else: for child in loc: child_locs = self.env['fsm.location'].\ search([('fsm_parent_id', '=', child.id)]) subloc = self.env['fsm.location'].\ search_count([('fsm_parent_id', '=', child.id)]) if child_locs: for loc in child_locs: subloc += loc.comp_count(0, 0, loc) return subloc def get_action_views(self, contact, equipment, loc): if equipment: for child in loc: child_locs = self.env['fsm.location'].\ search([('fsm_parent_id', '=', child.id)]) equip = self.env['fsm.equipment'].\ search([('location_id', '=', child.id)]) if child_locs: for loc in child_locs: equip += loc.get_action_views(0, 1, loc) return equip elif contact: for child in loc: child_locs = self.env['fsm.location'].\ search([('fsm_parent_id', '=', child.id)]) con = self.env['res.partner'].\ search([('service_location_id', '=', child.id)]) if child_locs: for loc in child_locs: con += loc.get_action_views(1, 0, loc) return con else: for child in loc: child_locs = self.env['fsm.location'].\ search([('fsm_parent_id', '=', child.id)]) subloc = child_locs if child_locs: for loc in child_locs: subloc += loc.get_action_views(0, 0, loc) return subloc @api.multi def action_view_contacts(self): ''' This function returns an action that display existing contacts of given fsm location id and its child locations. It can either be a in a list or in a form view, if there is only one contact to show. ''' for location in self: action = self.env.ref('contacts.action_contacts').\ read()[0] contacts = self.get_action_views(1, 0, location) if len(contacts) > 1: action['domain'] = [('id', 'in', contacts.ids)] elif contacts: action['views'] = [(self.env.ref('base.view_partner_form').id, 'form')] action['res_id'] = contacts.id return action @api.multi def _compute_contact_ids(self): for loc in self: contacts = self.comp_count(1, 0, loc) loc.contact_count = contacts @api.multi def action_view_equipment(self): ''' This function returns an action that display existing equipment of given fsm location id. It can either be a in a list or in a form view, if there is only one equipment to show. ''' for location in self: action = self.env.ref('fieldservice.action_fsm_equipment').\ read()[0] equipment = self.get_action_views(0, 1, location) if len(equipment) == 0 or len(equipment) > 1: action['domain'] = [('id', 'in', equipment.ids)] elif equipment: action['views'] = [(self.env.ref('fieldservice.' + 'fsm_equipment_form_view').id, 'form')] action['res_id'] = equipment.id return action @api.multi def _compute_sublocation_ids(self): for loc in self: sublocation = self.comp_count(0, 0, loc) loc.sublocation_count = sublocation @api.multi def action_view_sublocation(self): ''' This function returns an action that display existing sub-locations of a given fsm location id. It can either be a in a list or in a form view, if there is only one sub-location to show. ''' for location in self: action = self.env.ref('fieldservice.action_fsm_location').read()[0] sublocation = self.get_action_views(0, 0, location) if len(sublocation) > 1 or len(sublocation) == 0: action['domain'] = [('id', 'in', sublocation.ids)] elif sublocation: action['views'] = [(self.env.ref('fieldservice.' + 'fsm_location_form_view').id, 'form')] action['res_id'] = sublocation.id return action @api.multi def _compute_equipment_ids(self): for loc in self: equipment = self.comp_count(0, 1, loc) loc.equipment_count = equipment for location in self: child_locs = self.env['fsm.location']. \ search([('fsm_parent_id', '=', location.id)]) equipment = (self.env['fsm.equipment'].search_count([ ('location_id', 'in', child_locs.ids) ]) + self.env['fsm.equipment'].search_count( [('location_id', '=', location.id)])) location.equipment_count = equipment or 0 def geo_localize(self): for loc in self: if loc.partner_id: loc.partner_id.geo_localize() lat = loc.partner_latitude lng = loc.partner_longitude point = geo_fields.GeoPoint.from_latlon(cr=loc.env.cr, latitude=lat, longitude=lng) loc.shape = point def _update_order_geometries(self): for loc in self: orders = loc.env['fsm.order'].search([('location_id', '=', loc.id) ]) for order in orders: order.create_geometry() @api.multi def write(self, vals): res = super(FSMLocation, self).write(vals) if ('partner_latitude' in vals) and ('partner_longitude' in vals): self.shape = geo_fields.GeoPoint.from_latlon( cr=self.env.cr, latitude=vals['partner_latitude'], longitude=vals['partner_longitude']) self._update_order_geometries() return res
class ResPartner(models.Model): """ This class upgrade the partners to match Compassion needs. It also synchronize all changes with the MySQL server of GP. """ _inherit = 'res.partner' def _get_receipt_types(self): """ Display values for the receipt selection fields. """ return [ ('no', _('No receipt')), ('default', _('Default')), ('only_email', _('Only email')), ('paper', _('On paper'))] ########################################################################## # NEW PARTNER FIELDS # ########################################################################## lang = fields.Selection(default=False) total_invoiced = fields.Monetary(groups=False) street3 = fields.Char("Street3", size=128) invalid_mail = fields.Char("Invalid mail") church_unlinked = fields.Char( "Church (N/A)", help="Use this field if the church of the partner" " can not correctly be determined and linked.") deathdate = fields.Date('Death date', track_visibility='onchange') nbmag = fields.Integer('Number of Magazines', size=2, required=True, default=1) tax_certificate = fields.Selection( _get_receipt_types, required=True, default='default') thankyou_letter = fields.Selection( _get_receipt_types, 'Thank you letter', required=True, default='default') calendar = fields.Boolean( help="Indicates if the partner wants to receive the Compassion " "calendar.", default=True) christmas_card = fields.Boolean( help="Indicates if the partner wants to receive the " "christmas card.", default=True) birthday_reminder = fields.Boolean( help="Indicates if the partner wants to receive a birthday " "reminder of his child.", default=True) photo_delivery_preference = fields.Selection( selection='_get_delivery_preference', default='both', required=True, help='Delivery preference for Child photo') partner_duplicate_ids = fields.Many2many( 'res.partner', 'res_partner_duplicates', 'partner_id', 'duplicate_id', readonly=True) advocate_details_id = fields.Many2one( 'advocate.details', 'Advocate details', copy=False) engagement_ids = fields.Many2many( 'advocate.engagement', related='advocate_details_id.engagement_ids' ) other_contact_ids = fields.One2many(string='Linked Partners', domain=['|', ('active', '=', False), ('active', '=', True)]) state = fields.Selection([ ('pending', 'Waiting for validation'), ('active', 'Active') ], default='active', track_visibility='onchange') email_copy = fields.Boolean(string='CC e-mails sent to main partner') type = fields.Selection(selection_add=[ ('email_alias', 'Email alias') ]) uuid = fields.Char(default=lambda self: self._get_uuid(), copy=False, index=True) has_agreed_child_protection_charter = fields.Boolean( help="Indicates if the partner has agreed to the child protection" "charter.", default=False) date_agreed_child_protection_charter = fields.Datetime( help="The date and time when the partner has agreed to the child" "protection charter." ) geo_point = geo_fields.GeoPoint(copy=False) # add track on fields from module base email = fields.Char(track_visibility='onchange') title = fields.Many2one(track_visibility='onchange') lang = fields.Selection(track_visibility='onchange') # module from partner_firstname firstname = fields.Char(track_visibility='onchange') lastname = fields.Char(track_visibility='onchange') # module mail opt_out = fields.Boolean(track_visibility='onchange') ########################################################################## # FIELDS METHODS # ########################################################################## def _get_uuid(self): return str(uuid.uuid4()) @api.multi def agree_to_child_protection_charter(self): return self.write({ 'has_agreed_child_protection_charter': True, 'date_agreed_child_protection_charter': fields.Datetime.now() }) @api.multi def validate_partner(self): return self.write({ 'state': 'active' }) @api.multi def get_unreconciled_amount(self): """Returns the amount of unreconciled credits in Account 1050""" self.ensure_one() mv_line_obj = self.env['account.move.line'] move_line_ids = mv_line_obj.search([ ('partner_id', '=', self.id), ('account_id.code', '=', '1050'), ('credit', '>', '0'), ('full_reconcile_id', '=', False)]) res = 0 for move_line in move_line_ids: res += move_line.credit return res @api.multi def update_number_sponsorships(self): """ Update the sponsorship number for the related church as well. """ return super( ResPartner, self + self.mapped('church_id')).update_number_sponsorships() ########################################################################## # ORM METHODS # ########################################################################## @api.model def create(self, vals): """ Lookup for duplicate partners and notify. """ email = vals.get('email') if email: vals['email'] = email.strip() duplicate = self.search( ['|', '&', ('email', '=', vals.get('email')), ('email', '!=', False), '&', '&', ('firstname', 'ilike', vals.get('firstname')), ('lastname', 'ilike', vals.get('lastname')), ('zip', '=', vals.get('zip')) ]) duplicate_ids = [(4, itm.id) for itm in duplicate] vals.update({'partner_duplicate_ids': duplicate_ids}) vals['ref'] = self.env['ir.sequence'].get('partner.ref') # Never subscribe someone to res.partner record partner = super(ResPartner, self.with_context( mail_create_nosubscribe=True)).create(vals) partner.compute_geopoint() if partner.contact_type == 'attached' and not vals.get('active'): partner.active = False return partner @api.multi def write(self, vals): email = vals.get('email') if email: vals['email'] = email.strip() res = super(ResPartner, self).write(vals) if set(('country_id', 'city', 'zip')).intersection(vals): self.geo_localize() self.compute_geopoint() return res @api.model def name_search(self, name, args=None, operator='ilike', limit=80): """Extends to use trigram search.""" if args is None: args = [] if name: # First find by reference res = self.search([('ref', 'like', name)], limit=limit) if not res: res = self.search( ['|', ('name', '%', name), ('name', 'ilike', name)], order=u"similarity(res_partner.name, '%s') DESC" % name, limit=limit) # Search by e-mail if not res: res = self.search([('email', 'ilike', name)], limit=limit) else: res = self.search(args, limit=limit) return res.name_get() @api.model def search(self, args, offset=0, limit=None, order=None, count=False): """ Order search results based on similarity if name search is used.""" fuzzy_search = False for arg in args: if arg[0] == 'name' and arg[1] == '%': fuzzy_search = arg[2] break if fuzzy_search: order = self.env.cr.mogrify( u"similarity(res_partner.name, %s) DESC", [fuzzy_search]) return super(ResPartner, self).search( args, offset, limit, order, count) ########################################################################## # ONCHANGE METHODS # ########################################################################## @api.onchange('lastname', 'firstname', 'zip', 'email') def _onchange_partner(self): if ((self.lastname and self.firstname and self.zip) or self.email)\ and self.contact_type != 'attached': partner_duplicates = self.search([ ('id', '!=', self._origin.id), '|', '&', ('email', '=', self.email), ('email', '!=', False), '&', '&', ('firstname', 'ilike', self.firstname), ('lastname', 'ilike', self.lastname), ('zip', '=', self.zip) ]) if partner_duplicates: self.partner_duplicate_ids = partner_duplicates # Commit the found duplicates with api.Environment.manage(): with registry(self.env.cr.dbname).cursor() as new_cr: new_env = api.Environment(new_cr, self.env.uid, {}) self._origin.with_env(new_env).write({ 'partner_duplicate_ids': [(6, 0, partner_duplicates.ids)] }) return { 'warning': { 'title': _("Possible existing partners found"), 'message': _('The partner you want to add may ' 'already exist. Please use the "' 'Check duplicates" button to review it.') }, } ########################################################################## # PUBLIC METHODS # ########################################################################## @api.multi def compute_geopoint(self): """ Compute geopoints. """ self.filtered(lambda p: not p.partner_latitude or not p.partner_longitude).geo_localize() for partner in self.filtered(lambda p: p.partner_latitude and p.partner_longitude): geo_point = GeoPoint.from_latlon( self.env.cr, partner.partner_latitude, partner.partner_longitude) vals = {'geo_point': geo_point.wkt} partner.write(vals) partner.advocate_details_id.write(vals) return True @api.multi def generate_bvr_reference(self, product): """ Generates a bvr reference for a donation to the fund given by the product. :param product: fund product with a fund_id :return: bvr reference for the partner """ self.ensure_one() if isinstance(product, int): product = self.env['product.product'].browse(product) ref = self.ref bvr_reference = '0' * (9 + (7 - len(ref))) + ref bvr_reference += '0' * 5 bvr_reference += '6' # Fund donation bvr_reference += '0' * (4 - len(str(product.fund_id))) + str( product.fund_id) if len(bvr_reference) == 26: return mod10r(bvr_reference) ########################################################################## # VIEW CALLBACKS # ########################################################################## @api.multi def onchange_type(self, is_company): """ Put title 'Friends of Compassion for companies. """ res = super(ResPartner, self).onchange_type(is_company) if is_company: res['value']['title'] = self.env.ref( 'partner_compassion.res_partner_title_friends').id return res @api.model def get_lang_from_phone_number(self, phone): record = self.env['phone.common'].get_record_from_phone_number(phone) if record: partner = self.browse(record[1]) return record and partner.lang @api.multi def forget_me(self): # Store information in CSV, inside encrypted zip file. self._secure_save_data() super(ResPartner, self).forget_me() # Delete other objects and custom CH fields self.write({ 'church_id': False, 'church_unlinked': False, 'street3': False, 'firstname': False, 'deathdate': False, 'geo_point': False, 'partner_latitude': False, 'partner_longitude': False }) self.advocate_details_id.unlink() self.survey_inputs.unlink() self.env['mail.tracking.email'].search([ ('partner_id', '=', self.id)]).unlink() self.env['auditlog.log'].search([ ('model_id.model', '=', 'res.partner'), ('res_id', '=', self.id) ]).unlink() self.env['partner.communication.job'].search([ ('partner_id', '=', self.id) ]).unlink() self.message_ids.unlink() return True @api.multi def open_duplicates(self): partner_wizard = self.env['res.partner.check.double'].create({ 'partner_id': self.id, }) return { "type": "ir.actions.act_window", "res_model": "res.partner.check.double", "res_id": partner_wizard.id, "view_type": "form", "view_mode": "form", "target": "new", } ########################################################################## # PRIVATE METHODS # ########################################################################## @api.model def _address_fields(self): """ 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 _secure_save_data(self): """ Stores partner name and address in a CSV file on NAS, inside a password-protected ZIP file. :return: None """ smb_conn = self._get_smb_connection() if smb_conn and smb_conn.connect(SmbConfig.smb_ip, SmbConfig.smb_port): config_obj = self.env['ir.config_parameter'] share_nas = config_obj.get_param('partner_compassion.share_on_nas') store_path = config_obj.get_param('partner_compassion.store_path') src_zip_file = tempfile.NamedTemporaryFile() attrs = smb_conn.retrieveFile(share_nas, store_path, src_zip_file) file_size = attrs[1] if file_size: src_zip_file.flush() zip_dir = tempfile.mkdtemp() pyminizip.uncompress( src_zip_file.name, SmbConfig.file_pw, zip_dir, 0) csv_path = zip_dir + '/partner_data.csv' with open(csv_path, 'ab') as csv_file: csv_writer = csv.writer(csv_file) csv_writer.writerow([ str(self.id), self.ref, self.contact_address, fields.Date.today() ]) dst_zip_file = tempfile.NamedTemporaryFile() pyminizip.compress( csv_path, '', dst_zip_file.name, SmbConfig.file_pw, 5) try: smb_conn.storeFile(share_nas, store_path, dst_zip_file) except OperationFailure: logger.error( "Couldn't store secure partner data on NAS. " "Please do it manually by replicating the following " "file: " + dst_zip_file.name) def _get_smb_connection(self): """" Retrieve configuration SMB """ if not (SmbConfig.smb_user and SmbConfig.smb_pass and SmbConfig.smb_ip and SmbConfig.smb_port): return False else: return SMBConnection( SmbConfig.smb_user, SmbConfig.smb_pass, 'odoo', 'nas') def _get_active_sponsorships_domain(self): """ Include sponsorships of church members :return: search domain for recurring.contract """ domain = super(ResPartner, self)._get_active_sponsorships_domain() domain.insert(0, '|') domain.insert(3, ('partner_id', 'in', self.mapped('member_ids').ids)) domain.insert(4, '|') domain.insert(6, ('correspondent_id', 'in', self.mapped( 'member_ids').ids)) return domain @api.model def _notify_prepare_email_values(self, message): """ Always put reply_to value in mail notifications. :param message: the message record :return: mail values """ mail_values = super(ResPartner, self)._notify_prepare_email_values(message) # Find reply-to in mail template. base_template = None if message.model and self._context.get('custom_layout', False): base_template = self.env.ref(self._context['custom_layout'], raise_if_not_found=False) if not base_template: base_template = self.env.ref( 'mail.mail_template_data_notification_email_default') if base_template.reply_to: mail_values['reply_to'] = base_template.reply_to return mail_values
class ResPartner(geo_model.GeoModel): """Add geo_point to partner using a function field""" _inherit = "res.partner" @api.multi def geocode_address(self): """Get the latitude and longitude by requesting the "Nominatim" search engine from "openstreetmap". See: https://nominatim.org/release-docs/latest/api/Overview/ """ url = 'http://nominatim.openstreetmap.org/search' headers = {'User-Agent': 'Odoobot/11.0.1.0.0 (OCA-geospatial)'} for partner in self: pay_load = { 'limit': 1, 'format': 'json', 'street': partner.street or '', 'postalCode': partner.zip or '', 'city': partner.city or '', 'state': partner.state_id and partner.state_id.name or '', 'country': partner.country_id and partner.country_id.name or '', 'countryCodes': partner.country_id and partner.country_id.code or '' } request_result = requests.get(url, params=pay_load, headers=headers) try: request_result.raise_for_status() except Exception as e: _logger.exception('Geocoding error') raise exceptions.Warning( _('Geocoding error. \n %s') % e.message) vals = request_result.json() vals = vals and vals[0] or {} partner.write({ 'partner_latitude': vals.get('lat'), 'partner_longitude': vals.get('lon'), 'date_localization': fields.Date.today() }) @api.multi def geo_localize(self): self.geocode_address() return True @api.multi @api.depends('partner_latitude', 'partner_longitude') def _get_geo_point(self): """ Set the `geo_point` of the partner depending of its `partner_latitude` and its `partner_longitude` **Notes** If one of those parameters is not set then reset the partner's geo_point and do not recompute it """ for partner in self: if not partner.partner_latitude or not partner.partner_longitude: partner.geo_point = False else: partner.geo_point = geo_fields.GeoPoint.from_latlon( partner.env.cr, partner.partner_latitude, partner.partner_longitude) geo_point = geo_fields.GeoPoint(readonly=True, store=True, compute='_get_geo_point')
class luminarias(geo_model.GeoModel): _name = 'alumbrado.luminarias' _rec_name = 'xap_comp' xap_comp = fields.Char(string="Código compuesto") xap = fields.Integer(string="Código de la luminaría") cvecol = fields.Many2one('alumbrado.colonias',string="Clave de la colonia") cvecalle = fields.Char(string="Codigo de la calle") calle = fields.Char(string="Calle") tipo_c = fields.Char(string="tipo de calle") entre = fields.Char(string="entre que calles") t_piso = fields.Char(string="tipo de piso") nivel_c = fields.Char(string="nivel de la calle") ancho_c = fields.Char(string="ancho de la calle") mod_lam = fields.Char(string="modelo de la lampara") tecno = fields.Char(string="tecnologia led, vs, fluoresente") capac_pot = fields.Char(string="capacidad de potencia watts") marca_foco = fields.Char(string="marca de foco") marca_bala = fields.Char(string="marca de balastra") #perdidas = fields.Integer(string="datod e perdidas de energia") tension = fields.Char(string="tension baja o alta") #tipodeilum = fields.Char(string="tipo de iluminacion") zona_cfe = fields.Char(string="zona de cfe") tipoposte = fields.Char(string="tipo de poste") no_lum = fields.Float(string="numero de luminarios en el poste") altura_p = fields.Float(string="altura del poste") altura_lum = fields.Char(string="altura del luminario") brazo = fields.Char(string="numero de brazos en el poste") #jefe_cua = fields.Char(string="jefe de cuadrilla") #unidad = fields.Integer(string="numero de vehiculo") fecha_cens = fields.Date(string="fecha del censo") imagen = fields.Char(string="imagen de los luminarios") x = fields.Float(string="Coordenada X") y = fields.Float(string="Coordenada Y") observ = fields.Char(string="observaciones del poste, poda o limunario") servicio = fields.Char(string="tipo de servicio de cfe medido o estimado") rpu = fields.Char(string="numero de rpu cfe") medidor = fields.Char(string="numero de medidor cfe") geom = geo_fields.GeoPoint(string='Punto', srid=4326) name = fields.Char('Serial number', size=64, required=True) def genera_cod_comp(self): self.xap_comp = self.cvecol + self.xap def ConvertGeoJson(self, gjson, from_srid=4326, to_srid=3857): """ H.Stivalet 2019-09-25 Convierte el tipo de proyección en formato GeoJson usando funciones de PostGis. Requiere que la Base de datos de Odoo tenga instalada la estención de PosyGis params: gjson(str): GeoJson de un Point, Line o Polygon from_srid(int): SRID de entrada to_srid(int): SRID de salida returns: (str) GeoJson con la nueva proyección del Point, Line o Polygon """ if not isinstance(gjson, str) or gjson == '': return '' query = "select ST_AsGeoJSON(st_transform(st_setsrid(st_geomfromgeojson('" + gjson + "')," + str(from_srid) + ")," + str(to_srid) + "))" self._cr.execute(query) data = self._cr.fetchone() if data: gjson = data[0] return gjson @api.model#necesita hacer algo en el nivel del modelo en sí, no solo algunos registros. #api.model para estructuras de modelos, métodos auxiliares, etc. def create(self, values): if 'geom' in values and values['geom'] != '': values['geom'] = self.ConvertGeoJson(values['geom'], 3857, 4326) return super(luminarias, self).create(values) @api.multi#api.multi se usa para funciones en las que desea realizar alguna operación en uno o varios registros. Un ejemplo aquí puede ser establecer un campo en múltiples registros: def write(self, values): if 'geom' in values and values['aggeomeb_polygon'] != '': values['geom'] = self.ConvertGeoJson(values['geom'], 3857, 4326) return super(luminarias, self).write(values) @api.multi def read(self, fields=None, load='_classic_read'): record = super(luminarias, self).read(fields, load=load) if len(record) == 1 and 'geom' in record[0] and record[0]['ageb_polygon'] != '': record[0]['geom'] = self.ConvertGeoJson(record[0]['geom']) return record
class FSMLocation(geo_model.GeoModel): _name = 'fsm.location' _inherits = {'res.partner': 'partner_id'} _description = 'Field Service Location' @api.model def _tz_get(self): return [(tz, tz) for tz in sorted(pytz.all_timezones, key=lambda tz: tz if not tz.startswith('Etc/') else '_')] direction = fields.Char(string='Directions') partner_id = fields.Many2one('res.partner', string='Related Partner', required=True, ondelete='restrict', delegate=True, auto_join=True) owner_id = fields.Many2one('res.partner', string='Related Owner', required=True, ondelete='restrict', auto_join=True) customer_id = fields.Many2one('res.partner', string='Billed Customer', required=True, ondelete='restrict', auto_join=True) contact_id = fields.Many2one('res.partner', string='Primary Contact', ondelete='restrict', auto_join=True) tag_ids = fields.Many2many('fsm.tag', string='Tags') description = fields.Char(string='Description') territory_id = fields.Many2one('fsm.territory', string='Territory') branch_id = fields.Many2one('fsm.branch', string='Branch') district_id = fields.Many2one('fsm.district', string='District') region_id = fields.Many2one('fsm.region', string='Region') territory_manager_id = fields.Many2one(string='Primary Assignment', related='territory_id.person_id') district_manager_id = fields.Many2one(string='District Manager', related='district_id.partner_id') region_manager_id = fields.Many2one(string='Region Manager', related='region_id.partner_id') branch_manager_id = fields.Many2one(string='Branch Manager', related='branch_id.partner_id') timezone = fields.Selection(_tz_get, string='Timezone') parent_id = fields.Many2one('fsm.location', string='Parent') notes = fields.Text(string="Notes") person_ids = fields.Many2many('fsm.person', 'partner_id', string='Preferred Workers') # Geometry Field shape = geo_fields.GeoPoint(string='Coordinate') @api.model def create(self, vals): vals.update({'fsm_location': True}) return super(FSMLocation, self).create(vals) @api.onchange('territory_id') def _onchange_territory_id(self): self.branch_id = self.territory_id.branch_id @api.onchange('branch_id') def _onchange_branch_id(self): self.district_id = self.branch_id.district_id @api.onchange('district_id') def _onchange_district_id(self): self.region_id = self.district_id.region_id
class ResPartner(geo_model.GeoModel): """Add geo_point to partner using a function filed""" _inherit = "res.partner" geo_point = fields.GeoPoint("Address coordinates")