Initial commit: Sale packages

This commit is contained in:
Ernad Husremovic 2025-08-29 15:20:49 +02:00
commit 14e3d26998
6469 changed files with 2479670 additions and 0 deletions

View file

@ -0,0 +1,6 @@
# -*- coding: utf-8 -*-
from . import product
from . import product_pricelist
from . import sale_order
from . import website

View file

@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
from odoo import api, fields, models
# defined for access rules
class Product(models.Model):
_inherit = 'product.product'
event_ticket_ids = fields.One2many('event.event.ticket', 'product_id', string='Event Tickets')
def _is_add_to_cart_allowed(self):
# Allow adding event tickets to the cart regardless of product's rules
self.ensure_one()
res = super()._is_add_to_cart_allowed()
return res or any(event.website_published for event in self.event_ticket_ids.event_id)
class ProductTemplate(models.Model):
_inherit = 'product.template'
@api.model
def _get_product_types_allow_zero_price(self):
return super()._get_product_types_allow_zero_price() + ["event"]

View file

@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
from odoo import _, api, models
class PricelistItem(models.Model):
_inherit = "product.pricelist.item"
@api.onchange('applied_on', 'product_id', 'product_tmpl_id', 'min_quantity')
def _onchange_event_sale_warning(self):
if self.min_quantity > 0:
msg = ''
if self.applied_on == '3_global' or self.applied_on == '2_product_category':
msg = _("A pricelist item with a positive min. quantity will not be applied to the event tickets products.")
elif ((self.applied_on == '1_product' and self.product_tmpl_id.detailed_type == 'event') or
(self.applied_on == '0_product_variant' and self.product_id.detailed_type == 'event')):
msg = _("A pricelist item with a positive min. quantity cannot be applied to this event tickets product.")
if msg:
return {'warning':
{
'title': _("Warning"),
'message': msg
}
}

View file

@ -0,0 +1,114 @@
# -*- coding: utf-8 -*-
from odoo import api, models, _
from odoo.exceptions import UserError
class SaleOrder(models.Model):
_inherit = "sale.order"
def _cart_find_product_line(self, product_id=None, line_id=None, event_ticket_id=False, **kwargs):
lines = super()._cart_find_product_line(product_id, line_id, **kwargs)
if line_id or not event_ticket_id:
return lines
return lines.filtered(
lambda line: line.event_ticket_id.id == event_ticket_id
)
def _verify_updated_quantity(self, order_line, product_id, new_qty, event_ticket_id=False, **kwargs):
"""Restrict quantity updates for event tickets according to available seats."""
new_qty, warning = super()._verify_updated_quantity(order_line, product_id, new_qty, **kwargs)
if not event_ticket_id:
if not order_line.event_ticket_id or new_qty < order_line.product_uom_qty:
return new_qty, warning
else:
return order_line.product_uom_qty, _("You cannot raise manually the event ticket quantity in your cart")
# Adding new ticket to the cart (might be automatically linked to an existing line)
ticket = self.env['event.event.ticket'].browse(event_ticket_id).exists()
if not ticket:
raise UserError(_("The provided ticket doesn't exist"))
# TODO TDE consider full cart qty and not only added qty
# if event seats are not auto confirmed.
# Since created registrations are automatically reserved
# We should only consider new added qty and not full quantity
# when checking for seat availability
existing_qty = order_line.product_uom_qty if order_line else 0
qty_added = new_qty - existing_qty
warning = ''
if ticket.seats_limited and ticket.seats_available <= 0:
# Remove existing line if exists and do not add a new one
# if no ticket is available anymore
new_qty = existing_qty
warning = _(
'Sorry, The %(ticket)s tickets for the %(event)s event are sold out.',
ticket=ticket.name,
event=ticket.event_id.name,
)
elif ticket.seats_limited and qty_added > ticket.seats_available:
new_qty = existing_qty + ticket.seats_available
warning = _(
'Sorry, only %(remaining_seats)d seats are still available for the %(ticket)s ticket for the %(event)s event.',
remaining_seats=ticket.seats_available,
ticket=ticket.name,
event=ticket.event_id.name,
)
return new_qty, warning
def _prepare_order_line_values(self, product_id, quantity, event_ticket_id=False, **kwargs):
"""Add corresponding event to the SOline creation values (if ticket is provided)."""
values = super()._prepare_order_line_values(product_id, quantity, **kwargs)
if not event_ticket_id:
return values
ticket = self.env['event.event.ticket'].browse(event_ticket_id)
if ticket.product_id.id != product_id:
raise UserError(_("The ticket doesn't match with this product."))
values['event_id'] = ticket.event_id.id
values['event_ticket_id'] = ticket.id
return values
def _update_cart_line_values(self, order_line, update_values):
"""Remove event registrations on quantity decrease."""
old_qty = order_line.product_uom_qty
super()._update_cart_line_values(order_line, update_values)
if not order_line.event_ticket_id:
return
new_qty = order_line.product_uom_qty
if new_qty < old_qty:
attendees = self.env['event.registration'].search([
('state', '!=', 'cancel'),
('sale_order_id', '=', self.id),
('event_ticket_id', '=', order_line.event_ticket_id.id),
], offset=new_qty, limit=(old_qty - new_qty), order='create_date asc')
attendees.action_cancel()
def _filter_can_send_abandoned_cart_mail(self):
"""Prevent carts with expired/sold out tickets from being subject of reminder emails."""
return super()._filter_can_send_abandoned_cart_mail().filtered(
lambda so: all(ticket.sale_available for ticket in so.order_line.event_ticket_id)
)
class SaleOrderLine(models.Model):
_inherit = "sale.order.line"
@api.depends('product_id.display_name', 'event_ticket_id.display_name')
def _compute_name_short(self):
""" If the sale order line concerns a ticket, we don't want the product name, but the ticket name instead.
"""
super(SaleOrderLine, self)._compute_name_short()
for record in self:
if record.event_ticket_id:
record.name_short = record.event_ticket_id.display_name

View file

@ -0,0 +1,11 @@
# -*- coding: utf-8 -*-
from odoo import models
class Website(models.Model):
_inherit = 'website'
def sale_product_domain(self):
# remove product event from the website content grid and list view (not removed in detail view)
return ['&'] + super(Website, self).sale_product_domain() + [('detailed_type', '!=', 'event')]