diff --git a/README.md b/README.md
index 4273c83e..c6874393 100644
--- a/README.md
+++ b/README.md
@@ -39,6 +39,8 @@ addon | version | maintainers | summary
[g2p_payment_interop_layer](g2p_payment_interop_layer/) | 17.0.0.0.0 | | OpenG2P Program Payment (Payment Interoperability Layer)
[g2p_payment_phee](g2p_payment_phee/) | 17.0.0.0.0 | | OpenG2P Program Payment (Payment Hub EE)
[g2p_payment_simple_mpesa](g2p_payment_simple_mpesa/) | 17.0.0.0.0 | | OpenG2P Program Payment: Simple Mpesa Payment Manager
+[g2p_pbms_socio_economic_model](g2p_pbms_socio_economic_model/) | 17.0.0.0.0 | | G2P PBMS Socio-Economic Model
+[g2p_pbms_student_model](g2p_pbms_student_model/) | 17.0.0.0.0 | | G2P PBMS Student Model
[g2p_program_approval](g2p_program_approval/) | 17.0.0.0.0 | | OpenG2P Program: Approval
[g2p_program_assessment](g2p_program_assessment/) | 17.0.0.0.0 | | OpenG2P Program: Assessment
[g2p_program_autoenrol](g2p_program_autoenrol/) | 17.0.0.0.0 | | OpenG2P Programs: Autoenrol
diff --git a/g2p_pbms_socio_economic_model/README.md b/g2p_pbms_socio_economic_model/README.md
new file mode 100644
index 00000000..e83fabee
--- /dev/null
+++ b/g2p_pbms_socio_economic_model/README.md
@@ -0,0 +1,3 @@
+# G2P PBMS Socio-Economic Model
+
+Refer to https://docs.openg2p.org.
diff --git a/g2p_pbms_socio_economic_model/__init__.py b/g2p_pbms_socio_economic_model/__init__.py
new file mode 100644
index 00000000..5d6d1635
--- /dev/null
+++ b/g2p_pbms_socio_economic_model/__init__.py
@@ -0,0 +1,3 @@
+# Part of OpenG2P. See LICENSE file for full copyright and licensing details.
+
+from . import models
diff --git a/g2p_pbms_socio_economic_model/__manifest__.py b/g2p_pbms_socio_economic_model/__manifest__.py
new file mode 100644
index 00000000..697ec32f
--- /dev/null
+++ b/g2p_pbms_socio_economic_model/__manifest__.py
@@ -0,0 +1,18 @@
+# Part of OpenG2P. See LICENSE file for full copyright and licensing details.
+{
+ "name": "G2P PBMS Socio-Economic Model",
+ "category": "G2P",
+ "version": "17.0.0.0.0",
+ "sequence": 1,
+ "author": "OpenG2P",
+ "website": "https://openg2p.org",
+ "license": "LGPL-3",
+ "depends": ["g2p_registry_individual", "g2p_registry_group", "g2p_registry_membership", "g2p_programs"],
+ "data": ["views/group_view.xml", "views/individual_view.xml", "views/menu_view.xml"],
+ "assets": {},
+ "demo": [],
+ "images": [],
+ "application": True,
+ "installable": True,
+ "auto_install": False,
+}
diff --git a/g2p_pbms_socio_economic_model/data/sr_pbms_import_jq_mapping.jq b/g2p_pbms_socio_economic_model/data/sr_pbms_import_jq_mapping.jq
new file mode 100644
index 00000000..cbb9c606
--- /dev/null
+++ b/g2p_pbms_socio_economic_model/data/sr_pbms_import_jq_mapping.jq
@@ -0,0 +1,68 @@
+{
+ "name": .name,
+ "is_group": .isGroup,
+ "address": .address,
+ "registration_date": .registrationDate,
+ "group_membership_ids": (
+ if .groupMembershipIds? then
+ [
+ .groupMembershipIds[] | {
+ "individual": {
+ "name": .individual.name,
+ "given_name": .individual.givenName,
+ "family_name": .individual.familyName,
+ "addl_name": .individual.addlName,
+ "email": .individual.email,
+ "address": .individual.address,
+ "registration_date": .individual.registrationDate,
+ "birth_place": .individual.birthPlace,
+ "birthdate": .individual.birthdate,
+ "create_date": .individual.createDate,
+ "write_date": .individual.writeDate,
+ "reg_ids": (
+ if .individual.regIds? then
+ [.individual.regIds[] | {id_type: {name:.idTypeAsStr}, value: .value}]
+ else
+ []
+ end
+ ),
+ "phone_number_ids": (
+ if .individual.phoneNumberIds? then
+ [.individual.phoneNumberIds[] | {
+ phone_no: .phoneNo,
+ phone_sanitized: .phoneSanitized,
+ date_collected: .dateCollected,
+ disabled: .disabled
+ }]
+ else
+ []
+ end
+ )
+ },
+ "kind": {name:.kind.name}
+ }
+ ]
+ else
+ []
+ end
+ ),
+ "reg_ids": (
+ if .regIds? then
+ [.regIds[] | {id_type: {name:.idTypeAsStr}, value: .value}]
+ else
+ []
+ end
+ ),
+ "phone_number_ids": (
+ if .phoneNumberIds? then
+ [.phoneNumberIds[] | {
+ phone_no: .phoneNo,
+ phone_sanitized: .phoneSanitized,
+ date_collected: .dateCollected,
+ disabled: .disabled
+ }]
+ else
+ []
+ end
+ )
+}
diff --git a/g2p_pbms_socio_economic_model/models/__init__.py b/g2p_pbms_socio_economic_model/models/__init__.py
new file mode 100644
index 00000000..a471c819
--- /dev/null
+++ b/g2p_pbms_socio_economic_model/models/__init__.py
@@ -0,0 +1,4 @@
+# Part of OpenG2P. See LICENSE file for full copyright and licensing details.
+
+from . import group_membership
+from . import res_partner
diff --git a/g2p_pbms_socio_economic_model/models/group_membership.py b/g2p_pbms_socio_economic_model/models/group_membership.py
new file mode 100644
index 00000000..504f0317
--- /dev/null
+++ b/g2p_pbms_socio_economic_model/models/group_membership.py
@@ -0,0 +1,24 @@
+# Part of OpenG2P Registry. See LICENSE file for full copyright and licensing details.
+
+import logging
+
+from odoo import fields, models
+
+_logger = logging.getLogger(__name__)
+
+
+class G2PGroupMembershipInherit(models.Model):
+ _inherit = "g2p.group.membership"
+
+ employment_status = fields.Selection(related="individual.employment_status")
+
+ def get_household_head(self, registrant):
+ if registrant.is_group:
+ for member in registrant.group_membership_ids:
+ if member.kind and "Head" in [kind.name for kind in member.kind]:
+ return member.individual
+
+ else:
+ for member in registrant.individual_membership_ids:
+ if member.kind and "Head" in [kind.name for kind in member.kind]:
+ return member.individual
diff --git a/g2p_pbms_socio_economic_model/models/res_partner.py b/g2p_pbms_socio_economic_model/models/res_partner.py
new file mode 100644
index 00000000..a3902179
--- /dev/null
+++ b/g2p_pbms_socio_economic_model/models/res_partner.py
@@ -0,0 +1,127 @@
+# Part of OpenG2P. See LICENSE file for full copyright and licensing details.
+import logging
+
+from odoo import api, fields, models
+
+_logger = logging.getLogger(__name__)
+
+
+class G2PResPartnerInherited(models.Model):
+ _inherit = "res.partner"
+
+ #######################################################
+ ##### Social Status Information #####
+ #######################################################
+
+ num_preg_lact_women = fields.Integer()
+ num_malnourished_children = fields.Integer()
+ num_disabled = fields.Integer()
+ type_of_disability = fields.Selection(
+ [
+ ("visual_impairment", "Visual Impairment"),
+ ("hearing_impairment", "Hearing Impairment"),
+ ("physical_disability", "Physical Disability"),
+ ("cognitive_disability", "Cognitive Disability"),
+ ]
+ )
+ caste_ethnic_group = fields.Selection(
+ [
+ ("bantu", "Bantu"),
+ ("nilotic", "Nilotic"),
+ ("afro_asian", "Afro-Asiatic"),
+ ("khoisan", "Khoisan"),
+ ("pygmy", "Pygmy"),
+ ("other", "Other"),
+ ],
+ )
+ belong_to_protected_groups = fields.Selection([("yes", "Yes"), ("no", "No")])
+ other_vulnerable_status = fields.Selection([("yes", "Yes"), ("no", "No")])
+
+ #######################################################
+ ##### Household Details #####
+ #######################################################
+
+ education_level = fields.Selection(
+ [
+ ("primary", "Primary"),
+ ("secondary", "Secondary"),
+ ("higher_secondary", "Higher Secondary"),
+ ("bachelors", "Bachelors"),
+ ("masters", "Masters"),
+ ]
+ )
+ employment_status = fields.Selection(
+ [
+ ("employed_full", "Employed - Full Time"),
+ ("employed_part", "Employed - Part Time"),
+ ("self_employed", "Self-employed"),
+ ("unemployed", "Unemployed"),
+ ]
+ )
+ marital_status = fields.Selection(
+ [("single", "Single"), ("married", "Married"), ("divorced", "divorced")]
+ )
+
+ #######################################################
+ ##### Economic Status Information #####
+ #######################################################
+
+ income_sources = fields.Selection(
+ [
+ ("agriculture", "Agriculture"),
+ ("business", "Business"),
+ ("mining", "Mining"),
+ ("manufacturing", "Manufacturing"),
+ ("construction", "Construction"),
+ ]
+ )
+ # Income field was of INT type and it has validation which will cause the errors.
+ annual_income = fields.Selection(
+ [("below_5000", "Below 5000"), ("5001_10000", "5001-10,000"), ("above_10000", "Above 10,000")],
+ )
+ owns_two_wheeler = fields.Selection([("yes", "Yes"), ("no", "No")])
+ owns_three_wheeler = fields.Selection([("yes", "Yes"), ("no", "No")])
+ owns_four_wheeler = fields.Selection([("yes", "Yes"), ("no", "No")])
+ owns_cart = fields.Selection([("yes", "Yes"), ("no", "No")])
+ land_ownership = fields.Selection([("yes", "Yes"), ("no", "No")])
+ type_of_land_owned = fields.Selection(
+ [
+ ("agricultural", "Agricultural Land"),
+ ("residential", "Residential Land"),
+ ("pastoral", "Pastoral Land"),
+ ("forest", "Forest Land"),
+ ("commercial", "Commercial Land"),
+ ("communal", "Communal Land"),
+ ("other", "Other"),
+ ]
+ )
+ land_size = fields.Float()
+ owns_house = fields.Selection([("yes", "Yes"), ("no", "No")])
+ owns_livestock = fields.Selection([("yes", "Yes"), ("no", "No")])
+
+ #######################################################
+ ##### Housing Condition Information #####
+ #######################################################
+
+ housing_type = fields.Selection(
+ [("permanent", "Permanent"), ("temporary", "Temporary")], "Type of Housing"
+ )
+ house_condition = fields.Selection([("mud", "Mud"), ("cement", "Cement")])
+ sanitation_condition = fields.Selection([("yes", "Yes"), ("no", "No")])
+ water_access = fields.Selection([("yes", "Yes"), ("no", "No")])
+ electricity_access = fields.Selection([("yes", "Yes"), ("no", "No")])
+
+ #######################################################
+ ##### Computed Fields #####
+ #######################################################
+
+ is_hh_unemployed = fields.Boolean(compute="_compute_hh_is_unemployed", store=True)
+
+ @api.constrains("group_membership_ids", "employment_status")
+ def _compute_hh_is_unemployed(self):
+ for rec in self:
+ group_head = self.env["g2p.group.membership"].get_household_head(rec)
+ if group_head:
+ rec.is_hh_unemployed = group_head.employment_status == "unemployed"
+ else:
+ rec.is_hh_unemployed = False
diff --git a/g2p_pbms_socio_economic_model/pyproject.toml b/g2p_pbms_socio_economic_model/pyproject.toml
new file mode 100644
index 00000000..4231d0cc
--- /dev/null
+++ b/g2p_pbms_socio_economic_model/pyproject.toml
@@ -0,0 +1,3 @@
+[build-system]
+requires = ["whool"]
+build-backend = "whool.buildapi"
diff --git a/g2p_pbms_socio_economic_model/static/description/icon.png b/g2p_pbms_socio_economic_model/static/description/icon.png
new file mode 100644
index 00000000..5ecb429e
Binary files /dev/null and b/g2p_pbms_socio_economic_model/static/description/icon.png differ
diff --git a/g2p_pbms_socio_economic_model/views/group_view.xml b/g2p_pbms_socio_economic_model/views/group_view.xml
new file mode 100644
index 00000000..fcce65ae
--- /dev/null
+++ b/g2p_pbms_socio_economic_model/views/group_view.xml
@@ -0,0 +1,41 @@
+
+
+
+
+ view_group_view_pbms_model
+ res.partner
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/g2p_pbms_socio_economic_model/views/individual_view.xml b/g2p_pbms_socio_economic_model/views/individual_view.xml
new file mode 100644
index 00000000..b59e1bfb
--- /dev/null
+++ b/g2p_pbms_socio_economic_model/views/individual_view.xml
@@ -0,0 +1,18 @@
+
+
+
+
+ view_individual_form_pbms_model
+ res.partner
+
+
+
+
+
+
+
+
+
+
diff --git a/g2p_pbms_socio_economic_model/views/menu_view.xml b/g2p_pbms_socio_economic_model/views/menu_view.xml
new file mode 100644
index 00000000..34fc0d6d
--- /dev/null
+++ b/g2p_pbms_socio_economic_model/views/menu_view.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
diff --git a/g2p_pbms_student_model/README.md b/g2p_pbms_student_model/README.md
new file mode 100644
index 00000000..658444f5
--- /dev/null
+++ b/g2p_pbms_student_model/README.md
@@ -0,0 +1,3 @@
+# G2P PBMS Student Model
+
+Refer to https://docs.openg2p.org.
diff --git a/g2p_pbms_student_model/__init__.py b/g2p_pbms_student_model/__init__.py
new file mode 100644
index 00000000..5d6d1635
--- /dev/null
+++ b/g2p_pbms_student_model/__init__.py
@@ -0,0 +1,3 @@
+# Part of OpenG2P. See LICENSE file for full copyright and licensing details.
+
+from . import models
diff --git a/g2p_pbms_student_model/__manifest__.py b/g2p_pbms_student_model/__manifest__.py
new file mode 100644
index 00000000..9759ab8d
--- /dev/null
+++ b/g2p_pbms_student_model/__manifest__.py
@@ -0,0 +1,18 @@
+# Part of OpenG2P. See LICENSE file for full copyright and licensing details.
+{
+ "name": "G2P PBMS Student Model",
+ "category": "G2P",
+ "version": "17.0.0.0.0",
+ "sequence": 1,
+ "author": "OpenG2P",
+ "website": "https://openg2p.org",
+ "license": "LGPL-3",
+ "depends": ["g2p_registry_individual", "g2p_programs"],
+ "data": ["views/individual_view.xml"],
+ "assets": {},
+ "demo": [],
+ "images": [],
+ "application": True,
+ "installable": True,
+ "auto_install": False,
+}
diff --git a/g2p_pbms_student_model/models/__init__.py b/g2p_pbms_student_model/models/__init__.py
new file mode 100644
index 00000000..a5507987
--- /dev/null
+++ b/g2p_pbms_student_model/models/__init__.py
@@ -0,0 +1,3 @@
+# Part of OpenG2P. See LICENSE file for full copyright and licensing details.
+
+from . import res_partner
diff --git a/g2p_pbms_student_model/models/res_partner.py b/g2p_pbms_student_model/models/res_partner.py
new file mode 100644
index 00000000..70c12576
--- /dev/null
+++ b/g2p_pbms_student_model/models/res_partner.py
@@ -0,0 +1,65 @@
+# Part of OpenG2P. See LICENSE file for full copyright and licensing details.
+import logging
+
+from odoo import fields, models
+
+_logger = logging.getLogger(__name__)
+
+
+class ResPartner(models.Model):
+ _inherit = "res.partner"
+
+ ##########################
+ ## Student Info ##
+ ##########################
+
+ institution_name = fields.Selection(
+ [
+ ("UCT", "University of Cape Town"),
+ ("SIT", "Stellenbosch Institute of Technology"),
+ ("DCE", "Durban College of Engineering"),
+ ]
+ )
+
+ year_of_study = fields.Selection(
+ [("1", "1st Year"), ("2", "2nd Year"), ("3", "3rd Year"), ("4", "4th Year")]
+ )
+
+ course_name = fields.Selection(
+ [
+ ("cs", "Computer Science"),
+ ("ec", "Electrical Engineering"),
+ ("me", "Mechanical Engineering"),
+ ("ce", "Civil Engineering"),
+ ("fa", "Financial Accounting"),
+ ("mm", "Marketing Management"),
+ ("scl", "Supply Chain & Logistics"),
+ ("bt", "Biotechnology"),
+ ("es", "Environmental Science"),
+ ("pse", "Polymer Science"),
+ ]
+ )
+ qualification_type = fields.Selection(
+ [("cerficiate", "Certificate"), ("diploma", "Diploma"), ("degree", "Degree")]
+ )
+ duration_of_course = fields.Char()
+ previous_nsfas_beneficiary = fields.Selection([("yes", "Yes"), ("no", "No")])
+
+ ##########################
+ ## Parent/Guardian Info ##
+ ##########################
+
+ guardian_name = fields.Char()
+ guardian_id = fields.Char()
+ household_income = fields.Selection(
+ [
+ ("below_122000", "Below R122,000"),
+ ("122000_to_350000", "R122,000 - R350,000"),
+ ("above_350000", "Above R350,000"),
+ ]
+ )
+ guardian_employment_status = fields.Selection([("employed", "Employed"), ("unemployed", "Unemployed")])
+ number_of_dependents = fields.Integer()
+ accommodation_type = fields.Selection(
+ [("owned", "Owned"), ("rented", "Rented"), ("informal", "Informal Settlement")]
+ )
diff --git a/g2p_pbms_student_model/pyproject.toml b/g2p_pbms_student_model/pyproject.toml
new file mode 100644
index 00000000..4231d0cc
--- /dev/null
+++ b/g2p_pbms_student_model/pyproject.toml
@@ -0,0 +1,3 @@
+[build-system]
+requires = ["whool"]
+build-backend = "whool.buildapi"
diff --git a/g2p_pbms_student_model/static/description/icon.png b/g2p_pbms_student_model/static/description/icon.png
new file mode 100644
index 00000000..5ecb429e
Binary files /dev/null and b/g2p_pbms_student_model/static/description/icon.png differ
diff --git a/g2p_pbms_student_model/views/individual_view.xml b/g2p_pbms_student_model/views/individual_view.xml
new file mode 100644
index 00000000..2aaf161b
--- /dev/null
+++ b/g2p_pbms_student_model/views/individual_view.xml
@@ -0,0 +1,35 @@
+
+
+
+
+ view_individual_form_pbms_student_model
+ res.partner
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+