From 3391a699a137ee89c38c97261171c888629b2e5f Mon Sep 17 00:00:00 2001 From: Anjeel Haria Date: Wed, 9 Oct 2024 18:55:08 +0530 Subject: [PATCH] [Add] Added modules membership_group and mass_mailing_membership_group modules --- mass_mailing_membership_group/__init__.py | 1 + mass_mailing_membership_group/__manifest__.py | 28 ++++++ .../data/mailing_contact_demo.xml | 27 ++++++ .../data/mailing_list_demo.xml | 22 +++++ .../data/membership_group_demo.xml | 22 +++++ .../data/res_partner_demo.xml | 34 +++++++ .../models/__init__.py | 3 + .../models/mailing_list.py | 7 ++ .../models/membership_group.py | 7 ++ .../models/membership_group_member.py | 53 +++++++++++ .../views/membership_group_member_view.xml | 25 +++++ .../views/membership_group_view.xml | 29 ++++++ .../views/res_partner_view.xml | 14 +++ membership_group/__init__.py | 1 + membership_group/__manifest__.py | 25 +++++ .../data/membership_group_demo.xml | 22 +++++ membership_group/data/res_partner_demo.xml | 40 ++++++++ membership_group/menuitems.xml | 27 ++++++ membership_group/models/__init__.py | 3 + membership_group/models/membership_group.py | 47 ++++++++++ .../models/membership_group_member.py | 9 ++ membership_group/models/res_partner.py | 43 +++++++++ membership_group/readme/CONTRIBUTORS.rst | 1 + membership_group/readme/DESCRIPTION.rst | 5 + membership_group/readme/USAGE.rst | 5 + membership_group/security/ir.model.access.csv | 7 ++ membership_group/tests/__init__.py | 1 + .../tests/test_membership_group.py | 86 +++++++++++++++++ .../views/membership_group_member_view.xml | 33 +++++++ .../views/membership_group_view.xml | 93 +++++++++++++++++++ membership_group/views/res_partner_view.xml | 33 +++++++ 31 files changed, 753 insertions(+) create mode 100644 mass_mailing_membership_group/__init__.py create mode 100644 mass_mailing_membership_group/__manifest__.py create mode 100644 mass_mailing_membership_group/data/mailing_contact_demo.xml create mode 100644 mass_mailing_membership_group/data/mailing_list_demo.xml create mode 100644 mass_mailing_membership_group/data/membership_group_demo.xml create mode 100644 mass_mailing_membership_group/data/res_partner_demo.xml create mode 100644 mass_mailing_membership_group/models/__init__.py create mode 100644 mass_mailing_membership_group/models/mailing_list.py create mode 100644 mass_mailing_membership_group/models/membership_group.py create mode 100644 mass_mailing_membership_group/models/membership_group_member.py create mode 100644 mass_mailing_membership_group/views/membership_group_member_view.xml create mode 100644 mass_mailing_membership_group/views/membership_group_view.xml create mode 100644 mass_mailing_membership_group/views/res_partner_view.xml create mode 100644 membership_group/__init__.py create mode 100644 membership_group/__manifest__.py create mode 100644 membership_group/data/membership_group_demo.xml create mode 100644 membership_group/data/res_partner_demo.xml create mode 100644 membership_group/menuitems.xml create mode 100644 membership_group/models/__init__.py create mode 100644 membership_group/models/membership_group.py create mode 100644 membership_group/models/membership_group_member.py create mode 100644 membership_group/models/res_partner.py create mode 100644 membership_group/readme/CONTRIBUTORS.rst create mode 100644 membership_group/readme/DESCRIPTION.rst create mode 100644 membership_group/readme/USAGE.rst create mode 100644 membership_group/security/ir.model.access.csv create mode 100644 membership_group/tests/__init__.py create mode 100644 membership_group/tests/test_membership_group.py create mode 100644 membership_group/views/membership_group_member_view.xml create mode 100644 membership_group/views/membership_group_view.xml create mode 100644 membership_group/views/res_partner_view.xml diff --git a/mass_mailing_membership_group/__init__.py b/mass_mailing_membership_group/__init__.py new file mode 100644 index 0000000..0650744 --- /dev/null +++ b/mass_mailing_membership_group/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/mass_mailing_membership_group/__manifest__.py b/mass_mailing_membership_group/__manifest__.py new file mode 100644 index 0000000..224035d --- /dev/null +++ b/mass_mailing_membership_group/__manifest__.py @@ -0,0 +1,28 @@ +# Copyright 2022 Onestein () +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +{ + "name": "Mass Mailing Membership Group", + "category": "Membership", + "version": "16.0.1.0.0", + "author": "Onestein", + "license": "AGPL-3", + "website": "https://www.onestein.nl", + "depends": [ + "mass_mailing", + "mass_mailing_partner", + "membership", + "membership_group", + ], + "data": [ + "views/membership_group_view.xml", + "views/membership_group_member_view.xml", + "views/res_partner_view.xml", + ], + "demo": [ + "data/mailing_list_demo.xml", + "data/membership_group_demo.xml", + "data/mailing_contact_demo.xml", + "data/res_partner_demo.xml", + ], +} diff --git a/mass_mailing_membership_group/data/mailing_contact_demo.xml b/mass_mailing_membership_group/data/mailing_contact_demo.xml new file mode 100644 index 0000000..5fd83be --- /dev/null +++ b/mass_mailing_membership_group/data/mailing_contact_demo.xml @@ -0,0 +1,27 @@ + + + + + + John Doe + demomember1@example.com + + + + + + Jane Smith + demomember2@example.com + + + + + + Alice Johnson + demomember3@example.com + + + + + + diff --git a/mass_mailing_membership_group/data/mailing_list_demo.xml b/mass_mailing_membership_group/data/mailing_list_demo.xml new file mode 100644 index 0000000..9e44e01 --- /dev/null +++ b/mass_mailing_membership_group/data/mailing_list_demo.xml @@ -0,0 +1,22 @@ + + + + + + Executive Board + + + + Marketing Team + + + + Finance Committee + + + + Youth Club + + + + diff --git a/mass_mailing_membership_group/data/membership_group_demo.xml b/mass_mailing_membership_group/data/membership_group_demo.xml new file mode 100644 index 0000000..cce68b3 --- /dev/null +++ b/mass_mailing_membership_group/data/membership_group_demo.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/mass_mailing_membership_group/data/res_partner_demo.xml b/mass_mailing_membership_group/data/res_partner_demo.xml new file mode 100644 index 0000000..2218eb3 --- /dev/null +++ b/mass_mailing_membership_group/data/res_partner_demo.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + diff --git a/mass_mailing_membership_group/models/__init__.py b/mass_mailing_membership_group/models/__init__.py new file mode 100644 index 0000000..6915eac --- /dev/null +++ b/mass_mailing_membership_group/models/__init__.py @@ -0,0 +1,3 @@ +from . import membership_group +from . import membership_group_member +from . import mailing_list diff --git a/mass_mailing_membership_group/models/mailing_list.py b/mass_mailing_membership_group/models/mailing_list.py new file mode 100644 index 0000000..0d17daf --- /dev/null +++ b/mass_mailing_membership_group/models/mailing_list.py @@ -0,0 +1,7 @@ +from odoo import fields, models + + +class MailingList(models.Model): + _inherit = "mailing.list" + + membership_group_ids = fields.One2many("membership.group", "mailing_list_id") diff --git a/mass_mailing_membership_group/models/membership_group.py b/mass_mailing_membership_group/models/membership_group.py new file mode 100644 index 0000000..2325447 --- /dev/null +++ b/mass_mailing_membership_group/models/membership_group.py @@ -0,0 +1,7 @@ +from odoo import fields, models + + +class MembershipGroup(models.Model): + _inherit = "membership.group" + + mailing_list_id = fields.Many2one("mailing.list") diff --git a/mass_mailing_membership_group/models/membership_group_member.py b/mass_mailing_membership_group/models/membership_group_member.py new file mode 100644 index 0000000..f4dd817 --- /dev/null +++ b/mass_mailing_membership_group/models/membership_group_member.py @@ -0,0 +1,53 @@ +from odoo import api, fields, models + + +class MembershipGroupMember(models.Model): + _inherit = "membership.group.member" + + on_mailing_list = fields.Boolean( + compute="_compute_on_mailing_list", + inverse="_inverse_on_mailing_list", + store=True, + readonly=False, + ) + + @api.depends( + "group_id", + "group_id.mailing_list_id", + "partner_id", + "partner_id.mass_mailing_contact_ids", + ) + def _compute_on_mailing_list(self): + for membership in self: + if ( + not membership.group_id + or not membership.group_id.mailing_list_id + or not membership.partner_id + ): + membership.on_mailing_list = False + else: + related_lists = membership.partner_id.mass_mailing_contact_ids.mapped( + "subscription_list_ids" + ).mapped("list_id") + membership.on_mailing_list = ( + membership.group_id.mailing_list_id in related_lists + ) + + def _inverse_on_mailing_list(self): + mailing_contact_obj = self.env["mailing.contact"] + for membership in self: + related_contacts = mailing_contact_obj.search( + [("partner_id", "=", membership.partner_id.id)] + ) + if membership.on_mailing_list: + if not related_contacts: + related_contacts = mailing_contact_obj.create( + {"email": membership.partner_id.email} + ) + membership.group_id.mailing_list_id.write( + {"contact_ids": [(4, related_contacts[0].id)]} + ) + elif related_contacts: + membership.group_id.mailing_list_id.write( + {"contact_ids": [(3, c.id) for c in related_contacts]} + ) diff --git a/mass_mailing_membership_group/views/membership_group_member_view.xml b/mass_mailing_membership_group/views/membership_group_member_view.xml new file mode 100644 index 0000000..45e7909 --- /dev/null +++ b/mass_mailing_membership_group/views/membership_group_member_view.xml @@ -0,0 +1,25 @@ + + + + + membership.group.member + + + + + + + + + + membership.group.member + + + + + + + + + + diff --git a/mass_mailing_membership_group/views/membership_group_view.xml b/mass_mailing_membership_group/views/membership_group_view.xml new file mode 100644 index 0000000..0085809 --- /dev/null +++ b/mass_mailing_membership_group/views/membership_group_view.xml @@ -0,0 +1,29 @@ + + + + + membership.group + + + + + + + + + + membership.group + + + + + + + + + + + + + + diff --git a/mass_mailing_membership_group/views/res_partner_view.xml b/mass_mailing_membership_group/views/res_partner_view.xml new file mode 100644 index 0000000..c7b41f2 --- /dev/null +++ b/mass_mailing_membership_group/views/res_partner_view.xml @@ -0,0 +1,14 @@ + + + + + + res.partner + + + + + + + + diff --git a/membership_group/__init__.py b/membership_group/__init__.py new file mode 100644 index 0000000..0650744 --- /dev/null +++ b/membership_group/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/membership_group/__manifest__.py b/membership_group/__manifest__.py new file mode 100644 index 0000000..45ffcd3 --- /dev/null +++ b/membership_group/__manifest__.py @@ -0,0 +1,25 @@ +# Copyright 2023 Onestein () +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +{ + "name": "Membership Group", + "category": "Membership", + "version": "16.0.1.0.0", + "author": "Onestein, Odoo Community Association (OCA)", + "license": "AGPL-3", + "website": "https://www.onestein.nl", + "depends": [ + "contacts", + "membership", + ], + "data": [ + "security/ir.model.access.csv", + "views/membership_group_member_view.xml", + "views/membership_group_view.xml", + "views/res_partner_view.xml", + "menuitems.xml", + ], + "demo": [ + "data/membership_group_demo.xml", + "data/res_partner_demo.xml", + ], +} diff --git a/membership_group/data/membership_group_demo.xml b/membership_group/data/membership_group_demo.xml new file mode 100644 index 0000000..191841f --- /dev/null +++ b/membership_group/data/membership_group_demo.xml @@ -0,0 +1,22 @@ + + + + + + Executive Board + + + + Marketing Team + + + + Finance Committee + + + + Youth Club + + + + diff --git a/membership_group/data/res_partner_demo.xml b/membership_group/data/res_partner_demo.xml new file mode 100644 index 0000000..0f8dbb0 --- /dev/null +++ b/membership_group/data/res_partner_demo.xml @@ -0,0 +1,40 @@ + + + + + + John Doe + + + + + + Jane Smith + + + + + + Alice Johnson + + + + + + diff --git a/membership_group/menuitems.xml b/membership_group/menuitems.xml new file mode 100644 index 0000000..d283672 --- /dev/null +++ b/membership_group/menuitems.xml @@ -0,0 +1,27 @@ + + + + + + + + + + diff --git a/membership_group/models/__init__.py b/membership_group/models/__init__.py new file mode 100644 index 0000000..4c65b0a --- /dev/null +++ b/membership_group/models/__init__.py @@ -0,0 +1,3 @@ +from . import membership_group +from . import membership_group_member +from . import res_partner diff --git a/membership_group/models/membership_group.py b/membership_group/models/membership_group.py new file mode 100644 index 0000000..b3d9d88 --- /dev/null +++ b/membership_group/models/membership_group.py @@ -0,0 +1,47 @@ +from odoo import api, fields, models + + +class MembershipGroup(models.Model): + _name = "membership.group" + _description = "Membership Group" + _parent_store = True + _parent_name = "parent_id" + + name = fields.Char() + membership_group_member_ids = fields.One2many("membership.group.member", "group_id") + parent_id = fields.Many2one( + comodel_name="membership.group", string="Parent Membership Group", index=True + ) + child_ids = fields.One2many( + comodel_name="membership.group", + inverse_name="parent_id", + string="Sub Membership Groups", + ) + parent_path = fields.Char(index=True, unaccent=False) + partner_ids = fields.Many2many( + "res.partner", string="Contacts", compute="_compute_partner_ids" + ) + partner_ids_count = fields.Integer("# of Members", compute="_compute_partner_ids") + + @api.depends( + "membership_group_member_ids", "membership_group_member_ids.partner_id" + ) + def _compute_partner_ids(self): + for group in self: + group.partner_ids = group.membership_group_member_ids.mapped("partner_id") + group.partner_ids_count = len(group.partner_ids) + + def action_open_partner_view(self): + action_name = "membership.action_membership_members" + action_vals = self.env["ir.actions.act_window"]._for_xml_id(action_name) + action_vals["context"] = {} + record_ids = self.mapped("partner_ids").ids + if len(record_ids) > 1: + action_vals["domain"] = ( + "[('id','in',[" + ",".join(map(str, record_ids)) + "])]" + ) + elif len(record_ids) == 1: + res = self.env.ref("base.view_partner_form", False) + action_vals["views"] = [(res and res.id or False, "form")] + action_vals["res_id"] = record_ids[0] + return action_vals diff --git a/membership_group/models/membership_group_member.py b/membership_group/models/membership_group_member.py new file mode 100644 index 0000000..1d8e766 --- /dev/null +++ b/membership_group/models/membership_group_member.py @@ -0,0 +1,9 @@ +from odoo import fields, models + + +class MembershipGroupMember(models.Model): + _name = "membership.group.member" + _description = "Membership Group Member" + + partner_id = fields.Many2one("res.partner", required=True, ondelete="cascade") + group_id = fields.Many2one("membership.group", required=True, ondelete="cascade") diff --git a/membership_group/models/res_partner.py b/membership_group/models/res_partner.py new file mode 100644 index 0000000..f1fd8eb --- /dev/null +++ b/membership_group/models/res_partner.py @@ -0,0 +1,43 @@ +from odoo import api, fields, models + + +class ResPartner(models.Model): + _inherit = "res.partner" + + membership_group_member_ids = fields.One2many( + "membership.group.member", "partner_id" + ) + membership_group_ids = fields.Many2many( + "membership.group", + string="Membership Groups", + compute="_compute_membership_group_ids", + store=True, + ) + membership_group_ids_count = fields.Integer( + string="# of Groups", compute="_compute_membership_group_ids", store=True + ) + + @api.depends("membership_group_member_ids", "membership_group_member_ids.group_id") + def _compute_membership_group_ids(self): + for partner in self: + partner.membership_group_ids = partner.membership_group_member_ids.mapped( + "group_id" + ) + partner.membership_group_ids_count = len(partner.membership_group_ids) + + def action_open_membership_group_view(self): + action_name = "membership_group.membership_group_action" + action_vals = self.env["ir.actions.act_window"]._for_xml_id(action_name) + + action_vals["context"] = {} + record_ids = self.mapped("membership_group_ids").ids + # choose the view_mode accordingly + if len(record_ids) > 1: + action_vals["domain"] = ( + "[('id','in',[" + ",".join(map(str, record_ids)) + "])]" + ) + elif len(record_ids) == 1: + res = self.env.ref("membership_group.membership_group_view_form", False) + action_vals["views"] = [(res and res.id or False, "form")] + action_vals["res_id"] = record_ids[0] + return action_vals diff --git a/membership_group/readme/CONTRIBUTORS.rst b/membership_group/readme/CONTRIBUTORS.rst new file mode 100644 index 0000000..85125ab --- /dev/null +++ b/membership_group/readme/CONTRIBUTORS.rst @@ -0,0 +1 @@ +* `Onestein `_ diff --git a/membership_group/readme/DESCRIPTION.rst b/membership_group/readme/DESCRIPTION.rst new file mode 100644 index 0000000..847b151 --- /dev/null +++ b/membership_group/readme/DESCRIPTION.rst @@ -0,0 +1,5 @@ +This module introduces groups in the Membership functional area. +Groups are for Organizations what Departments are for Companies, as they +divide the activities of the organization based on their functions. + +Usually, members of an organization adhere to one or more groups. diff --git a/membership_group/readme/USAGE.rst b/membership_group/readme/USAGE.rst new file mode 100644 index 0000000..c0fafe0 --- /dev/null +++ b/membership_group/readme/USAGE.rst @@ -0,0 +1,5 @@ +To create or edit a group: + +* Go to Members / Configuration / Groups +* Create a new record or select an existing one +* In the Membership tab, you can add the members who adhere to it. diff --git a/membership_group/security/ir.model.access.csv b/membership_group/security/ir.model.access.csv new file mode 100644 index 0000000..9c90d41 --- /dev/null +++ b/membership_group/security/ir.model.access.csv @@ -0,0 +1,7 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_membership_group_public,access_membership_group_public,model_membership_group,base.group_public,1,0,0,0 +access_membership_group_portal,access_membership_group_portal,model_membership_group,base.group_portal,1,0,0,0 +access_membership_group,access_membership_group,model_membership_group,base.group_user,1,1,1,1 +access_membership_group_member_public,access_membership_group_member_public,model_membership_group_member,base.group_public,1,0,0,0 +access_membership_group_member_portal,access_membership_group_member_portal,model_membership_group_member,base.group_portal,1,0,0,0 +access_membership_group_member,access_membership_group_member,model_membership_group_member,base.group_user,1,1,1,1 diff --git a/membership_group/tests/__init__.py b/membership_group/tests/__init__.py new file mode 100644 index 0000000..a422cae --- /dev/null +++ b/membership_group/tests/__init__.py @@ -0,0 +1 @@ +from . import test_membership_group diff --git a/membership_group/tests/test_membership_group.py b/membership_group/tests/test_membership_group.py new file mode 100644 index 0000000..e951568 --- /dev/null +++ b/membership_group/tests/test_membership_group.py @@ -0,0 +1,86 @@ +from odoo.tests import common + + +class TestMembershipGroup(common.TransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + membership_group_obj = cls.env["membership.group"] + res_partner_obj = cls.env["res.partner"] + membership_group_member_obj = cls.env["membership.group.member"] + + cls.group_1 = membership_group_obj.create({"name": "Test Group 1"}) + cls.group_2 = membership_group_obj.create({"name": "Test Group 2"}) + cls.partner_1 = res_partner_obj.create({"name": "Test partner 1"}) + cls.partner_2 = res_partner_obj.create({"name": "Test partner 2"}) + + cls.membership_1a = membership_group_member_obj.create( + { + "partner_id": cls.partner_1.id, + "group_id": cls.group_1.id, + } + ) + cls.membership_1b = membership_group_member_obj.create( + { + "partner_id": cls.partner_2.id, + "group_id": cls.group_1.id, + } + ) + cls.membership_2a = membership_group_member_obj.create( + { + "partner_id": cls.partner_1.id, + "group_id": cls.group_2.id, + } + ) + + def test_01_membership_group_computed_fields(self): + self.assertListEqual( + self.group_1.partner_ids.ids, [self.partner_1.id, self.partner_2.id] + ) + self.assertListEqual(self.group_2.partner_ids.ids, [self.partner_1.id]) + self.assertEqual(self.group_1.partner_ids_count, 2) + self.assertEqual(self.group_2.partner_ids_count, 1) + + def test_02_partner_computed_fields(self): + self.assertListEqual( + self.partner_1.membership_group_ids.ids, [self.group_1.id, self.group_2.id] + ) + self.assertListEqual(self.partner_2.membership_group_ids.ids, [self.group_1.id]) + self.assertEqual(self.partner_1.membership_group_ids_count, 2) + self.assertEqual(self.partner_2.membership_group_ids_count, 1) + + def test_03_action_open_partner_view(self): + res = self.group_1.action_open_partner_view() + self.assertEqual(res["xml_id"], "membership.action_membership_members") + self.assertEqual( + res["domain"], + "[('id','in',[" + ",".join(map(str, self.group_1.partner_ids.ids)) + "])]", + ) + + res = self.group_2.action_open_partner_view() + self.assertEqual( + res["views"], [(self.env.ref("base.view_partner_form").id, "form")] + ) + self.assertEqual(res["res_id"], self.partner_1.id) + + def test_04_action_open_membership_group_view(self): + res = self.partner_1.action_open_membership_group_view() + self.assertEqual(res["xml_id"], "membership_group.membership_group_action") + self.assertEqual( + res["domain"], + "[('id','in',[" + + ",".join(map(str, self.partner_1.membership_group_ids.ids)) + + "])]", + ) + + res = self.partner_2.action_open_membership_group_view() + self.assertEqual( + res["views"], + [ + ( + self.env.ref("membership_group.membership_group_view_form").id, + "form", + ) + ], + ) + self.assertEqual(res["res_id"], self.group_1.id) diff --git a/membership_group/views/membership_group_member_view.xml b/membership_group/views/membership_group_member_view.xml new file mode 100644 index 0000000..6528a2d --- /dev/null +++ b/membership_group/views/membership_group_member_view.xml @@ -0,0 +1,33 @@ + + + + + membership.group.member + + + + + + + + + + membership.group.member + + + + + + + + + + + + + + Membership Group Members + membership.group.member + tree + + diff --git a/membership_group/views/membership_group_view.xml b/membership_group/views/membership_group_view.xml new file mode 100644 index 0000000..3ee8b59 --- /dev/null +++ b/membership_group/views/membership_group_view.xml @@ -0,0 +1,93 @@ + + + + + membership.group + + + + + + + + + + membership.group + +
+ +
+ +
+ +
+

+ +

+
+ + + + + + + + + + + + + + + + + + + +
+
+
+
+ + + membership.group + + + + + + + + + Membership Groups + ir.actions.act_window + membership.group + + +

+ Create a Membership Group +

+
+
+ +
diff --git a/membership_group/views/res_partner_view.xml b/membership_group/views/res_partner_view.xml new file mode 100644 index 0000000..417f861 --- /dev/null +++ b/membership_group/views/res_partner_view.xml @@ -0,0 +1,33 @@ + + + + + + res.partner + + + + + + + + + + + + + + +