diff --git a/event_ticket_registration_limit/__init__.py b/event_ticket_registration_limit/__init__.py
new file mode 100644
index 00000000000..0650744f6bc
--- /dev/null
+++ b/event_ticket_registration_limit/__init__.py
@@ -0,0 +1 @@
+from . import models
diff --git a/event_ticket_registration_limit/__manifest__.py b/event_ticket_registration_limit/__manifest__.py
new file mode 100644
index 00000000000..0120f76feef
--- /dev/null
+++ b/event_ticket_registration_limit/__manifest__.py
@@ -0,0 +1,19 @@
+{
+ 'name': 'Event Ticket Registration Limit',
+ 'description': 'adds a feature to restrict the maximum number of tickets per registration',
+ 'category': 'Event/Event Ticket',
+ 'depends': ['base', 'event', 'website_event'],
+
+ 'version': '1.0',
+ 'author': 'Kishan B. Gajera',
+
+ 'installable': True,
+ 'application': True,
+
+ 'license': 'LGPL-3',
+
+ 'data': [
+ 'views/event_ticket_view.xml',
+ 'views/modal_ticket_registration_web_view.xml',
+ ]
+}
diff --git a/event_ticket_registration_limit/models/__init__.py b/event_ticket_registration_limit/models/__init__.py
new file mode 100644
index 00000000000..34b3cee397f
--- /dev/null
+++ b/event_ticket_registration_limit/models/__init__.py
@@ -0,0 +1 @@
+from . import event_ticket
diff --git a/event_ticket_registration_limit/models/event_ticket.py b/event_ticket_registration_limit/models/event_ticket.py
new file mode 100644
index 00000000000..97123318296
--- /dev/null
+++ b/event_ticket_registration_limit/models/event_ticket.py
@@ -0,0 +1,6 @@
+from odoo import models, fields
+
+class EventTicket(models.Model):
+ _inherit = "event.event.ticket"
+
+ max_tickets_per_registration = fields.Integer(string="Max Tickets per Registration", help="Define the maximum number of tickets that can be booked per registration.")
diff --git a/event_ticket_registration_limit/views/event_ticket_view.xml b/event_ticket_registration_limit/views/event_ticket_view.xml
new file mode 100644
index 00000000000..64c4c364cd8
--- /dev/null
+++ b/event_ticket_registration_limit/views/event_ticket_view.xml
@@ -0,0 +1,13 @@
+
+
+
+ event.event.ticket.view.list.inherit
+ event.event.ticket
+
+
+
+
+
+
+
+
diff --git a/event_ticket_registration_limit/views/modal_ticket_registration_web_view.xml b/event_ticket_registration_limit/views/modal_ticket_registration_web_view.xml
new file mode 100644
index 00000000000..29adcba272c
--- /dev/null
+++ b/event_ticket_registration_limit/views/modal_ticket_registration_web_view.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+ min(seats_max_ticket, seats_max_event, seats_max_registration)
+
+
+
+
+
+
+
+ min(seats_max_ticket, seats_max_event, seats_max_registration)
+
+
+
diff --git a/sale_extension/__init__.py b/sale_extension/__init__.py
new file mode 100644
index 00000000000..9b4296142f4
--- /dev/null
+++ b/sale_extension/__init__.py
@@ -0,0 +1,2 @@
+from . import models
+from . import wizard
diff --git a/sale_extension/__manifest__.py b/sale_extension/__manifest__.py
new file mode 100644
index 00000000000..4d895d8a2ab
--- /dev/null
+++ b/sale_extension/__manifest__.py
@@ -0,0 +1,21 @@
+{
+ 'name': 'Sale Extension',
+ 'version': '1.0',
+ 'depends': ['base', 'sale', 'sale_management'],
+ 'author': "Kishan B. Gajera",
+ 'category': 'Sale/Sale',
+ 'description': """
+ A sample sale extension
+ """,
+
+ 'application': True,
+ 'installable': True,
+
+ 'data': [
+ 'security/ir.model.access.csv',
+ 'views/sale_order_views.xml',
+ 'wizard/cost_distribution_wizard.xml',
+ ],
+
+ 'license':'LGPL-3',
+}
diff --git a/sale_extension/models/__init__.py b/sale_extension/models/__init__.py
new file mode 100644
index 00000000000..8eb9d1d4046
--- /dev/null
+++ b/sale_extension/models/__init__.py
@@ -0,0 +1 @@
+from . import sale_order_line
diff --git a/sale_extension/models/sale_order_line.py b/sale_extension/models/sale_order_line.py
new file mode 100644
index 00000000000..270fc42f072
--- /dev/null
+++ b/sale_extension/models/sale_order_line.py
@@ -0,0 +1,20 @@
+from odoo import models, fields
+
+class SaleOrderLine(models.Model):
+ _inherit = 'sale.order.line'
+
+ distributed_cost = fields.Float("Distributed Cost")
+
+ def action_distribute_cost(self):
+ return {
+ 'type': 'ir.actions.act_window',
+ 'name': 'Distribute Cost',
+ 'res_model': 'cost.distribution.wizard',
+ 'view_mode': 'form',
+ 'target': 'new',
+ 'context': {
+ 'order_id': self.order_id.id,
+ 'default_order_line_id': self.id,
+ 'default_price_subtotal': self.price_subtotal,
+ }
+ }
diff --git a/sale_extension/security/ir.model.access.csv b/sale_extension/security/ir.model.access.csv
new file mode 100644
index 00000000000..1f4f31dfaef
--- /dev/null
+++ b/sale_extension/security/ir.model.access.csv
@@ -0,0 +1,2 @@
+id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
+sale_extension.access_cost_distribution_wizard,access_cost_distribution_wizard,sale_extension.model_cost_distribution_wizard,base.group_user,1,1,1,1
diff --git a/sale_extension/views/sale_order_views.xml b/sale_extension/views/sale_order_views.xml
new file mode 100644
index 00000000000..ecc7091488a
--- /dev/null
+++ b/sale_extension/views/sale_order_views.xml
@@ -0,0 +1,18 @@
+
+
+
+
+ sale.order.view.form.inherit
+ sale.order
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sale_extension/wizard/__init__.py b/sale_extension/wizard/__init__.py
new file mode 100644
index 00000000000..d331964c545
--- /dev/null
+++ b/sale_extension/wizard/__init__.py
@@ -0,0 +1 @@
+from . import cost_distribution_wizard
diff --git a/sale_extension/wizard/cost_distribution_wizard.py b/sale_extension/wizard/cost_distribution_wizard.py
new file mode 100644
index 00000000000..e52582e175f
--- /dev/null
+++ b/sale_extension/wizard/cost_distribution_wizard.py
@@ -0,0 +1,52 @@
+from odoo import models, fields, api
+from odoo.exceptions import UserError
+
+class CostDistributionWizard(models.TransientModel):
+ _name = "cost.distribution.wizard"
+ _description = "A Wizard to distribute cost over other sales order lines."
+
+ order_line_id = fields.Many2one("sale.order.line", string="Order Line")
+ order_line_ids = fields.Many2many("sale.order.line", string="Order Lines")
+ order_line_cost = fields.Float(string="Cost to Distribute")
+ order_id = fields.Many2one("sale.order")
+ price_subtotal = fields.Float("Price Subtotal")
+
+ @api.model
+ def default_get(self, fields_list):
+ res = super(CostDistributionWizard, self).default_get(fields_list)
+ order_id = self.env.context.get("order_id")
+ default_order_line_id = self.env.context.get("default_order_line_id")
+ default_price_subtotal = self.env.context.get("default_price_subtotal", 0)
+
+ if order_id:
+ order = self.env["sale.order"].browse(order_id)
+ order_line_ids = order.order_line.ids
+ if default_order_line_id in order_line_ids:
+ order_line_ids.remove(default_order_line_id)
+
+ res.update({
+ "order_id": order_id,
+ "order_line_ids": [(6, 0, order_line_ids)],
+ "order_line_cost": default_price_subtotal,
+ })
+
+ distributed_cost = round(default_price_subtotal / len(order_line_ids), 2) if order_line_ids else 0
+ for line in self.env["sale.order.line"].browse(order_line_ids):
+ line.write({"distributed_cost": distributed_cost})
+
+ return res
+
+ def distribute_cost(self):
+ total_cost_distributed = sum(line.distributed_cost for line in self.order_line_ids)
+
+ if total_cost_distributed > self.price_subtotal:
+ raise UserError("Distributed price is greater than the distributable price.")
+
+ for line in self.order_line_ids:
+ line.price_subtotal += line.distributed_cost
+
+ original_order_line = self.env["sale.order.line"].browse(self.env.context.get("default_order_line_id"))
+ original_order_line.price_subtotal -= total_cost_distributed
+
+ if total_cost_distributed == original_order_line.price_subtotal:
+ pass
diff --git a/sale_extension/wizard/cost_distribution_wizard.xml b/sale_extension/wizard/cost_distribution_wizard.xml
new file mode 100644
index 00000000000..639fe4d74de
--- /dev/null
+++ b/sale_extension/wizard/cost_distribution_wizard.xml
@@ -0,0 +1,27 @@
+
+
+
+ cost.distribution.wizard.form
+ cost.distribution.wizard
+
+
+
+
+
diff --git a/website_appointment_filters/__init__.py b/website_appointment_filters/__init__.py
new file mode 100644
index 00000000000..e046e49fbe2
--- /dev/null
+++ b/website_appointment_filters/__init__.py
@@ -0,0 +1 @@
+from . import controllers
diff --git a/website_appointment_filters/__manifest__.py b/website_appointment_filters/__manifest__.py
new file mode 100644
index 00000000000..14224a0b6f7
--- /dev/null
+++ b/website_appointment_filters/__manifest__.py
@@ -0,0 +1,19 @@
+{
+ 'name': 'Website Appointment Filters',
+ 'version': '1.0',
+ 'depends': ['base', 'website_appointment', 'appointment_account_payment'],
+ 'author': 'Kishan B. Gajera',
+ 'category': 'Appointment',
+ 'description': """
+ A sample module to add filters in Appointment Website View
+ """,
+
+ 'application': True,
+ 'installable': True,
+
+ 'data': [
+ 'views/appointment_templates_appointment_filters.xml'
+ ],
+
+ 'license':'LGPL-3',
+}
diff --git a/website_appointment_filters/controllers/__init__.py b/website_appointment_filters/controllers/__init__.py
new file mode 100644
index 00000000000..12a7e529b67
--- /dev/null
+++ b/website_appointment_filters/controllers/__init__.py
@@ -0,0 +1 @@
+from . import main
diff --git a/website_appointment_filters/controllers/main.py b/website_appointment_filters/controllers/main.py
new file mode 100644
index 00000000000..6b2ec7f07e4
--- /dev/null
+++ b/website_appointment_filters/controllers/main.py
@@ -0,0 +1,73 @@
+from odoo import http
+from odoo.http import request
+from odoo.addons.website_appointment.controllers.appointment import WebsiteAppointment
+
+class WebsiteAppointmentFiltersController(WebsiteAppointment):
+ @http.route(['/appointment'], type='http', auth='public', website=True)
+ def appointment_type_index(self, page=1, **filters):
+ filtered_appointments_by_mode = set()
+ filtered_appointments_by_type = set()
+ filtered_appointments_by_schedule = set()
+
+ if 'mode' in filters:
+ if filters['mode'] == 'online':
+ for appointment in request.env['appointment.type'].search([('location_id', '=', None)]):
+ filtered_appointments_by_mode.add(appointment.id)
+ elif filters['mode'] == 'offline':
+ for appointment in request.env['appointment.type'].search([('location_id', '!=', None)]):
+ filtered_appointments_by_mode.add(appointment.id)
+ elif filters['mode'] == 'all':
+ for appointment in request.env['appointment.type'].search([]):
+ filtered_appointments_by_mode.add(appointment.id)
+ else:
+ for appointment in request.env['appointment.type'].search([]):
+ filtered_appointments_by_mode.add(appointment.id)
+
+ if 'type' in filters:
+ if filters['type'] == 'paid':
+ for appointment in request.env['appointment.type'].search([('has_payment_step', '=', 'true')]):
+ filtered_appointments_by_type.add(appointment.id)
+ elif filters['type'] == 'free':
+ for appointment in request.env['appointment.type'].search([('has_payment_step', '!=', 'null')]):
+ filtered_appointments_by_type.add(appointment.id)
+ elif filters['type'] == 'all':
+ for appointment in request.env['appointment.type'].search([]):
+ filtered_appointments_by_type.add(appointment.id)
+ else:
+ for appointment in request.env['appointment.type'].search([]):
+ filtered_appointments_by_type.add(appointment.id)
+
+ if 'schedule' in filters:
+ if filters['schedule'] == 'resources':
+ for appointment in request.env['appointment.type'].search([('schedule_based_on', '=', 'resources')]):
+ filtered_appointments_by_schedule.add(appointment.id)
+ elif filters['schedule'] == 'users':
+ for appointment in request.env['appointment.type'].search([('schedule_based_on', '=', 'users')]):
+ filtered_appointments_by_schedule.add(appointment.id)
+ elif filters['schedule'] == 'all':
+ for appointment in request.env['appointment.type'].search([]):
+ filtered_appointments_by_schedule.add(appointment.id)
+ else:
+ for appointment in request.env['appointment.type'].search([]):
+ filtered_appointments_by_schedule.add(appointment.id)
+
+ filtered_appointments_by_mode = set(map(lambda id: str(id), filtered_appointments_by_mode))
+ filtered_appointments_by_type = set(map(lambda id: str(id), filtered_appointments_by_type))
+ filtered_appointments_by_schedule = set(map(lambda id: str(id), filtered_appointments_by_schedule))
+
+ filters['filter_appointment_type_ids'] = f"[{','.join(filtered_appointments_by_mode & filtered_appointments_by_type & filtered_appointments_by_schedule)}]"
+
+ if 'mode' not in filters.keys():
+ filters['mode'] = 'all'
+ if 'type' not in filters.keys():
+ filters['type'] = 'all'
+ if 'schedule' not in filters.keys():
+ filters['schedule'] = 'all'
+
+
+ return super().appointment_type_index(**filters)
+
+ def _prepare_appointments_cards_data(self, page, appointment_types, **kwargs):
+ res = super()._prepare_appointments_cards_data(page, appointment_types, **kwargs)
+ res.update(kwargs)
+ return res
diff --git a/website_appointment_filters/views/appointment_templates_appointment_filters.xml b/website_appointment_filters/views/appointment_templates_appointment_filters.xml
new file mode 100644
index 00000000000..9cc9ac195b9
--- /dev/null
+++ b/website_appointment_filters/views/appointment_templates_appointment_filters.xml
@@ -0,0 +1,70 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+