diff --git a/README.md b/README.md index a9a194ef..bc2ac559 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ addon | version | summary [l10n_us_form_1099](l10n_us_form_1099/) | 12.0.1.2.0 | Manage 1099 Types and Suppliers [l10n_us_gaap](l10n_us_gaap/) | 12.0.1.0.0 | United States Sample GAAP Chart of Accounts [l10n_us_gaap_mis_report](l10n_us_gaap_mis_report/) | 12.0.1.0.0 | MIS Builder Templates for US Chart of Accounts +[connector_alndata](connector_alndata/) | 12.0.1.0.0 | ALN Data Connector [//]: # (end addons) diff --git a/connector_alndata/README.rst b/connector_alndata/README.rst new file mode 100644 index 00000000..0eadce63 --- /dev/null +++ b/connector_alndata/README.rst @@ -0,0 +1,48 @@ +===================================================== +ALN ODOO Connector +===================================================== + +Usage +===== + + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* Open Source Integrators +* Odoo Community Association (OCA) + +Contributors +~~~~~~~~~~~~ + +* Maxime Chambreuil +* Serpent Consulting Services Pvt. Ltd. + +Maintainers +~~~~~~~~~~~ + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +This module is part of the `OCA/l10n-usa `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/connector_alndata/__init__.py b/connector_alndata/__init__.py new file mode 100644 index 00000000..60cdf63f --- /dev/null +++ b/connector_alndata/__init__.py @@ -0,0 +1,5 @@ +# Copyright (C) 2019 Open Source Integrators +# Copyright (C) 2019 Serpent Consulting Services Pvt. Ltd. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from . import models diff --git a/connector_alndata/__manifest__.py b/connector_alndata/__manifest__.py new file mode 100644 index 00000000..e6118705 --- /dev/null +++ b/connector_alndata/__manifest__.py @@ -0,0 +1,27 @@ +# Copyright (C) 2019 Open Source Integrators +# Copyright (C) 2019 Serpent Consulting Services Pvt. Ltd. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) + +{ + 'name': 'ALN Data Connector', + 'summary': '''This module allows you to synchronize your Odoo database + with ALN Data once a month''', + 'version': '12.0.1.0.0', + 'license': 'AGPL-3', + 'author': 'Open Source Integrators, Odoo Community Association (OCA),', + 'website': 'https://github.com/OCA/l10n-usa', + 'category': 'Tools', + 'maintainers': ['max3903'], + 'development_status': 'Beta', + 'depends': [ + 'crm', + 'fieldservice', + ], + 'data': [ + 'data/ir_config_parameter_data.xml', + 'data/sync_aln_data_view.xml', + 'views/res_partner_industry_view.xml', + 'views/res_partner_view.xml', + ], + 'installable': True, +} diff --git a/connector_alndata/data/ir_config_parameter_data.xml b/connector_alndata/data/ir_config_parameter_data.xml new file mode 100644 index 00000000..2dd10658 --- /dev/null +++ b/connector_alndata/data/ir_config_parameter_data.xml @@ -0,0 +1,57 @@ + + + + + alndata.api.url + http://api2.alndata.com/odata/ + + + + + alndata.api.key + b85f4d81-d726-42d4-a524-30f75e28a1ac + + + + + alndata.markets.rowversion + 0 + + + + + alndata.submarkets.rowversion + 0 + + + + + alndata.managementcompanies.rowversion + 0 + + + + + alndata.owners.rowversion + 0 + + + + + alndata.apartments.rowversion + 0 + + + + + alndata.contacts.rowversion + 0 + + + + + alndata.newconstructions.rowversion + 0 + + + diff --git a/connector_alndata/data/sync_aln_data_view.xml b/connector_alndata/data/sync_aln_data_view.xml new file mode 100644 index 00000000..f1e4af22 --- /dev/null +++ b/connector_alndata/data/sync_aln_data_view.xml @@ -0,0 +1,16 @@ + + + + + Sync with ALN Data + + code + 1 + months + -1 + + + model._cron_sync_with_aln() + + + diff --git a/connector_alndata/models/__init__.py b/connector_alndata/models/__init__.py new file mode 100644 index 00000000..e14dc2c9 --- /dev/null +++ b/connector_alndata/models/__init__.py @@ -0,0 +1,8 @@ +# Copyright (C) 2019 Open Source Integrators +# Copyright (C) 2019 Serpent Consulting Services Pvt. Ltd. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from . import res_partner +from . import res_partner_industry +from . import crm_lead +from . import fsm_location diff --git a/connector_alndata/models/crm_lead.py b/connector_alndata/models/crm_lead.py new file mode 100644 index 00000000..81f0d342 --- /dev/null +++ b/connector_alndata/models/crm_lead.py @@ -0,0 +1,725 @@ +# Copyright (C) 2019 Open Source Integrators +# Copyright (C) 2019 Serpent Consulting Services Pvt. Ltd. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +import threading +import logging +import requests +import json +import base64 + +from odoo import api, fields, models, _, sql_db +from odoo.exceptions import RedirectWarning + +_logger = logging.getLogger(__name__) + +us_timezone = { + 'M': 'US/Mountain', + 'C': 'US/Central', + 'E': 'US/Eastern', + 'P': 'US/Pacific', + 'H': 'US/Hawaii', +} + + +class Lead(models.Model): + _inherit = "crm.lead" + + lastdate_changed = fields.Datetime('LastDateChanged') + industry_id = fields.Many2one('res.partner.industry', 'Market') + lead_type = fields.Selection( + [('owner', 'Owner'), + ('management_company', 'Management Company'), + ('contact', 'Contact'), + ('new_construction', 'New Constructions')], + 'Lead Type') + + def aln_auth_login(self, data_key='', data_params=None): + """Aln Connector. + + This method is used to connect with ALN and fetch the data. + """ + config_obj = self.env['ir.config_parameter'] + url = config_obj.get_param('alndata.api.url') + api_key = config_obj.get_param('alndata.api.key') + full_content = [] + + url = url + data_key + count = 0 + flag = True + params = { + 'apikey': api_key, + 'Accept': 'application/json', + } + _logger.info('ALN Data Connector Data Key: %s', data_key) + if not data_params: + data_params = {} + params.update(data_params) + + # use count to fetch all data related filter by multiple request + while flag: + try: + if count > 0: + params.update({'$skip': count}) + response = requests.get(url, params=params) + response.raise_for_status() + except Exception as e: + _logger.error('%s', e) + content = response.content.decode('utf8') + if content: + content = json.loads(content).get('value') + if len(content or []) > 0: + count += len(content) + full_content += content + else: + flag = False + count = 0 + _logger.info('ALN Data Connector. Number of records: %s', + len(full_content)) + return full_content or [] + + def remove_data(self, model, datas, origin=''): + """Removed Data. + + This method is used to removed data from odoo database. + which deleted from ALN Data. + """ + obj = self.env[model] + removed_ids = [] + rem = obj + domain = [] + if model == 'res.partner': + domain += [('partner_type', '=', origin)] + elif origin and model == 'crm.lead': + domain += [('lead_type', '=', origin)] + for rec in obj.search(domain): + if model == 'crm.lead' and rec.referred not in datas: + removed_ids.append(rec.id) + rem += rec + elif model in ['res.partner', 'fsm.location'] and \ + rec.ref not in datas: + removed_ids.append(rec.id) + rem += rec + rem.write({'active': False}) + return removed_ids + + def get_state(self, obj, state_code): + """State. + + This method is used to search state from state code. + """ + return obj.search([('code', '=', state_code)], limit=1) + + def get_market(self, obj, market): + """Market/Submarket. + + This method is used to search market or submarket base on name. + """ + return obj.search([('name', '=', market)], limit=1) + + @api.model + def _prepare_industry_values(self, industry=None, origin='market'): + if not industry: + return {} + values = { + 'name': industry.get('MarketId'), + 'full_name': industry.get('MarketDescription'), + } + if origin == 'submarket': + values.update({ + 'name': industry.get('SubMarketDescription'), + 'ref': industry.get('SubmarketId'), + }) + return values + + @api.model + def sync_market_data(self): + """Synchronize Market Data. + + This method is used to sync market data. + """ + industry_obj = self.env['res.partner.industry'] + market_ids = [] + updated_market_ids = [] + markets = self.aln_auth_login('Markets') + for market in markets: + available_market = self.get_market(industry_obj, + market.get('MarketId')) + market_vals = self._prepare_industry_values(market) + if available_market: + available_market.write(market_vals) + updated_market_ids.append(available_market.id) + else: + market = industry_obj.create(market_vals) + market_ids.append(market.id) + _logger.info('ALN Data Connector.Created Market Ids : %s', market_ids) + _logger.info('ALN Data Connector.Updated Market Ids : %s', + updated_market_ids) + + @api.model + def sync_submarket_data(self): + """Synchronize Submarket Data. + + This method is used to sync submarket data. + """ + industry_obj = self.env['res.partner.industry'] + submarket_ids = [] + updated_submarket_ids = [] + for submarket in self.aln_auth_login('Submarkets'): + available_submarket = self.get_market( + industry_obj, submarket.get('SubMarketDescription')) + market_id = self.get_market(industry_obj, + submarket.get('Market')) + submarket_vals = self._prepare_industry_values( + submarket, 'submarket') + if market_id: + submarket_vals.update({ + 'parent_id': market_id.id}) + if not available_submarket: + submarket = industry_obj.create(submarket_vals) + submarket_ids.append(submarket.id) + else: + available_submarket.write(submarket_vals) + updated_submarket_ids.append(available_submarket.id) + _logger.info( + 'ALN Data Connector.Created SubMarket Ids : %s', submarket_ids) + _logger.info('ALN Data Connector.Updated SubMarket Ids : %s', + updated_submarket_ids) + + @api.model + def sync_status_code_data(self): + """Synchronize Status Codes Data. + + This method is used to sync status code data. + """ + status_obj = self.env['fsm.stage'] + stage_ids = [] + for state in self.aln_auth_login('StatusCodes'): + available_state = self.get_market(status_obj, + state.get('StatusDescription')) + if not available_state: + state_vals = { + 'name': state.get('StatusDescription'), + 'stage_type': 'location', + 'sequence': state.get('Status'), + } + state = status_obj.create(state_vals) + stage_ids.append(state.id) + _logger.info( + 'ALN Data Connector.Created FSM Stage Ids : %s', stage_ids) + + @api.model + def get_title(self, obj, title): + """Get Title. + + This method is used to get title from odoo database + if it is not exist then create a title. + """ + title_id = obj.search([('name', '=', title)], limit=1) + if not title_id: + title_id = obj.create({'name': title}) + return title_id.id + + def sync_owner_contact_data(self, origin=''): + """Synchronize Owner, Contact and Management Companies Data. + + This method is used to synchronize owner, contact and + management companies data.Differentiate data by origin. + Using origin prepared values for owner, contact and + management companies data. + """ + config_obj = self.env['ir.config_parameter'] + lead_obj = self.env['crm.lead'] + partner_obj = self.env['res.partner'] + industry_obj = self.env['res.partner.industry'] + state_obj = self.env['res.country.state'] + res_partner_title_obj = self.env['res.partner.title'] + partner_ids = [] + lead_ids = [] + contact_ids = [] + owner_ids = [] + construction_ids = [] + updated_partner_ids = [] + updated_lead_ids = [] + updated_contact_ids = [] + updated_owner_ids = [] + updated_construction_ids = [] + rowversion_list = [] + last_update_date = [] + params = {} + removed_lead = [] + removed_partner = [] + + if not origin: + management_company_key = config_obj.get_param( + 'alndata.managementcompanies.rowversion') + params.update({'$expand': 'Addresses,PhoneNumbers'}) + if management_company_key != '0': + params.update( + {'$filter': 'RowVersion lt ' + management_company_key, + '$orderby': 'RowVersion'}) + old_manage_company = self.aln_auth_login( + 'ManagementCompanies', params) + company_id_ref = [dat.get('ManagementCompanyEntityId') + for dat in old_manage_company] + removed_lead = self.remove_data( + 'crm.lead', company_id_ref, 'management_company') + removed_partner = self.remove_data( + 'res.partner', company_id_ref, 'management_company') + params.update( + {'$filter': 'RowVersion gt ' + management_company_key, + '$orderby': 'RowVersion'}) + datas = self.aln_auth_login( + 'ManagementCompanies', params) + elif origin == 'owner': + datas = self.aln_auth_login('Owners') + old_owner = [owner.get('OwnerId') for owner in datas] + removed_lead = self.remove_data('crm.lead', old_owner, 'owner') + elif origin == 'new_construction': + construction_key = config_obj.get_param( + 'alndata.newconstructions.rowversion') + if construction_key != '0': + params.update( + {'$filter': "LastDateNewConstructionChanged lt datetime'" + + construction_key + "'", + '$orderby': "LastDateNewConstructionChanged"}) + old_construction = self.aln_auth_login( + 'NewConstructions', params) + constructions = [dat.get('NewConstructionId') + for dat in old_construction] + removed_lead = self.remove_data( + 'crm.lead', constructions, 'new_construction') + params.update( + {'$filter': "LastDateNewConstructionChanged gt datetime'" + + construction_key + "'", + '$orderby': "LastDateNewConstructionChanged"}) + datas = self.aln_auth_login('NewConstructions', params) + else: + contact_key = config_obj.get_param('alndata.contacts.rowversion') + params.update({'$expand': 'Addresses,PhoneNumbers,JobCategories'}) + if contact_key != '0': + params.update({'$filter': 'RowVersion lt ' + contact_key, + '$orderby': 'RowVersion'}) + old_contacts = self.aln_auth_login('Contacts', params) + contacts = [cont.get('ContactId') for cont in old_contacts] + removed_lead = self.remove_data( + 'crm.lead', contacts, 'contact') + params.update({'$filter': 'RowVersion gt ' + contact_key, + '$orderby': 'RowVersion'}) + datas = self.aln_auth_login('Contacts', params) + + for data in datas: + obj = lead_obj + name = data.get('ManagementCompanyName') + rowversion = int(data.get('RowVersion', 0)) + rowversion_list.append(rowversion) + last_changed = data.get('ManagementCompanyLastDateChanged') + referred = data.get('ManagementCompanyEntityId') + lead_type = 'management_company' + if origin == 'owner': + name = data.get('OwnerName') + lead_type = 'owner' + last_changed = False + referred = data.get('OwnerId') + elif origin == 'contact': + lead_type = 'contact' + name = data.get('ContactName') + last_changed = data.get('ContactLastDateChanged') + referred = data.get('ContactId') + elif origin == 'new_construction': + lead_type = "new_construction" + name = data.get('ProjectName') + referred = data.get('NewConstructionId') + last_changed = data.get('LastDateNewConstructionChanged') + if last_changed: + last_update_date.append(last_changed) + domain = [('name', '=', name)] + if data.get('ManagementCompanyParentId'): + obj = partner_obj + domain += [('partner_type', '=', 'management_company'), + ('ref', '=', referred)] + else: + domain += [('lead_type', '=', lead_type), + ('referred', '=', referred)] + market = self.get_market( + industry_obj, data.get('ManagementCompanyMarket')) + lead_vals = { + 'name': name, + } + if data.get('ManagementCompanyWebSite'): + lead_vals.update( + {'website': data.get('ManagementCompanyWebSite'), + 'industry_id': market.id, }) + if data.get('Addresses', False): + address = data['Addresses'][0] + state = self.get_state(state_obj, + address.get('AddressState')) + country_id = state and state.country_id.id or 0 + lead_vals.update( + { + 'street': address.get('AddressLine1'), + 'street2': address.get('AddressLine2'), + 'city': address.get('AddressCity'), + 'state_id': state.id, + 'zip': address.get('AddressZIP'), + 'country_id': country_id, + }) + if data.get('ManagementCompanyParentId'): + lead_vals.update( + { + 'address_type': address.get('AddressType'), + 'ref': data.get('ManagementCompanyEntityId'), + 'partner_type': 'management_company', + }) + else: + lead_vals.update( + { + 'lead_type': lead_type, + 'lastdate_changed': last_changed, + 'referred': referred, + }) + if data.get('PhoneNumbers', False): + numbers = data['PhoneNumbers'] + for num in numbers: + if num.get('IsPrimary') == 'Y': + lead_vals.update({ + 'phone': num.get('Number') + }) + if (num.get('PhoneNumberType') == "Fax Number" and + data.get('ManagementCompanyParentId')): + lead_vals.update({ + 'fax': num.get('Number') + }) + if origin == 'contact': + lead = lead_obj.search([ + ('referred', '=', data.get('CorporateEntityId'))], limit=1) + partner_name = lead and lead.name or '' + function = '' + for job in data.get('JobCategories'): + function += job.get('JobCategoryDescription') + ',' + lead_vals.update({ + 'email_from': data.get('ContactEMail'), + 'partner_name': partner_name, + 'contact_name': data.get('ContactName'), + 'title': self.get_title(res_partner_title_obj, + data.get('ContactTitle')), + 'function': function[:-1], + }) + if origin == 'owner': + address = data.get('OwnerAddress', '') + address_lst = address and address.split('\r\n') or [] + street = city = state_code = owner_zip = '' + if len(address_lst) > 1: + street = address_lst[0] + city_state = address_lst[1].split(',') + if len(city_state) > 1: + city = city_state[0] + state_zip = (city_state[1].strip()).split(' ') + if len(state_zip) > 1: + state_code = state_zip[0] + owner_zip = state_zip[1] + state = self.get_state(state_obj, state_code) + lead_vals.update({ + 'street': street, + 'city': city, + 'state_id': state.id, + 'zip': owner_zip, + 'country_id': state.country_id.id, + 'phone': data.get('OwnerPhone'), + 'contact_name': data.get('OwnerName') + }) + if origin == 'new_construction': + state = self.get_state(state_obj, data.get('ProjectState')) + description = '' + if data.get('StartDate'): + description += "Start Date : " + data.get('StartDate') + if data.get('LeaseDate'): + description += "\n" + "Lease Date : " + \ + data.get('LeaseDate') + if data.get('OccupancyDate'): + description += "\n" + "Occupancy Date : " + \ + data.get('OccupancyDate') + if data.get('CompletionDate'): + description += "\n" + "Completion Date : " + \ + data.get('CompletionDate') + if data.get('Progress'): + description += "\n" + "Progress : " + data.get('Progress') + if data.get('Market'): + description += "\n" + "Market : " + data.get('Market') + lead_vals.update({ + 'street': data.get('ProjectAddress'), + 'city': data.get('ProjectCity'), + 'state_id': state.id, + 'country_id': state.country_id.id, + 'zip': data.get('ProjectZIP'), + 'partner_name': data.get('Company'), + 'description': description, + }) + + lead = obj.search(domain) + if lead: + lead.write(lead_vals) + if origin == 'contact': + updated_contact_ids.append(lead.id) + elif origin == 'owner': + updated_owner_ids.append(lead.id) + elif origin == 'new_construction': + updated_construction_ids.append(lead.id) + elif data.get('ManagementCompanyParentId'): + updated_partner_ids.append(lead.id) + else: + updated_lead_ids.append(lead.id) + else: + lead = obj.create(lead_vals) + if origin == 'contact': + contact_ids.append(lead.id) + elif origin == 'owner': + owner_ids.append(lead.id) + elif origin == 'new_construction': + construction_ids.append(lead.id) + elif data.get('ManagementCompanyParentId'): + partner_ids.append(lead.id) + else: + lead_ids.append(lead.id) + row_version = rowversion_list and max(rowversion_list) or 0 + max_date = last_update_date and max(last_update_date) or 0 + + if origin == 'contact': + _logger.info( + 'ALN Data Connector.Created Contacts : %s', contact_ids) + _logger.info('ALN Data Connector.Updated Contacts : %s', + updated_contact_ids) + _logger.info( + 'ALN Data Connector.Deleted Contacts : %s', removed_lead) + if row_version: + config_obj.sudo().set_param( + 'alndata.contacts.rowversion', row_version) + elif origin == 'owner': + _logger.info('ALN Data Connector.Created Owners : %s', owner_ids) + _logger.info('ALN Data Connector.Updated Owners : %s', + updated_owner_ids) + _logger.info( + 'ALN Data Connector.Deleted Owners : %s', removed_lead) + elif origin == 'new_construction': + _logger.info('ALN Data Connector.Created New Constructions : %s', + construction_ids) + _logger.info('ALN Data Connector.Updated Constructions : %s', + updated_construction_ids) + _logger.info('ALN Data Connector.Deleted Constructions : %s', + removed_lead) + if max_date: + config_obj.sudo().set_param( + 'alndata.newconstructions.rowversion', max_date) + else: + if row_version: + config_obj.sudo().set_param( + 'alndata.managementcompanies.rowversion', row_version) + _logger.info( + 'ALN Data Connector.Created Management Companies Partners' + ' : %s', partner_ids) + _logger.info( + 'ALN Data Connector.Updated Management Companies Partners ' + ': %s', + updated_partner_ids) + _logger.info( + 'ALN Data Connector.Deleted Management Companies Partners ' + ': %s', + removed_partner) + _logger.info( + 'ALN Data Connector.Created Management Companies Leads ' + ': %s', lead_ids) + _logger.info('ALN Data Connector.Updated Management Companies ' + 'Leads : %s', + updated_lead_ids) + _logger.info( + 'ALN Data Connector.Deleted Management Companies Leads :' + ' %s', removed_lead) + + @api.model + def sync_apartment_data(self): + """Synchronize Apartments Data. + + This method is used to synchronize apartment data. + """ + create_apart_ids = [] + update_apart_ids = [] + config_obj = self.env['ir.config_parameter'] + stage_obj = self.env['fsm.stage'] + industry_obj = self.env['res.partner.industry'] + partner_obj = self.env['res.partner'] + state_obj = self.env['res.country.state'] + fsm_obj = self.env['fsm.location'] + rowversion_list = [] + removed_apart_ids = [] + + params = {'$expand': 'Addresses,PhoneNumbers'} + apartment_key = config_obj.get_param( + 'alndata.apartments.rowversion') + if apartment_key != '0': + params.update({'$filter': 'RowVersion lt ' + apartment_key, + '$orderby': 'RowVersion'}) + old_apart = self.aln_auth_login('Apartments', params) + apartments = [apart.get('ApartmentId') for apart in old_apart] + removed_apart_ids = self.remove_data('fsm.location', apartments) + params.update({'$filter': 'RowVersion gt ' + apartment_key, + '$orderby': 'RowVersion'}) + + for apart in self.aln_auth_login('Apartments', params): + contact = partner_obj + industry = industry_obj + prop = apart.get('Property') + rowversion_list.append(int(apart.get('RowVersion'))) + stage = stage_obj.search( + [('sequence', '=', prop.get('Status')), + ('stage_type', '=', 'location')], + limit=1) + if prop.get('SubmarketId'): + industry = industry_obj.search( + [('ref', '=', prop.get('SubmarketId'))], + limit=1) + if not industry: + industry = self.get_market( + industry_obj, prop.get('Market')) + + image_data = b'' + if prop.get('AptPictureURL'): + image_res = requests.get(prop.get('AptPictureURL')) + image_data = image_res.content + apartment_vals = { + 'ref': apart.get('ApartmentId'), + 'stage_id': stage.id, + 'name': prop.get('AptName'), + 'email': prop.get('EMailAddress'), + 'industry_id': industry.id, + 'num_of_unit': prop.get('NumUnits'), + 'year_built': prop.get('YearBuilt'), + 'year_remodeled': prop.get('YearRemodeled'), + 'direction': prop.get('Directions'), + 'notes': prop.get('PropertyDescription'), + 'website': prop.get('AptHomePage'), + 'image': base64.b64encode(image_data), + 'tz': us_timezone.get(prop.get('TimeZone', '').strip(), ''), + } + if prop.get('CurrManager'): + contact = partner_obj.search( + [('name', '=', prop.get('CurrManager')), + ('partner_type', '=', 'contact')], + limit=1) + apartment_vals.update({'contact_id': contact and contact.id}) + if prop.get('CorporateManagementCompanyId'): + management_company = partner_obj.search( + [('ref', '=', prop.get('CorporateManagementCompanyId')), + ('partner_type', '=', 'management_company')], + limit=1) + apartment_vals.update( + { + 'commercial_partner_id': management_company and + management_company.id}) + if prop.get('OwnerId'): + owner = partner_obj.search( + [('ref', '=', prop.get('OwnerId')), + ('partner_type', '=', 'owner')], + limit=1) + apartment_vals.update( + { + 'owner_id': owner and owner.id}) + if apart.get('Addresses', False): + address = apart['Addresses'][0] + state = self.get_state(state_obj, + address.get('AddressState')) + country_id = state and state.country_id.id or 0 + apartment_vals.update( + { + 'street': address.get('AddressLine1'), + 'street2': address.get('AddressLine2'), + 'city': address.get('AddressCity'), + 'state_id': state.id, + 'zip': address.get('AddressZIP'), + 'country_id': country_id, + }) + if apart.get('PhoneNumbers', False): + numbers = apart['PhoneNumbers'] + for num in numbers: + if num.get('IsPrimary') == 'Y': + apartment_vals.update({ + 'phone': num.get('Number') + }) + if (num.get('PhoneNumberType') == "Fax Number" and + num.get('ManagementCompanyParentId')): + apartment_vals.update({ + 'fax': num.get('Number') + }) + fsm_location = fsm_obj.search( + [('name', '=', prop.get('AptName')), + ('ref', '=', apart.get('ApartmentId'))], + limit=1) + if not fsm_location: + fsm = fsm_obj.create(apartment_vals) + create_apart_ids.append(fsm.id) + else: + fsm_location.write(apartment_vals) + update_apart_ids.append(fsm_location.id) + row_version = rowversion_list and max(rowversion_list) or 0 + if row_version: + config_obj.sudo().set_param( + 'alndata.apartments.rowversion', row_version) + _logger.info( + 'ALN Data Connector.Created Fsm Location : %s', create_apart_ids) + _logger.info( + 'ALN Data Connector.Updated Fsm Location : %s', update_apart_ids) + _logger.info( + 'ALN Data Connector.Deleted Fsm Location : %s', removed_apart_ids) + + @api.model + def sync_aln_data_with_threading(self): + """Synchronize data with ALN Data By Threading. + + This method is used to get data from ALN Data usinf threading. + """ + _logger.info('ALN Data Connector. Starting the synchronization...') + new_cr = sql_db.db_connect(self.env.cr.dbname).cursor() + uid, context = self.env.uid, self.env.context + with api.Environment.manage(): + self.env = api.Environment(new_cr, uid, context) + # Get Market Data + self.sync_market_data() + + # Get Sub-Market Data + self.sync_submarket_data() + + # Get Owner Data + self.sync_owner_contact_data(origin='owner') + + # Get Management Companies Data + self.sync_owner_contact_data() + + # Get Contact Data + self.sync_owner_contact_data('contact') + + # Get the New Constructions Data + self.sync_owner_contact_data('new_construction') + + # Get the Apartment Data + self.sync_apartment_data() + + new_cr.commit() + new_cr.close() + _logger.info('ALN Data Connector. Synchronization successful.') + + @api.model + def _cron_sync_with_aln(self): + """Synchronize data with ALN Data. + + This method is used to get data from ALN Data and create data in odoo. + """ + api_key = self.env['ir.config_parameter'].get_param('alndata.api.key') + if api_key == '0': + action = self.env.ref('base.ir_config_list_action') + msg = _('Cannot find a ALN Data URL and API key, ' + 'You should configure it. ' + '\nPlease go to System Parameters.') + raise RedirectWarning(msg, action.id, + _('Go to the configuration panel')) + thred_cal = threading.Thread( + target=self.sync_aln_data_with_threading) + thred_cal.start() diff --git a/connector_alndata/models/fsm_location.py b/connector_alndata/models/fsm_location.py new file mode 100644 index 00000000..b49cd0f1 --- /dev/null +++ b/connector_alndata/models/fsm_location.py @@ -0,0 +1,17 @@ +# Copyright (C) 2019 Open Source Integrators +# Copyright (C) 2019 Serpent Consulting Services Pvt. Ltd. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import fields, models + + +class FsmLocation(models.Model): + _inherit = "fsm.location" + + industry_id = fields.Many2one('res.partner.industry', 'Market') + num_of_unit = fields.Integer('Number of Units') + year_built = fields.Char('Build Year') + year_remodeled = fields.Char('YearRemodeled') + owner_id = fields.Many2one('res.partner', string='Related Owner', + required=False, ondelete='restrict', + auto_join=True) diff --git a/connector_alndata/models/res_partner.py b/connector_alndata/models/res_partner.py new file mode 100644 index 00000000..95764111 --- /dev/null +++ b/connector_alndata/models/res_partner.py @@ -0,0 +1,33 @@ +# Copyright (C) 2019 Open Source Integrators +# Copyright (C) 2019 Serpent Consulting Services Pvt. Ltd. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import api, fields, models + + +class ResPartner(models.Model): + _inherit = "res.partner" + + @api.multi + def _compute_number_unit(self): + """Calculate Number of Units. + + This method is used to calculate the number of units + of the apartment which links with a particular corporate company. + """ + apart_obj = self.env['fsm.location'] + for rec in self: + apartments = apart_obj.search_read( + [('commercial_partner_id', '=', rec.id)], ['num_of_unit']) + rec.num_unit = sum([apart.get('num_of_unit') + for apart in apartments]) + + partner_type = fields.Selection( + [('owner', 'Owner'), + ('management_company', 'Management Company'), + ('contact', 'Contact'), + ('new_construction', 'New Constructions')], + 'Partner Type') + address_type = fields.Char('AddressType') + num_unit = fields.Integer(compute="_compute_number_unit", + string="Number of Apartments") diff --git a/connector_alndata/models/res_partner_industry.py b/connector_alndata/models/res_partner_industry.py new file mode 100644 index 00000000..e4f8399f --- /dev/null +++ b/connector_alndata/models/res_partner_industry.py @@ -0,0 +1,34 @@ +# Copyright (C) 2019 Open Source Integrators +# Copyright (C) 2019 Serpent Consulting Services Pvt. Ltd. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import api, fields, models + + +class ResPartnerIndustry(models.Model): + _inherit = "res.partner.industry" + + ref = fields.Char('Reference') + parent_id = fields.Many2one('res.partner.industry', 'Parent') + + _sql_constraints = [ + ('ref_uniq', 'unique(ref)', 'Reference must be unique!'), + ] + + @api.multi + def name_get(self): + """Compute Display Name. + + Return the display name, including their direct parent by default. + """ + res = [] + for partner in self: + display_name = '' + if partner.parent_id and not partner.name: + display_name += partner.parent_id.name + if partner.parent_id and partner.name: + display_name += partner.parent_id.name + '/' + partner.name + if partner.name and not partner.parent_id: + display_name += partner.name + res.append((partner.id, display_name)) + return res diff --git a/connector_alndata/readme/CONTRIBUTORS.rst b/connector_alndata/readme/CONTRIBUTORS.rst new file mode 100644 index 00000000..76464a46 --- /dev/null +++ b/connector_alndata/readme/CONTRIBUTORS.rst @@ -0,0 +1,8 @@ +* Open Source Integrators + + * Mayank Gosai + * Maxime Chambreuil + +* Serpent Consulting Services Pvt. Ltd. + + * Nikita Vaghela diff --git a/connector_alndata/readme/DESCRIPTION.rst b/connector_alndata/readme/DESCRIPTION.rst new file mode 100644 index 00000000..140525d5 --- /dev/null +++ b/connector_alndata/readme/DESCRIPTION.rst @@ -0,0 +1 @@ +This module allows you to synchronize your Odoo database with ALN Data once a month. diff --git a/connector_alndata/readme/USAGE.rst b/connector_alndata/readme/USAGE.rst new file mode 100644 index 00000000..0bb49e47 --- /dev/null +++ b/connector_alndata/readme/USAGE.rst @@ -0,0 +1,2 @@ +In the menu settings->System Parameters +Set the ALN API key as value of 'alndata.api.key' key. diff --git a/connector_alndata/static/description/icon.png b/connector_alndata/static/description/icon.png new file mode 100644 index 00000000..3a0328b5 Binary files /dev/null and b/connector_alndata/static/description/icon.png differ diff --git a/connector_alndata/static/description/index.html b/connector_alndata/static/description/index.html new file mode 100644 index 00000000..a24248f4 --- /dev/null +++ b/connector_alndata/static/description/index.html @@ -0,0 +1,52 @@ +
+ +
+
+

+ ALN Data Connector +

+
+ + +
+
+
+
+
+

+ + This module is used to fetch data from ALN data and + synchronize that data in ODOO. +

+

+ + This module contains cron job for synchronization of data. +

+

+ + Set the system parameter for ALN API key. +

+
+
+
+ +
diff --git a/connector_alndata/tests/__init__.py b/connector_alndata/tests/__init__.py new file mode 100644 index 00000000..e62e4cd4 --- /dev/null +++ b/connector_alndata/tests/__init__.py @@ -0,0 +1,5 @@ +# Copyright (C) 2019 Open Source Integrators +# Copyright (C) 2019 Serpent Consulting Services Pvt. Ltd. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from . import test_connector_alndata diff --git a/connector_alndata/tests/test_connector_alndata.py b/connector_alndata/tests/test_connector_alndata.py new file mode 100644 index 00000000..be87fd8d --- /dev/null +++ b/connector_alndata/tests/test_connector_alndata.py @@ -0,0 +1,25 @@ +# Copyright (C) 2019 Open Source Integrators +# Copyright (C) 2019 Serpent Consulting Services Pvt. Ltd. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo.tests.common import TransactionCase + +import logging +_logger = logging.getLogger(__name__) + + +class TestConnectorAlndata(TransactionCase): + + def setUp(self): + super(TestConnectorAlndata, self).setUp() + self.config_model = self.env['ir.config_parameter'] + self.crm_obj = self.env['crm.lead'] + + def test_cron_sync_with_aln(self): + """ + Test cron job for synchronization data. + """ + self.config_model.set_param( + 'alndata.api.key', + 'b85f4d81-d726-42d4-a524-30f75e28a1ac') + self.crm_obj._cron_sync_with_aln() diff --git a/connector_alndata/views/res_partner_industry_view.xml b/connector_alndata/views/res_partner_industry_view.xml new file mode 100644 index 00000000..8a519583 --- /dev/null +++ b/connector_alndata/views/res_partner_industry_view.xml @@ -0,0 +1,27 @@ + + + + + Industry + res.partner.industry + + + + + + + + + + + Industry + res.partner.industry + + + + + + + + + diff --git a/connector_alndata/views/res_partner_view.xml b/connector_alndata/views/res_partner_view.xml new file mode 100644 index 00000000..0aa195f3 --- /dev/null +++ b/connector_alndata/views/res_partner_view.xml @@ -0,0 +1,27 @@ + + + + + Partner + res.partner + + + + + + + + + + + Partner + res.partner + + + + + + + + + diff --git a/oca_dependencies.txt b/oca_dependencies.txt index 198f3dfe..1dee1878 100644 --- a/oca_dependencies.txt +++ b/oca_dependencies.txt @@ -4,3 +4,4 @@ account-payment account-reconcile bank-payment mis-builder +field-service