Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • coopdevs/comunitats-energetiques/odoo-ce
1 result
Show changes
Showing
with 787 additions and 22 deletions
......@@ -14,6 +14,15 @@ class ResPartner(models.Model):
("not_share", "I prefer to not share it"),
]
)
signup_token = fields.Char(
groups="base.group_erp_manager,energy_communities.group_admin"
)
signup_type = fields.Char(
groups="base.group_erp_manager,energy_communities.group_admin",
)
signup_expiration = fields.Datetime(
groups="base.group_erp_manager,energy_communities.group_admin"
)
@api.model
def create(self, vals):
......
from odoo import models
class ResPartnerBank(models.Model):
_inherit = "res.partner.bank"
_sql_constraints = [
(
"unique_number",
"unique(sanitized_acc_number, company_id, partner_id)",
"Account Number must be unique",
),
]
......@@ -60,7 +60,7 @@ class ResUsers(models.Model):
if user.oauth_uid:
# already sync'ed somewhere else
continue
keycloak_user = self._get_or_create_user(token, provider_id, user)
keycloak_user = self._get_or_create_kc_user(token, provider_id, user)
keycloak_key = self._LOGIN_MATCH_KEY.split(":")[0]
keycloak_login_provider = self.env.ref(
"energy_communities.keycloak_login_provider"
......@@ -76,7 +76,7 @@ class ResUsers(models.Model):
logger.debug("Create keycloak users STOP")
return True
def _get_users(self, token, provider_id, **params):
def _get_kc_users(self, token, provider_id, **params):
"""Retrieve users from Keycloak.
:param token: a valida auth token from Keycloak
......@@ -120,14 +120,14 @@ class ResUsers(models.Model):
except JSONDecodeError:
raise exceptions.UserError(_("Something went wrong. Please check logs."))
def _get_or_create_user(self, token, provider_id, odoo_user):
def _get_or_create_kc_user(self, token, provider_id, odoo_user):
"""Lookup for given user on Keycloak: create it if missing.
:param token: valid auth token from Keycloak
:param odoo_user: res.users record
"""
odoo_key = self._LOGIN_MATCH_KEY.split(":")[1]
keycloak_user = self._get_users(
keycloak_user = self._get_kc_users(
token, provider_id, search=odoo_user.mapped(odoo_key)[0]
)
if keycloak_user:
......@@ -137,7 +137,7 @@ class ResUsers(models.Model):
return keycloak_user[0]
else:
values = self._create_user_values(odoo_user)
keycloak_user = self._create_user(token, provider_id, **values)
keycloak_user = self._create_kc_user(token, provider_id, **values)
return keycloak_user
def _create_user_values(self, odoo_user):
......@@ -146,6 +146,7 @@ class ResUsers(models.Model):
"username": odoo_user.login,
"email": odoo_user.partner_id.email,
"attributes": {"lang": [odoo_user.lang]},
"enabled": True,
}
if "firstname" in odoo_user.partner_id:
......@@ -175,7 +176,7 @@ class ResUsers(models.Model):
firstname, lastname = name_parts[0], " ".join(name_parts[1:])
return firstname, lastname
def _create_user(self, token, provider_id, **data):
def _create_kc_user(self, token, provider_id, **data):
"""Create a user on Keycloak w/ given data."""
logger.info("CREATE Calling %s" % provider_id.admin_user_endpoint)
headers = {
......@@ -190,8 +191,118 @@ class ResUsers(models.Model):
self._validate_response(resp, no_json=True)
# yes, Keycloak sends back NOTHING on create
# so we are forced to do anothe call to get its data :(
return self._get_users(token, provider_id, search=data["username"])[0]
return self._get_kc_users(token, provider_id, search=data["username"])[0]
def get_role_codes(self):
# TODO Map all code to company and enable (We should update the API schema too)
return self.role_line_ids[0].role_id.code
def send_reset_password_mail(self):
provider_id = self.env.ref("energy_communities.keycloak_admin_provider")
provider_id.validate_admin_provider()
headers = {"Authorization": "Bearer %s" % self._get_admin_token(provider_id)}
headers["Content-Type"] = "application/json"
if provider_id.reset_password_endpoint:
endpoint = provider_id.reset_password_endpoint.format(kc_uid=self.oauth_uid)
response = requests.put(
endpoint, headers=headers, data='["UPDATE_PASSWORD", "VERIFY_EMAIL"]'
)
else:
raise exceptions.UserError(_("Reset password url is not set."))
if response.status_code != 204:
raise exceptions.UserError(
_(
"Something went wrong. Mail can not be sended. More details: {}"
).format(response.json())
)
def make_internal_user(self):
already_user = self.env["res.users.role.line"].search(
[
("user_id.id", "=", self.id),
("active", "=", True),
("role_id.code", "=", "role_internal_user"),
]
)
if not already_user:
role = self.env.ref("energy_communities.role_internal_user")
self.write(
{
"role_line_ids": [
(
0,
0,
{
"user_id": self.id,
"active": True,
"role_id": role.id,
},
)
]
}
)
def make_ce_user(self, company_id, role_name):
role = self.env["res.users.role"].search([("code", "=", role_name)])
current_role = self.env["res.users.role.line"].search(
[
("user_id", "=", self.id),
("active", "=", True),
("company_id", "=", company_id),
]
)
if current_role:
current_role.write({"role_id": role})
else:
self.write(
{
"company_ids": [(4, company_id)],
"role_line_ids": [
(
0,
0,
{
"user_id": self.id,
"active": True,
"role_id": role.id,
"company_id": company_id,
},
)
],
}
)
def make_coord_user(self, company_id, role_name):
# create ce user on this company
self.make_ce_user(company_id, role_name)
# apply manager role the child companies
company = self.env["res.company"].browse(company_id)
child_companies = company.get_child_companies()
for child_company in child_companies:
self.make_ce_user(child_company.id, "role_ce_manager")
def add_energy_community_role(self, company_id, role_name):
if role_name == "role_ce_member" or role_name == "role_ce_admin":
self.make_ce_user(company_id, role_name)
elif role_name == "role_coord_worker" or role_name == "role_coord_admin":
self.make_coord_user(company_id, role_name)
else:
raise exceptions.UserError(_("Role not found"))
def create_energy_community_base_user(
cls, vat, first_name, last_name, lang_code, email
):
vals = {
"login": vat,
"firstname": first_name,
"lastname": last_name,
"lang": lang_code,
"email": email,
}
user = cls.sudo().with_context(no_reset_password=True).create(vals)
user.make_internal_user()
user.create_users_on_keycloak()
user.send_reset_password_mail()
return user
......@@ -5,3 +5,7 @@ class ResUsersRole(models.Model):
_inherit = "res.users.role"
code = fields.Char(string="Code")
class ResUsersRoleLine(models.Model):
_inherit = "res.users.role.line"
......@@ -137,3 +137,9 @@ class SubscriptionRequest(models.Model):
"""
self = self.with_company(self.company_id)
return self.validate_subscription_request()
def _prepare_invoice_line(self, product, partner, qty):
res = super()._prepare_invoice_line(product, partner, qty)
res["tax_ids"] = product.taxes_id
return res
......@@ -16,6 +16,23 @@ class Client:
def __init__(self, baseurl):
self.baseurl = baseurl
def get(self, route, token=None):
"""Send a GET HTTP requests
Args:
route (str): String with the route to the endpoint
Return:
**response**: Return the response object
"""
headers = {"Authorization": token, "Content-Type": "application/json"}
return self._send_request(
verb="GET",
url=self._format_url(route),
payload={},
extra_headers=headers,
)
def post(self, route, token=None, body=None):
"""Send a POST HTTP requests
......@@ -26,10 +43,7 @@ class Client:
Return:
**response**: Return the response object
"""
headers = {
"Authorization": token,
"Content-Type": "application/json"
}
headers = {"Authorization": token, "Content-Type": "application/json"}
return self._send_request(
verb="POST",
url=self._format_url(route),
......@@ -47,10 +61,7 @@ class Client:
Return:
**response**: Return the response object
"""
headers = {
"Authorization": token,
"Content-Type": "application/json"
}
headers = {"Authorization": token, "Content-Type": "application/json"}
return self._send_request(
verb="PUT", url=self._format_url(route), payload=body, extra_headers=headers
)
......@@ -84,8 +95,7 @@ class Client:
if headers.get("Content-Type") == "application/json":
payload = json.dumps(payload)
logger.info("{verb} {url} \n {body}".format(
verb=verb, url=url, body=payload))
logger.info("{verb} {url}".format(verb=verb, url=url))
try:
response = requests.request(
......
......@@ -10,6 +10,15 @@ class LandingPage:
self.token = token
self.id = id
def get(self):
"""
Get Landing Page data.
"""
response_data = Client(self.baseurl).get(
"{url_path}/{id}".format(url_path=self._url_path, id=self.id), self.token
)
return response_data
def create(self, body):
"""
Creates a Landing Page instance.
......@@ -28,5 +37,4 @@ class LandingPage:
self.token,
body,
)
return response_data
......@@ -3,3 +3,15 @@ landing_page_admin,access_landing_page_admin,model_landing_page,group_admin,1,1,
landing_page_platform_manager,access_landing_page_platform_manager,model_landing_page,group_platform_manager,1,1,1,1
landing_page_public,access_landing_page_public,model_landing_page,base.group_public,1,0,0,0
assign_crm_to_coordinator_company_wizard_admin,assign_crm_to_coordinator_company_wizard_admin,model_assign_crm_to_coordinator_company_wizard,group_platform_manager,1,1,1,1
assign_admin_wizard,assign_admin_wizard_admin,model_assign_admin_wizard,group_admin,1,1,1,1
multicompany_easy_creation,multicompany_easy_creation_admin,model_account_multicompany_easy_creation_wiz,group_admin,1,1,1,1
user_role_read,user_role_read_admin,model_res_users_role,group_admin,1,0,0,0
user_role_line_read,user_role_line_read_admin,base_user_role.model_res_users_role_line,group_admin,1,0,0,0
ir_config_parameter_read,ir_config_parameter_read_admin,base.model_ir_config_parameter,group_admin,1,0,0,0
ir_actions_act_window_read,ir_actions_act_window_read_admin,base.model_ir_actions_act_window,group_admin,1,0,0,0
ir_actions_act_window_view_read,ir_actions_act_window_view_read_admin,base.model_ir_actions_act_window_view,group_admin,1,0,0,0
res_company_modify,res_company_modify_admin,model_res_company,group_admin,1,1,1,0
res_users_admin,access_res_users_admin,model_res_users,energy_communities.group_admin,1,1,1,0
res_users_role_admin,access_res_users_role_admin,model_res_users_role,energy_communities.group_admin,1,1,1,0
res_users_role_line_admin,access_res_users_role_line_admin,model_res_users_role_line,energy_communities.group_admin,1,1,1,0
auth_oauth_provider_public,access_auth_oauth_provider_public,model_auth_oauth_provider,energy_communities.group_admin,1,0,0,0
......@@ -172,7 +172,8 @@
(4, ref('account_payment_order.group_account_payment')),
(4, ref('crm.group_use_lead')),
(4, ref('mass_mailing.group_mass_mailing_user')),
(4, ref('l10n_es_aeat.group_account_aeat'))
(4, ref('l10n_es_aeat.group_account_aeat')),
(4, ref('queue_job.group_queue_job_manager')),
]"
/>
</record>
......
......@@ -28,6 +28,7 @@ class CRMLeadService(Component):
if target_source_xml_id:
# setup utm source on crm lead
crm_lead_id = crm_lead.get("id", False)
self._set_name(crm_lead_id, params)
self._setup_lead_utm_source(crm_lead_id, target_source_xml_id)
# select autoresponder notification id based on utm source
......@@ -68,6 +69,39 @@ class CRMLeadService(Component):
lang = data["value"]
return lang
def _get_ce_name(self, params):
metadata = params["metadata"]
for data in metadata:
if data["key"] == "ce_name":
ce_name = data["value"]
return ce_name
def _set_name(self, lead_id, params):
source_xml_id = self._get_source_xml_id(params)
lead = self.env["crm.lead"].browse(lead_id)
email = params["email_from"]
prefix = None
if source_xml_id == "ce_source_existing_ce_contact":
prefix = _("[Contact CE]")
elif source_xml_id == "ce_source_existing_ce_info":
prefix = _("[Newsletter CE]")
elif source_xml_id == "ce_source_general_contact":
prefix = _("[Contact SomComunitats]")
elif source_xml_id == "ce_source_general_info":
prefix = _("[Newsletter SomComunitats]")
if source_xml_id == "ce_source_creation_ce_proposal":
prefix = _("[Subscription CE]")
ce_name = self._get_ce_name(params)
name = "{prefix} {lead_id} {ce_name}".format(
prefix=prefix, lead_id=lead_id, ce_name=ce_name
)
else:
name = "{prefix} {lead_id} {email}".format(
prefix=prefix, lead_id=lead_id, email=email
)
if lead:
lead.write({"name": name})
def _get_autoresponder_email_template(self, source_xml_id):
template_external_id = None
if source_xml_id == "ce_source_creation_ce_proposal":
......@@ -82,4 +116,6 @@ class CRMLeadService(Component):
)
elif source_xml_id == "ce_source_general_info":
template_external_id = "email_templ_lead_request_platform_news_confirm_id"
elif source_xml_id == "ce_source_general_contact":
template_external_id == "email_templ_contact_platform_confirm_id"
return template_external_id
......@@ -216,7 +216,6 @@ S_LANDING_PAGE_CREATE = {
"type": "list",
"schema": {"type": "dict", "schema": S_COMMUNITY_SERVICE},
},
"group_image_link": {"type": "string"},
"primary_image_file": {"type": "string"},
"primary_image_file_write_date": {"type": "string"},
"secondary_image_file": {"type": "string"},
......@@ -225,7 +224,6 @@ S_LANDING_PAGE_CREATE = {
"long_description": {"type": "string"},
"why_become_cooperator": {"type": "string"},
"become_cooperator_process": {"type": "string"},
"map_geolocation": {"type": "string"},
"map_reference": {"type": "string"},
"street": {"type": "string"},
"postal_code": {"type": "string"},
......
from . import test_res_company
from . import test_res_users
from . import test_assign_admin_wizard
from . import test_crm_lead
from . import test_multicompany_easy_creation
from faker import Faker
faker = Faker(locale="es_ES")
class CompanySetupMixin:
def create_company(self, name, hierarchy_level, parent_id):
return self.company_model.create(
{
"name": name,
"hierarchy_level": hierarchy_level,
"parent_id": parent_id,
}
)
class UserSetupMixin:
def create_user(self, firstname, lastname, vat=False, email=False):
login = vat if vat else faker.vat_id()
return self.users_model.create(
{
"login": login.upper(),
"firstname": firstname,
"lastname": lastname,
"email": email if email else faker.email(),
}
)
def make_community_admin(self, community_admin):
community_admin.write({"company_ids": [(4, self.community.id)]})
self.admin_role = self.env["res.users.role"].search(
[("code", "=", "role_ce_admin")]
)
self.role_line_model.create(
{
"user_id": community_admin.id,
"active": True,
"role_id": self.admin_role.id,
"company_id": self.community.id,
}
)
self.internal_role = self.env["res.users.role"].search(
[("code", "=", "role_internal_user")]
)
self.role_line_model.create(
{
"user_id": community_admin.id,
"active": True,
"role_id": self.internal_role.id,
}
)
def make_coord_admin(self, coord_admin):
coord_admin.write({"company_ids": [(4, self.coordination.id)]})
coord_admin.write({"company_ids": [(4, self.community.id)]})
coord_admin_role = self.env["res.users.role"].search(
[("code", "=", "role_coord_admin")]
)
self.role_line_model.create(
{
"user_id": coord_admin.id,
"active": True,
"role_id": coord_admin_role.id,
"company_id": self.community.id,
}
)
self.ce_manager_role = self.env["res.users.role"].search(
[("code", "=", "role_ce_manager")]
)
self.role_line_model.create(
{
"user_id": coord_admin.id,
"active": True,
"role_id": self.ce_manager_role.id,
"company_id": self.community.id,
}
)
self.internal_role = self.env["res.users.role"].search(
[("code", "=", "role_internal_user")]
)
self.role_line_model.create(
{
"user_id": coord_admin.id,
"active": True,
"role_id": self.internal_role.id,
}
)
from unittest.mock import patch
from faker import Faker
from odoo.exceptions import ValidationError
from odoo.tests import common
from .helpers import UserSetupMixin
faker = Faker(locale="es_ES")
class TestAssingAdminWizard(UserSetupMixin, common.TransactionCase):
def setUp(self):
super().setUp()
self.users_model = self.env["res.users"]
self.company_model = self.env["res.company"]
self.random_user = self.create_user("Brandom", "Random", "43217763G")
@patch(
"odoo.addons.energy_communities.models.res_users.ResUsers.add_energy_community_role"
)
def test__process_data__search_user_case_insensitive(
self, add_energy_community_role_mocked
):
wizard = self.env["assign.admin.wizard"].create(
{
"is_new_admin": False,
"vat": "43217763g",
}
)
wizard.process_data()
add_energy_community_role_mocked.assert_called_once()
def test__process_data__user_not_found(self):
# When we search a new user
wizard = self.env["assign.admin.wizard"].create(
{
"is_new_admin": False,
"vat": "43217761g",
}
)
# Raises User not found error
with self.assertRaises(ValidationError):
wizard.process_data()
from datetime import datetime
from faker import Faker
from mock import patch
from odoo.tests import common
from odoo.exceptions import ValidationError
from .helpers import UserSetupMixin, CompanySetupMixin
faker = Faker(locale='es_ES')
class TestCRMLead(UserSetupMixin, CompanySetupMixin, common.TransactionCase):
def create_crm_lead(self):
return self.env["crm.lead"].create({
"name": "CE Name",
"email_from": faker.email(),
"submission_type": "place_submission",
"company_id": self.coordination.id,
"source_id": self.env.ref("energy_communities.ce_source_creation_ce_proposal").id,
"metadata_line_ids": [
(0, 0, {"key": "current_lang", "value": "en_US"}),
(0, 0, {"key": "ce_creation_date", "value": "1994-09-01"}),
(0, 0, {"key": "ce_address", "value": "Av St Narcís"}),
(0, 0, {"key": "ce_city", "value": "Girona"}),
(0, 0, {"key": "ce_zip", "value": "17005"}),
(0, 0, {"key": "contact_phone", "value": "666666666"}),
(0, 0, {"key": "email_from", "value": "random@somcomunitats.coop"}),
(0, 0, {"key": "ce_vat", "value": "38948723V"}),
]
})
def setUp(self):
super().setUp()
self.users_model = self.env['res.users']
self.company_model = self.env['res.company']
self.role_line_model = self.env["res.users.role.line"]
self.instance = self.company_model.search([
('hierarchy_level', '=', 'instance')
])[0]
self.coordination = self.create_company(
'Coordinator', 'coordinator', self.instance.id,
)
self.coord_admin = self.create_user("Coord", "Admin")
self.make_coord_user(self.coordination, self.coord_admin)
self.crm_lead = self.create_crm_lead()
def test__get_default_community_wizard__base_case(self):
result = self.crm_lead._get_default_community_wizard()
lang = self.env["res.lang"].search([("code", "=", "en_US")])
foundation_date = datetime.strptime('1994-09-01', '%Y-%m-%d')
users = self.crm_lead.company_id.get_users()
self.assertEquals(result, {
'name': self.crm_lead.name,
'city': 'Girona',
'default_lang_id': lang.id,
'email': 'random@somcomunitats.coop',
'foundation_date': foundation_date,
'hierarchy_level': 'community',
'parent_id': self.coordination.id,
'crm_lead_id': self.crm_lead.id,
'phone': '666666666',
'street': 'Av St Narcís',
'vat': '38948723V',
'zip_code': '17005',
'user_ids': [user.id for user in users],
'chart_template_id': self.env["account.chart.template"].search([("name", "=", "PGCE PYMEs 2008")])[0].id,
'update_default_taxes': True,
'default_sale_tax_id': self.env["account.tax.template"].search([("name", "=", "IVA 21% (Servicios)")])[0].id,
'default_purchase_tax_id': self.env["account.tax.template"].search([("name", "=", "21% IVA soportado (bienes corrientes)")])[0].id,
'property_cooperator_account': self.env.ref("l10n_es.account_common_4400").id,
'create_user': False,
})
from faker import Faker
from mock import patch
from odoo.tests import common
from odoo.exceptions import ValidationError
from .helpers import UserSetupMixin, CompanySetupMixin
faker = Faker(locale='es_ES')
class TestMultiEasyCreation(UserSetupMixin, CompanySetupMixin, common.TransactionCase):
def create_crm_lead(self):
return self.env["crm.lead"].create({
"name": "CE Name",
"email_from": faker.email(),
"submission_type": "place_submission",
"company_id": self.coordination.id,
"source_id": self.env.ref("energy_communities.ce_source_creation_ce_proposal").id,
"metadata_line_ids": [
(0, 0, {"key": "current_lang", "value": "ca"}),
(0, 0, {"key": "ce_creation_date", "value": "1994-09-01"}),
(0, 0, {"key": "ce_address", "value": "Av St Narcís"}),
(0, 0, {"key": "ce_city", "value": "Girona"}),
(0, 0, {"key": "ce_zip", "value": "17005"}),
(0, 0, {"key": "contact_phone", "value": "666666666"}),
(0, 0, {"key": "email_from", "value": "random@somcomunitats.coop"}),
(0, 0, {"key": "ce_vat", "value": "38948723V"}),
]
})
def setUp(self):
super().setUp()
self.users_model = self.env['res.users']
self.company_model = self.env['res.company']
self.role_line_model = self.env["res.users.role.line"]
self.instance = self.company_model.search([
('hierarchy_level', '=', 'instance')
])[0]
self.coordination = self.create_company(
'Coordinator', 'coordinator', self.instance.id,
)
self.coord_admin = self.create_user("Coord", "Admin")
self.make_coord_user(self.coordination, self.coord_admin)
self.crm_lead = self.create_crm_lead()
self.wizard = self.env["account.multicompany.easy.creation.wiz"].create(
self.crm_lead.get_default_community_wizard()
)
def test__action_accept__company_created(self):
self.wizard.action_accept()
companies = self.env["res.company"].search([("parent_id", "=", self.coordination.id)])
self.assertEquals(len(companies), 1)
company = companies[0]
self.assertEquals(company.hierarchy_level, "community")
@patch("odoo.addons.energy_communities.models.res_users.ResUsers.make_ce_user")
def test__add_company_managers(self, make_ce_user_mocked):
new_company = self.create_company(
"Community", "community", self.coordination.id
)
self.wizard.new_company_id = new_company
self.wizard.add_company_managers()
make_ce_user_mocked.assert_called_with(new_company, "role_ce_manager")
def test__add_company_log(self):
new_company = self.create_company(
"Community", "community", self.coordination.id
)
self.wizard.new_company_id = new_company
self.wizard.add_company_log()
self.assertTrue(new_company.message_ids)
expected_msg = '<p>Community created from: <a href="/web#id={}&amp;view_type=form&amp;model=crm.lead&amp;menu_id={}">{}</a></p>'.format(
self.crm_lead.id, self.env.ref("crm.crm_menu_root").id, self.crm_lead.name
)
self.assertEquals(expected_msg, new_company.message_ids[0].body)
This diff is collapsed.
......@@ -33,6 +33,16 @@
name="admin_user_endpoint"
attrs="{'invisible': ['|', ('is_admin_provider','=',False),
('is_keycloak_provider', '=', False)]}"
/>
<field
name="redirect_admin_url"
attrs="{'invisible': ['|', ('is_admin_provider','=',False),
('is_keycloak_provider', '=', False)]}"
/>
<field
name="reset_password_endpoint"
attrs="{'invisible': ['|', ('is_admin_provider','=',False),
('is_keycloak_provider', '=', False)]}"
/>
</xpath>
</field>
......
......@@ -78,4 +78,12 @@
eval="[('source_id','=', ref('ce_source_creation_ce_proposal'))]"
/>
</record>
<record id="action_open_assign_admin_wizard" model="ir.actions.act_window">
<field name="name">Create User</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">assign.admin.wizard</field>
<field name="view_mode">form</field>
<field name="target">new</field>
</record>
</odoo>