Skip to content

Commit 8db0cf6

Browse files
[ADD] tg_website_event_sale_combo_tickets: модуль для клиента (#105)
Co-authored-by: Igor Makarenkov <igormak0325@gmail.com>
1 parent 45f0a6b commit 8db0cf6

25 files changed

+804
-0
lines changed
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
===============
2+
Combo tickets
3+
===============
4+
5+
Shuttle tickets - configuration
6+
-------------------------------
7+
8+
- Go to Main menu -> Events -> Choose existing or add a new event
9+
10+
- In the "Tickets" tab add tickets, if none exist
11+
12+
- In the Questions tab:
13+
14+
* add generic questions like name, email
15+
16+
* add question with type "Selection" and "Is shuttle" checked.
17+
18+
For this question add answers and corresponding tickets.
19+
20+
If you already have prepared shuttle events with tickets, you can use "Generate shuttle tickets" button.
21+
22+
After pressing the "Generate shuttle tickets" button, a wizard will open. Press "Add a line", and a new window will open, allowing you to select events to add to the Answers list.
23+
24+
By checking the checkbox in the upper-left corner, you can select all events at once.
25+
26+
- Close question form
27+
28+
- Save
29+
30+
Shuttle tickets - usage
31+
-----------------------
32+
33+
- Go to `/events`
34+
35+
- Click on "Get tickets" button in the event, that was used in the configuration above, then
36+
choose one ticket
37+
38+
- Fill in registration data and select a shuttle
39+
40+
- Pay for the registration for the event, if required
41+
42+
- RESULT: You will have at least 2 registrations.
43+
One is the event for which you have just registered.
44+
Second one is the shuttle event, that is used in the shuttle ticket question
45+
46+
Accommodation - configuration
47+
-----------------------------
48+
49+
- Go to Main menu -> Events -> Choose existing or add a new event
50+
51+
- In the "Tickets" tab add tickets, if none exists
52+
53+
- In the Questions tab:
54+
55+
* add generic questions like name, email
56+
57+
* add question with the type "Selection" and the "Is Accommodation" checked.
58+
59+
For this question add an answer and check the "Is Positive Accommodation Answer" checkbox.
60+
Pressing the "Generate Answers" button will automatically generate a single answer named "Yes" with a "Is Positive Accommdation Answer" checkbox already set.
61+
62+
- Set the price for the ticket
63+
64+
- Save
65+
66+
- Go to Main menu -> Events -> Configuration -> Settings
67+
68+
- In "Shop - Combo Tickets" section set the "Accommodation Category" value
69+
70+
Accommodation - usage
71+
---------------------
72+
73+
- Go to `/events`
74+
75+
- Click on "Get tickets" button in the event, that was used in the configuration above, then
76+
choose one ticket with the question with the type "Selection" and "Is Accommodation" checked.
77+
78+
- Fill in answers to questions, in the question for accommodation, pick answer that was set as the "Is Positive Accommodation Answer".
79+
80+
- Click on "Go to payment"
81+
82+
- Click "Checkout"
83+
84+
- RESULT: in the payment page you will see "Choose accommodation" instead of the payment button.
85+
86+
After the "Choose accommodation" button is pressed, you will be redirected to the shop page with a preselected category configured earlier in the "configuration" step.
87+
88+
Once an accommodation product is added to the shopping cart, the order can be processed.
89+
90+
Credits
91+
=======
92+
93+
Contributors
94+
------------
95+
96+
* `Eugene Molotov <https://github.com/em230418>`__
97+
98+
* `Igor Makarenkov <https://github.com/SecretAgentNull>`__
99+
100+
Sponsors
101+
--------
102+
103+
* `Tribal Gathering <https://www.tribalgathering.com/>`__
104+
105+
Maintainers
106+
-----------
107+
108+
* `IT-Projects LLC <https://it-projects.info>`__
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from . import models
2+
from . import controllers
3+
from . import wizard
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"name": """Combo tickets""",
3+
"version": "17.0.0.2.0",
4+
"author": "IT-Projects LLC, Eugene Molotov",
5+
"support": "it@it-projects.info",
6+
"website": "https://github.com/it-projects-llc/tg-addons",
7+
"license": "LGPL-3",
8+
"depends": [
9+
"website_event_sale",
10+
"website_event_questions_by_ticket",
11+
],
12+
"data": [
13+
"security/ir.model.access.csv",
14+
"wizard/generate_shuttle_ticket_answers_views.xml",
15+
"views/event_question_views.xml",
16+
"views/templates.xml",
17+
"views/res_config_settings_views.xml",
18+
"views/event_event_views.xml",
19+
],
20+
"demo": [],
21+
"assets": {
22+
"web.assets_frontend": [
23+
"tg_website_event_sale_combo_tickets/static/**/*",
24+
]
25+
},
26+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from . import main
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
from copy import deepcopy
2+
3+
from odoo.http import request
4+
5+
from odoo.addons.website_event_sale.controllers.main import WebsiteEventSaleController
6+
from odoo.addons.website_sale.controllers.main import WebsiteSale
7+
8+
9+
class WebsiteEventSaleComboTicketsController(WebsiteEventSaleController):
10+
def _process_attendees_form(self, event, form_details):
11+
registrations = super()._process_attendees_form(event, form_details)
12+
extra_registrations = []
13+
for reg in registrations:
14+
answers = reg.get("registration_answer_ids") or []
15+
for answer in answers:
16+
answer_id = answer[2].get("value_answer_id")
17+
answer_record = request.env["event.question.answer"].browse(answer_id)
18+
if answer_record.question_id.is_shuttle:
19+
extra_reg = deepcopy(reg)
20+
extra_reg.update(event_ticket_id=answer_record.shuttle_ticket.id)
21+
extra_registrations.append(extra_reg)
22+
return registrations + extra_registrations
23+
24+
25+
class TGWebsiteSale(WebsiteSale):
26+
def _get_shop_payment_values(self, order, **kwargs):
27+
res = super()._get_shop_payment_values(order, **kwargs)
28+
29+
has_positive_accomodation_answers = False
30+
accomodation_line = False
31+
accomodation_category = order.company_id.accomodation_category
32+
if not accomodation_category:
33+
return res
34+
35+
for line in order.order_line:
36+
has_positive_accomodation_answers = (
37+
has_positive_accomodation_answers
38+
or any(
39+
line.registration_ids.registration_answer_choice_ids.filtered(
40+
lambda x: x.question_id.is_accomodation
41+
).mapped("value_answer_id.is_positive_accomodation_answer")
42+
)
43+
)
44+
45+
if (
46+
accomodation_category
47+
in line.product_id.public_categ_ids.parents_and_self
48+
):
49+
accomodation_line = line
50+
51+
if has_positive_accomodation_answers and not accomodation_line:
52+
res.update(
53+
{
54+
"hide_payment_button": True,
55+
"should_include_accomodation": True,
56+
"accomodation_category_url": f"/shop/category/{accomodation_category.id}", # noqa: E501
57+
"errors": [None],
58+
}
59+
)
60+
61+
return res
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
def migrate(cr, installed_version):
2+
cr.execute(
3+
"ALTER TABLE event_question RENAME COLUMN is_shuttle_ticket TO is_shuttle"
4+
)
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from . import event_question
2+
from . import event_question_answer
3+
from . import event_ticket
4+
from . import res_config_settings
5+
from . import res_company
6+
from . import event_event
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
from odoo import api, models
2+
3+
4+
class EventEvent(models.Model):
5+
_inherit = "event.event"
6+
7+
@api.model_create_multi
8+
def create(self, vals_list):
9+
records = super().create(vals_list)
10+
records.question_ids._check_accomodation_answers()
11+
return records
12+
13+
def write(self, vals):
14+
res = super().write(vals)
15+
self.question_ids._check_accomodation_answers()
16+
return res
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
from odoo import _, api, fields, models
2+
from odoo.exceptions import ValidationError
3+
4+
5+
class EventQuestion(models.Model):
6+
_inherit = "event.question"
7+
8+
is_shuttle = fields.Boolean(
9+
compute="_compute_is_shuttle", store=True, readonly=False
10+
)
11+
12+
is_accomodation = fields.Boolean(
13+
compute="_compute_is_accomodation", store=True, readonly=False
14+
)
15+
16+
@api.depends("question_type")
17+
def _compute_is_shuttle(self):
18+
for record in self:
19+
if record.question_type != "simple_choice" or record.is_accomodation:
20+
record.is_shuttle = False
21+
22+
@api.depends("question_type")
23+
def _compute_is_accomodation(self):
24+
for record in self:
25+
if record.question_type != "simple_choice" or record.is_shuttle:
26+
record.is_accomodation = False
27+
28+
@api.constrains("is_shuttle", "is_accomodation")
29+
def _check_shuttle_accomodation(self):
30+
for record in self:
31+
if record.is_shuttle and record.is_accomodation:
32+
raise ValidationError(
33+
_("Question cannot be both for shuttle and accomodation")
34+
)
35+
36+
def _check_accomodation_answers(self, allow_empty=False):
37+
for question in self.filtered("is_accomodation"):
38+
flags = question.answer_ids.mapped("is_positive_accomodation_answer")
39+
if not flags and allow_empty:
40+
continue
41+
42+
has_positive_accomodation_answer = any(flags)
43+
if not has_positive_accomodation_answer:
44+
raise ValidationError(
45+
_(
46+
'Accomodation "%s" question should have positive answer',
47+
question.title,
48+
)
49+
)
50+
51+
def _check_accomodation_category(self):
52+
questions = self.filtered("is_accomodation")
53+
54+
for company in questions.mapped("event_id.company_id"):
55+
if not company.accomodation_category:
56+
raise ValidationError(_("Accomodation category is not set in settings"))
57+
58+
def action_generate_ticket_answers(self):
59+
self._check_shuttle_accomodation()
60+
61+
if self.is_shuttle:
62+
w = self.env["generate.shuttle.ticket.answers"].create(
63+
{
64+
"question": self.id,
65+
}
66+
)
67+
68+
return {
69+
"type": "ir.actions.act_window",
70+
"res_model": w._name,
71+
"res_id": w.id,
72+
"view_mode": "form",
73+
"target": "new",
74+
}
75+
76+
elif self.is_accomodation:
77+
EQA = self.env["event.question.answer"].sudo()
78+
79+
has_positive_accomodation_answer = any(
80+
self.answer_ids.mapped("is_positive_accomodation_answer")
81+
)
82+
if not has_positive_accomodation_answer:
83+
EQA.create(
84+
{
85+
"name": _("Yes"),
86+
"question_id": self.id,
87+
"is_positive_accomodation_answer": True,
88+
}
89+
)
90+
91+
else:
92+
raise NotImplementedError()
93+
94+
@api.model_create_multi
95+
def create(self, vals_list):
96+
records = super().create(vals_list)
97+
records._check_accomodation_category()
98+
records._check_accomodation_answers(allow_empty=True)
99+
return records
100+
101+
def write(self, vals):
102+
res = super().write(vals)
103+
self._check_accomodation_category()
104+
self._check_accomodation_answers(allow_empty=True)
105+
return res
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
from odoo import api, fields, models
2+
3+
4+
class EventQuestionAnswer(models.Model):
5+
_inherit = "event.question.answer"
6+
7+
shuttle_ticket = fields.Many2one("event.event.ticket")
8+
shuttle_ticket_domain = fields.Binary(compute="_compute_shuttle_ticket_domain")
9+
is_positive_accomodation_answer = fields.Boolean()
10+
11+
@api.depends("question_id.event_id")
12+
def _compute_shuttle_ticket_domain(self):
13+
for record in self:
14+
record.shuttle_ticket_domain = [
15+
("event_id.stage_id.pipe_end", "=", False),
16+
]
17+
18+
@api.onchange("shuttle_ticket")
19+
def _onchange_shuttle_ticket(self):
20+
if not self.name and self.shuttle_ticket:
21+
self.name = self.shuttle_ticket.name

0 commit comments

Comments
 (0)