From c386bd1be761c708d9eda776d3ce37d391092aa4 Mon Sep 17 00:00:00 2001 From: niku-odoo Date: Thu, 5 Sep 2024 20:20:23 +0530 Subject: [PATCH] [IMP] dental: implemented various views, controller and portal After this commit - added controller so that dental will be visible in my account - implemented invoice in dental --- dental/__manifest__.py | 13 +- dental/models/__init__.py | 1 + dental/models/dental_allergies.py | 3 +- dental/models/dental_chronic_conditions.py | 3 +- dental/models/dental_configuration.py | 3 +- dental/models/dental_habits.py | 3 +- dental/models/dental_medical_aids.py | 3 +- dental/models/dental_medical_history.py | 98 +++++++++++++ dental/models/dental_medical_symptoms.py | 3 +- dental/models/dental_medication.py | 3 +- dental/models/dental_patient.py | 78 ++++++++-- dental/security/ir.model.access.csv | 1 + dental/static/tooth.png | Bin 0 -> 2620 bytes dental/views/dental_allergies_view.xml | 5 + .../views/dental_chronic_conditions_views.xml | 5 + dental/views/dental_configuration_views.xml | 5 + dental/views/dental_habits_view.xml | 5 + dental/views/dental_medical_aids_view.xml | 5 + dental/views/dental_medical_history_view.xml | 138 ++++++++++++++++++ dental/views/dental_medical_symptoms_view.xml | 5 + dental/views/dental_medication_views.xml | 5 + dental/views/dental_patient_menus.xml | 2 +- dental/views/dental_patient_views.xml | 117 +++++++++++++-- 23 files changed, 472 insertions(+), 32 deletions(-) create mode 100644 dental/models/dental_medical_history.py create mode 100644 dental/static/tooth.png create mode 100644 dental/views/dental_medical_history_view.xml diff --git a/dental/__manifest__.py b/dental/__manifest__.py index 8799610e7..aae01407a 100644 --- a/dental/__manifest__.py +++ b/dental/__manifest__.py @@ -3,19 +3,22 @@ 'version': '17.0', 'summary': 'Manage Dental Patients , Appointments and Medical Record', 'author': 'Your Name', - 'depends': ['base'], + 'depends': ['base', 'mail', 'account'], 'license': 'LGPL-3', 'data': [ - 'security/ir.model.access.csv', - 'views/dental_patient_views.xml', + 'security/ir.model.access.csv', 'views/dental_medical_aids_view.xml', + 'views/dental_patient_views.xml', 'views/dental_medical_symptoms_view.xml', 'views/dental_medication_views.xml', 'views/dental_chronic_conditions_views.xml', 'views/dental_allergies_view.xml', 'views/dental_habits_view.xml', - 'views/dental_configuration_views.xml', - 'views/dental_patient_menus.xml', + 'views/dental_configuration_views.xml', + 'views/dental_medical_history_view.xml', + 'views/dental_patient_menus.xml' + + ], 'installable': True, 'application': True, diff --git a/dental/models/__init__.py b/dental/models/__init__.py index f028651c2..dc4013f18 100644 --- a/dental/models/__init__.py +++ b/dental/models/__init__.py @@ -6,3 +6,4 @@ from . import dental_allergies from . import dental_habits from . import dental_configuration +from . import dental_medical_history \ No newline at end of file diff --git a/dental/models/dental_allergies.py b/dental/models/dental_allergies.py index 64889c12a..de83c95b7 100644 --- a/dental/models/dental_allergies.py +++ b/dental/models/dental_allergies.py @@ -1,9 +1,10 @@ -from odoo import api, models, fields +from odoo import models, fields class DentalAllergies(models.Model): _name = "dental.allergies" _description = "Dental Allergies" + _inherit = ['mail.thread', 'mail.activity.mixin'] name = fields.Char(string='Name') diff --git a/dental/models/dental_chronic_conditions.py b/dental/models/dental_chronic_conditions.py index d542062da..797ad1fb5 100644 --- a/dental/models/dental_chronic_conditions.py +++ b/dental/models/dental_chronic_conditions.py @@ -1,10 +1,11 @@ -from odoo import api, models, fields +from odoo import models, fields class DentalChronicConditions(models.Model): _name = "dental.chronic.conditions" _description = "Dental Chronic Conditions" + _inherit = ['mail.thread', 'mail.activity.mixin'] name = fields.Char(string='Name') sequence = fields.Integer('Sequence') diff --git a/dental/models/dental_configuration.py b/dental/models/dental_configuration.py index 5ec1bbf0a..665ac2841 100644 --- a/dental/models/dental_configuration.py +++ b/dental/models/dental_configuration.py @@ -1,9 +1,10 @@ -from odoo import api, models, fields +from odoo import models, fields class DentalConfiguration(models.Model): _name = "dental.configuration" _description = "Dental Configuration" + _inherit = ['mail.thread', 'mail.activity.mixin'] name = fields.Char(string='Name') diff --git a/dental/models/dental_habits.py b/dental/models/dental_habits.py index ce44fcb94..8807bc176 100644 --- a/dental/models/dental_habits.py +++ b/dental/models/dental_habits.py @@ -1,9 +1,10 @@ -from odoo import api, models, fields +from odoo import models, fields class DentalHabits(models.Model): _name = "dental.habits" _description = "Dental Habits" + _inherit = ['mail.thread', 'mail.activity.mixin'] name = fields.Char(string='Name') diff --git a/dental/models/dental_medical_aids.py b/dental/models/dental_medical_aids.py index 018d4efff..d278b8c47 100644 --- a/dental/models/dental_medical_aids.py +++ b/dental/models/dental_medical_aids.py @@ -1,10 +1,11 @@ -from odoo import api, models, fields +from odoo import models, fields class DentalMedicalAids(models.Model): _name = "dental.medical.aids" _description = "Medical Aids" + _inherit = ['mail.thread', 'mail.activity.mixin'] name = fields.Char(string='Name') contact_id = fields.Many2one('res.partner', string='Contact') diff --git a/dental/models/dental_medical_history.py b/dental/models/dental_medical_history.py new file mode 100644 index 000000000..0186cbca4 --- /dev/null +++ b/dental/models/dental_medical_history.py @@ -0,0 +1,98 @@ +from odoo import models, fields, api +from datetime import date + + +class DentalMedicalHistory(models.Model): + _name = 'dental.medical.history' + _description = 'Dental Mdeical History' + _inherit = ['mail.thread', 'mail.activity.mixin'] + + + name = fields.Char(string="Consultation Name", compute="_compute_name") + date = fields.Date( + string="Date", default=fields.Date.context_today, required=True) + patient_id = fields.Many2one( + 'dental.patient', string="Patient", required=True) + main_complaint = fields.Text(string="Main Complaint") + history = fields.Text(string="History") + tags = fields.Char(string="Tags") + company_id = fields.Many2one('res.company', string='Company') + did_not_attend = fields.Boolean(required=True) + + xray_file_1 = fields.Binary(string="X-ray File 1") + xray_file_2 = fields.Binary(string="X-ray File 2") + clear_aligner_file_1 = fields.Binary(string="Clear Aligner File 1") + clear_aligner_file_2 = fields.Binary(string="Clear Aligner File 2") + + + habits = fields.Text(string="Habits") + extra_oral_observation = fields.Text(string="Extra-Oral Observation") + + teeth_chart = fields.Char(string="Teeth Chart") + + + treatment_notes = fields.Text(string="Treatment Notes") + + consultation_type = fields.Selection([ + ('full_consultation', 'Full Consultation with Bitewings and Scan'), + ('basic_consultation', 'Basic Consultation'), + ('no_consultation', 'No Consultation') + ], string="Consultation Type") + + call_out = fields.Boolean(string="Call Out") + scale_and_polish = fields.Boolean(string="Scale and Polish") + fluoride = fields.Boolean(string="Fluoride") + + filling_description = fields.Text(string="Filling Description") + + aligner_delivery = fields.Boolean( + string="Aligner Delivery and Attachment Placed") + whitening = fields.Boolean(string="Whitening") + + fissure_sealant_qty = fields.Float( + string="Fissure Sealant Quantity", digits=(6, 2)) + + attachments_removed = fields.Boolean(string="Attachments Removed") + aligner_followup_scan = fields.Boolean(string="Aligner Follow-up Scan") + + other_notes = fields.Text(string="Other Notes") + + notes = fields.Text(string="Additional Notes") + + @api.depends("patient_id") + def _compute_name(self): + for record in self: + record.name = f"{record.patient_id.name}-{date.today()}" + + tooth_18_staining = fields.Boolean(string="18 Staining") + tooth_17_staining = fields.Boolean(string="17 Staining") + tooth_16_staining = fields.Boolean(string="16 Staining") + tooth_15_staining = fields.Boolean(string="15 Staining") + tooth_14_staining = fields.Boolean(string="14 Staining") + tooth_13_staining = fields.Boolean(string="13 Staining") + tooth_12_staining = fields.Boolean(string="12 Staining") + tooth_11_staining = fields.Boolean(string="11 Staining") + tooth_28_staining = fields.Boolean(string="28 Staining") + tooth_27_staining = fields.Boolean(string="27 Staining") + tooth_26_staining = fields.Boolean(string="26 Staining") + tooth_25_staining = fields.Boolean(string="25 Staining") + tooth_24_staining = fields.Boolean(string="24 Staining") + tooth_23_staining = fields.Boolean(string="23 Staining") + tooth_22_staining = fields.Boolean(string="22 Staining") + tooth_21_staining = fields.Boolean(string="21 Staining") + tooth_38_staining = fields.Boolean(string="38 Staining") + tooth_37_staining = fields.Boolean(string="37 Staining") + tooth_36_staining = fields.Boolean(string="36 Staining") + tooth_35_staining = fields.Boolean(string="35 Staining") + tooth_34_staining = fields.Boolean(string="34 Staining") + tooth_33_staining = fields.Boolean(string="33 Staining") + tooth_32_staining = fields.Boolean(string="32 Staining") + tooth_31_staining = fields.Boolean(string="31 Staining") + tooth_41_staining = fields.Boolean(string="41 Staining") + tooth_42_staining = fields.Boolean(string="42 Staining") + tooth_43_staining = fields.Boolean(string="43 Staining") + tooth_44_staining = fields.Boolean(string="44 Staining") + tooth_45_staining = fields.Boolean(string="45 Staining") + tooth_46_staining = fields.Boolean(string="46 Staining") + tooth_47_staining = fields.Boolean(string="47 Staining") + tooth_48_staining = fields.Boolean(string="48 Staining") \ No newline at end of file diff --git a/dental/models/dental_medical_symptoms.py b/dental/models/dental_medical_symptoms.py index 5ba4c6da9..e1c9a7bd7 100644 --- a/dental/models/dental_medical_symptoms.py +++ b/dental/models/dental_medical_symptoms.py @@ -1,9 +1,10 @@ -from odoo import api, models, fields +from odoo import models, fields class DentalMedicalSymptoms(models.Model): _name = "dental.medical.symptoms" _description = "Dental Medical Symptoms" + _inherit = ['mail.thread', 'mail.activity.mixin'] name = fields.Char(string='Name') diff --git a/dental/models/dental_medication.py b/dental/models/dental_medication.py index ba2992d05..d447738b6 100644 --- a/dental/models/dental_medication.py +++ b/dental/models/dental_medication.py @@ -1,10 +1,11 @@ -from odoo import api, models, fields +from odoo import models, fields class DentalMedication(models.Model): _name = "dental.medication" _description = "Dental Medication" + _inherit = ['mail.thread', 'mail.activity.mixin'] name = fields.Char(string='Name') sequence = fields.Integer('Sequence') diff --git a/dental/models/dental_patient.py b/dental/models/dental_patient.py index 5ad5dd8d8..cb7d69b19 100644 --- a/dental/models/dental_patient.py +++ b/dental/models/dental_patient.py @@ -1,24 +1,27 @@ -from odoo import api, models, fields +from odoo import models, fields , Command class DentalPatient(models.Model): _name = "dental.patient" _description = "Dental Patient" + _inherit = ['mail.thread', 'mail.activity.mixin'] - name = fields.Char(string="Name") + name = fields.Char(string="Name", required=True) + image = fields.Image('Image') gp_id = fields.Many2one('res.partner', string="GP's Name") gp_phone = fields.Char(related='gp_id.phone', string="GP's Phone", readonly=True) - chronic_conditions = fields.Many2many( - 'dental.chronic.condition', string="Chronic Conditions") - medication = fields.Many2many('dental.medication', string="Medication") - hospitalized_this_year = fields.Boolean(string="Hospitalised this Year") - allergies = fields.Char(string="Allergies") + chronic_conditions_ids = fields.Many2many( + 'dental.chronic.conditions', string="Chronic Conditions") + medication_ids = fields.Many2many('dental.medication', string="Medication") + hospitalized_this_year = fields.Text(string="Hospitalised this Year") + allergies_ids = fields.Many2many('dental.allergies', string="Allergies") notes = fields.Text(string='Notes') - habits_substance_abuse = fields.Char(string="Habits/Substance Abuse") + habits_substance_abuse_ids = fields.Many2many('dental.habits', string="Habits/Substance Abuse") under_specialist_care = fields.Char(string="Under Specialist Care") psychiatric_history = fields.Char(string="Psychiatric History") + female = fields.Boolean(string="Female") is_pregnant = fields.Boolean(string="Are you pregnant?", default=False) is_nursing = fields.Boolean(string="Are you nursing?", default=False) @@ -27,8 +30,65 @@ class DentalPatient(models.Model): ('birth_control', 'Birth control'), ('neither', 'Neither') ], string="Are you on...?", default='neither') - medical_aid = fields.Many2one('dental.medical.aids', string="Medical Aid") + + medical_aid_id = fields.Many2one( + 'dental.medical.aids', string="Medical Aid") medical_aid_plan = fields.Char(string='Medical Aid Plan') medical_aid_number = fields.Integer(string='Medical Aid Number') main_member_code = fields.Integer(string='Medical Member Code') dependent_code = fields.Integer(string='Dependent Code') + + emergency_contact_id = fields.Many2one( + 'res.partner', string="Emergency Contact") + mobile = fields.Char(related='emergency_contact_id.phone', string='Mobile') + + company_school_id = fields.Many2one('res.partner', string="Company or School") + occupation_grade = fields.Char(string="Occupation or Grade") + identity_number = fields.Char(string="Identity Number") + date_of_birth = fields.Date(string="Date of Birth") + gender = fields.Selection([ + ('female', 'Female'), + ('male', 'Male'), + ('neither', 'Neither') + ], string="Gender") + marital_status = fields.Selection([ + ('single', 'Single'), + ('married', 'Married'), + ('divorced', 'Divorced'), + ('widowed', 'Widowed') + ], string="Marital Status") + + consent_signature = fields.Binary(string="Consent Signature") + consent_date = fields.Date(string="Consent Date") + + + guarantor_id = fields.Many2one( + 'res.partner', string="Guarantor") + guarantor_mobile = fields.Char( + related='guarantor_id.mobile', string="Guarantor Mobile Phone", readonly=True) + guarantor_phone = fields.Char(related='guarantor_id.phone', string="Phone") + guarantor_email = fields.Char(related='guarantor_id.email', string="Email") + + tags = fields.Char(string="Tags") + company_id = fields.Many2one( + 'res.company', string="Company", default=lambda self: self.env.company) + + + history_ids = fields.One2many('dental.medical.history', 'patient_id') + state = fields.Selection(string='Status', default='new', + selection=[('new', 'New'), ('to do today', 'To Do Today'), ('done', 'Done'), ('to invoice', 'To Invoice')]) + + def book_an_appointment(self): + move_values = { + "partner_id": self.guarantor_id.id, + "move_type": "out_invoice", + 'invoice_date': fields.Date.today(), + "invoice_line_ids": [ + Command.create({ + "name": "consultant fee", + "quantity": 1, + "price_unit": 500 + }) + ], + } + self.env['account.move'].sudo().create(move_values) diff --git a/dental/security/ir.model.access.csv b/dental/security/ir.model.access.csv index d34a07464..77a63bac1 100644 --- a/dental/security/ir.model.access.csv +++ b/dental/security/ir.model.access.csv @@ -7,6 +7,7 @@ access_dental_configuration,access_dental_configuration,model_dental_configurati access_dental_chronic_conditions,access_dental_chronic_conditions,model_dental_chronic_conditions,base.group_user,1,1,1,1 access_dental_allergies,access_dental_allergies,model_dental_allergies,base.group_user,1,1,1,1 access_dental_habits,access_dental_habits,model_dental_habits,base.group_user,1,1,1,1 +dental.access_dental_medical_history,access_dental_medical_history,dental.model_dental_medical_history,base.group_user,1,1,1,1 diff --git a/dental/static/tooth.png b/dental/static/tooth.png new file mode 100644 index 0000000000000000000000000000000000000000..5d6ab6fb3a84423e000f72b4fe7b63455f0f37c9 GIT binary patch literal 2620 zcmb7`c{tSDAIHBl5;KVGA;v|| z$={v(D7%(<2hITicQxvqsbkpX*Y0k9hx=3WeDZO*K)vVSwklmm!~~u==huXKP<`;5c4p1ypE^P8zd z)z{aTo}nB%ZOy%^)MvgR{~UkcOd0Qgpg6$}nyAb6HjPP#OouCxKuFM!t@$*tq&;>r z6b=>tV(ZBnwa>B}&OWWO%c#Ywhe~MzXy>vq-`>?vQ5g2xC)o$9XA$Kzn)J7o!DkZU z;6h2b|6E_Ul|e>Xg~jU;0#*PtulKz98gAt)L!BXW_E-tq zL((Mlu<4IvkhcH(m&JFb3<@W!J*rnn^2!;QtU`-4>AWZgHZNO$h))txLbpsSn|(oc zkz%AO5$qHI9!;Wb7nr2Gh7bL7T#2KdweiQq!opM4FFL7ls(nS)07JmecPe;znPal* zk}vn!(FmA)^LA)o5tKc%<9B>s1#QBAY%}jIQ_&`yzIvw;wJl00ZjWTg&o0I zxx#qgk3PlPJRkX|g?XOsm82^K?6bUUHbfBko+OR)SH9l64hg8FJ_6BAkF{D3lZz=CNjnfT1o+PHRVnyS5Y@J8YTI4eA@REf~8*I075%KaiW0B{-UBPD&IK!_P7-nsJ-`s?slvm;kT;W)V6yaMQBxU$7d zyKIv{OpcZte<5|LFB~{W5j9{~&Kjo~I@Uc-U*LoHGv4I>PF3LVV70`Oc|k6-a_35I z>gC1&Sqsmh98vf7y{~J0s^u=##|%3%IITDe7^|nuNx{L-9YGPV*5){~voWf>>I#9d z?)uh%MYGbO&i$n^s(RdQF%4Y!?8uq@EPa8_grjMkG-R}oPN2_N>vi%hA4?~vUcQ!Q zw5K7$!TQn0qYj#pHQ*tHpHy+K*j_oZN7xYUooYg(TD2eqS|)ei5urpmmjjj$_8REQ z_}rW)9H4~C%Xmf3YPmnL33TR(Rqf00Oz_ii&lTnrx)`zDyu*5kW01gD zm^S$8I(z4ZvCA8*FDrw~h-Br3j+aS6Xo1TX`o}4aqx^j%=&avNl{2``H3U6eV?KG>pzate zTYj7Wa4qZPoki+z+$gYoy~4EFVvEGGUR||E8@9$M=AaQ2^``Lr!&+QOqW98?JgJ@l zFnPv{eJlR9J85DSVKAUDIW;AjFMT#97o1Ql4t*!60dH-?^D5FnW~Hgr+_-cqWUKkw zv^G9ys@mg1O^%M&-8XAW0ncd_eMs{g^h&xh`0>^%~_Ww+kl z*SGvlYc6 z#f>b$m!=%+VYgIdF?+8!_&9TC$3gmsLW$xKx@ifJw_M*5E>OO-q(`396@C`7VXx>} zXGgqR!Dwx5b$n~AuMf|@A)84@Bp(c4YK>|d<14NTv6m0L|9b*rMht&C&p2|DwKc z+8c2qANd$O|J*Chp^Ze3))Hm0-=cBnlo`CzuQ~-lrL6MwZBhP{h8YVtPg4s`jE_t6 z`JiW-Db*bulvu4c?j>k#VyBT&#%C~&`xHho#V1u#@6hhK7ZX9F{Plh_Dv(4s(iEM1 z&7p)6O@gWvA=?wAPs!|GYTw;idgZiI0=ATy(mKJsr+Y8r#=L!hu1JHHG39fvSQJK9 zPNREypfPAAC1fM=CznY6@Tu>a^miK@ko}RLEEU7a{=wLVoOcZ|C;Ryk{X+nA zazJ>Hw-+e{hQgX(uy=r2`u%PNgHtFJb#FY`pBNTE4Du%W|GiudO8wQ}xW6xEYU5Wn> DdFaZg literal 0 HcmV?d00001 diff --git a/dental/views/dental_allergies_view.xml b/dental/views/dental_allergies_view.xml index 5d25032f8..fffa6825e 100644 --- a/dental/views/dental_allergies_view.xml +++ b/dental/views/dental_allergies_view.xml @@ -27,6 +27,11 @@ +
+ + + +
diff --git a/dental/views/dental_chronic_conditions_views.xml b/dental/views/dental_chronic_conditions_views.xml index 0cba8e1d8..5c21a668e 100644 --- a/dental/views/dental_chronic_conditions_views.xml +++ b/dental/views/dental_chronic_conditions_views.xml @@ -32,6 +32,11 @@ +
+ + + +
diff --git a/dental/views/dental_configuration_views.xml b/dental/views/dental_configuration_views.xml index ba1268d3d..f30f95330 100644 --- a/dental/views/dental_configuration_views.xml +++ b/dental/views/dental_configuration_views.xml @@ -27,6 +27,11 @@ +
+ + + +
diff --git a/dental/views/dental_habits_view.xml b/dental/views/dental_habits_view.xml index 4611eecda..c394fb85d 100644 --- a/dental/views/dental_habits_view.xml +++ b/dental/views/dental_habits_view.xml @@ -28,6 +28,11 @@ +
+ + + +
diff --git a/dental/views/dental_medical_aids_view.xml b/dental/views/dental_medical_aids_view.xml index 5360d2649..e8dcd7892 100644 --- a/dental/views/dental_medical_aids_view.xml +++ b/dental/views/dental_medical_aids_view.xml @@ -45,6 +45,11 @@ +
+ + + +
diff --git a/dental/views/dental_medical_history_view.xml b/dental/views/dental_medical_history_view.xml new file mode 100644 index 000000000..4e3276f79 --- /dev/null +++ b/dental/views/dental_medical_history_view.xml @@ -0,0 +1,138 @@ + + + Dental Consultation Form + dental.medical.history + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+
18
+
17
+
16
+
15
+
14
+
13
+
12
+
11
+
28
+
27
+
26
+
25
+
24
+
23
+
22
+
21
+
+
+ + + + + + + + + + + + + + + + + + + +
+
38
+
37
+
36
+
35
+
34
+
33
+
32
+
31
+
41
+
42
+
43
+
44
+
45
+
46
+
47
+
48
+
+
+ + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + +
+ +
+
+
+ + + +
+
+
+
+
\ No newline at end of file diff --git a/dental/views/dental_medical_symptoms_view.xml b/dental/views/dental_medical_symptoms_view.xml index c34d2fc7a..3b1fcf884 100644 --- a/dental/views/dental_medical_symptoms_view.xml +++ b/dental/views/dental_medical_symptoms_view.xml @@ -28,6 +28,11 @@ +
+ + + +
diff --git a/dental/views/dental_medication_views.xml b/dental/views/dental_medication_views.xml index 3df5d0dc4..cec9cbaff 100644 --- a/dental/views/dental_medication_views.xml +++ b/dental/views/dental_medication_views.xml @@ -29,6 +29,11 @@ +
+ + + +
diff --git a/dental/views/dental_patient_menus.xml b/dental/views/dental_patient_menus.xml index 780c5ed9b..e1c8df72d 100644 --- a/dental/views/dental_patient_menus.xml +++ b/dental/views/dental_patient_menus.xml @@ -4,7 +4,7 @@ - + - Patient dental.patient @@ -17,6 +16,11 @@ + + + + + @@ -26,25 +30,46 @@ dental.patient
+
+
+ +
+

+ +

+
+ + + + + + + + + - + - + - + + - - + + - + @@ -58,7 +83,7 @@ - + @@ -68,19 +93,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+ + + + + + + + + + + + + +
+ +
+
+
+ + + +
- + dental.patient.kanban dental.patient - + + +
+
+ + + +
+ +
+
+