mirror of
https://github.com/bringout/oca-ocb-sale.git
synced 2026-04-27 01:32:00 +02:00
19.0 vanilla
This commit is contained in:
parent
79f83631d5
commit
73afc09215
6267 changed files with 1534193 additions and 1130106 deletions
|
|
@ -1,13 +1,11 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import account_move
|
||||
from . import product
|
||||
from . import event_booth_registration
|
||||
from . import event_booth
|
||||
from . import event_booth_category
|
||||
from . import event_type_booth
|
||||
from . import product_product
|
||||
from . import product_template
|
||||
from . import sale_order
|
||||
from . import sale_order_line
|
||||
from . import sale_order_template_line
|
||||
from . import sale_order_template_option
|
||||
|
|
|
|||
|
|
@ -17,10 +17,10 @@ class EventBooth(models.Model):
|
|||
groups='sales_team.group_sale_salesman', copy=False)
|
||||
sale_order_line_id = fields.Many2one(
|
||||
'sale.order.line', string='Final Sale Order Line', ondelete='set null',
|
||||
readonly=True, states={'available': [('readonly', False)]},
|
||||
readonly=False, index='btree_not_null',
|
||||
groups='sales_team.group_sale_salesman', copy=False)
|
||||
sale_order_id = fields.Many2one(
|
||||
related='sale_order_line_id.order_id', store='True', readonly=True,
|
||||
related='sale_order_line_id.order_id', store='True', readonly=True, index='btree_not_null',
|
||||
groups='sales_team.group_sale_salesman')
|
||||
is_paid = fields.Boolean('Is Paid', copy=False)
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,9 @@
|
|||
|
||||
import logging
|
||||
|
||||
from odoo import api, fields, models
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.addons.product.models.product_template import PRICE_CONTEXT_KEYS
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
|
@ -16,21 +18,35 @@ class EventBoothCategory(models.Model):
|
|||
|
||||
product_id = fields.Many2one(
|
||||
'product.product', string='Product', required=True,
|
||||
domain=[('detailed_type', '=', 'event_booth')], default=_default_product_id,
|
||||
domain=[('service_tracking', '=', 'event_booth')], default=_default_product_id,
|
||||
groups="event.group_event_registration_desk")
|
||||
price = fields.Float(
|
||||
string='Price', compute='_compute_price', digits='Product Price', readonly=False,
|
||||
string='Price', compute='_compute_price', min_display_digits='Product Price', readonly=False,
|
||||
store=True, groups="event.group_event_registration_desk")
|
||||
price_incl = fields.Float(
|
||||
string='Price incl', compute='_compute_price_incl', min_display_digits='Product Price', readonly=False,
|
||||
groups="event.group_event_registration_desk")
|
||||
currency_id = fields.Many2one(related='product_id.currency_id', groups="event.group_event_registration_desk")
|
||||
price_reduce = fields.Float(
|
||||
string='Price Reduce', compute='_compute_price_reduce',
|
||||
compute_sudo=True, digits='Product Price', groups="event.group_event_registration_desk")
|
||||
compute_sudo=True, min_display_digits='Product Price', groups="event.group_event_registration_desk")
|
||||
price_reduce_taxinc = fields.Float(
|
||||
string='Price Reduce Tax inc', compute='_compute_price_reduce_taxinc',
|
||||
compute_sudo=True
|
||||
)
|
||||
image_1920 = fields.Image(compute='_compute_image_1920', readonly=False, store=True)
|
||||
|
||||
@api.constrains('product_id')
|
||||
def _check_service_tracking(self):
|
||||
for record in self:
|
||||
if record.product_id and record.product_id.service_tracking != 'event_booth':
|
||||
raise ValidationError(
|
||||
_(
|
||||
'The product, %(product_name)s , is used for Event Booth, it must have service_tracking set to "Event Booth".',
|
||||
product_name=record.product_id.name
|
||||
)
|
||||
)
|
||||
|
||||
@api.depends('product_id')
|
||||
def _compute_image_1920(self):
|
||||
for category in self:
|
||||
|
|
@ -44,29 +60,30 @@ class EventBoothCategory(models.Model):
|
|||
if category.product_id and category.product_id.list_price:
|
||||
category.price = category.product_id.list_price + category.product_id.price_extra
|
||||
|
||||
@api.depends_context('pricelist', 'quantity')
|
||||
@api.depends('product_id', 'product_id.taxes_id', 'price')
|
||||
def _compute_price_incl(self):
|
||||
for category in self:
|
||||
if category.product_id and category.price:
|
||||
tax_ids = category.product_id.taxes_id
|
||||
taxes = tax_ids.compute_all(category.price, category.currency_id, 1.0, product=category.product_id)
|
||||
category.price_incl = taxes['total_included']
|
||||
else:
|
||||
category.price_incl = 0
|
||||
|
||||
@api.depends_context(*PRICE_CONTEXT_KEYS)
|
||||
@api.depends('product_id', 'price')
|
||||
def _compute_price_reduce(self):
|
||||
for category in self:
|
||||
product = category.product_id
|
||||
pricelist = product.product_tmpl_id._get_contextual_pricelist()
|
||||
lst_price = product.currency_id._convert(
|
||||
product.lst_price,
|
||||
pricelist.currency_id,
|
||||
self.env.company,
|
||||
fields.Datetime.now(),
|
||||
round=False,
|
||||
)
|
||||
discount = (lst_price - product._get_contextual_price()) / lst_price if lst_price else 0.0
|
||||
category.price_reduce = (1.0 - discount) * category.price
|
||||
contextual_discount = category.product_id._get_contextual_discount()
|
||||
category.price_reduce = (1.0 - contextual_discount) * category.price
|
||||
|
||||
@api.depends_context('pricelist', 'quantity')
|
||||
@api.depends_context(*PRICE_CONTEXT_KEYS)
|
||||
@api.depends('product_id', 'price_reduce')
|
||||
def _compute_price_reduce_taxinc(self):
|
||||
for category in self:
|
||||
tax_ids = category.product_id.taxes_id
|
||||
taxes = tax_ids.compute_all(category.price_reduce, category.currency_id, 1.0, product=category.product_id)
|
||||
category.price_reduce_taxinc = taxes['total_included']
|
||||
category.price_reduce_taxinc = taxes['total_included'] or 0
|
||||
|
||||
def _init_column(self, column_name):
|
||||
""" Initialize product_id for existing columns when installing sale
|
||||
|
|
@ -89,10 +106,11 @@ class EventBoothCategory(models.Model):
|
|||
else:
|
||||
product_id = self.env['product.product'].create({
|
||||
'name': 'Generic Event Booth Product',
|
||||
'categ_id': self.env.ref('event_sale.product_category_events').id,
|
||||
'categ_id': self.env.ref('event_product.product_category_events').id,
|
||||
'list_price': 100,
|
||||
'standard_price': 0,
|
||||
'detailed_type': 'event_booth',
|
||||
'type': 'service',
|
||||
'service_tracking': 'event_booth',
|
||||
'invoice_policy': 'order',
|
||||
}).id
|
||||
self.env['ir.model.data'].create({
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from markupsafe import Markup
|
||||
from odoo import api, fields, models, _
|
||||
|
||||
|
||||
|
|
@ -11,17 +12,18 @@ class EventBoothRegistration(models.Model):
|
|||
_name = 'event.booth.registration'
|
||||
_description = 'Event Booth Registration'
|
||||
|
||||
sale_order_line_id = fields.Many2one('sale.order.line', string='Sale Order Line', required=True, ondelete='cascade')
|
||||
event_booth_id = fields.Many2one('event.booth', string='Booth', required=True)
|
||||
sale_order_line_id = fields.Many2one('sale.order.line', string='Sale Order Line', required=True, index=True, ondelete='cascade')
|
||||
event_booth_id = fields.Many2one('event.booth', string='Booth', required=True, index=True)
|
||||
partner_id = fields.Many2one(
|
||||
'res.partner', related='sale_order_line_id.order_partner_id', store=True)
|
||||
contact_name = fields.Char(string='Contact Name', compute='_compute_contact_name', readonly=False, store=True)
|
||||
contact_email = fields.Char(string='Contact Email', compute='_compute_contact_email', readonly=False, store=True)
|
||||
contact_phone = fields.Char(string='Contact Phone', compute='_compute_contact_phone', readonly=False, store=True)
|
||||
contact_mobile = fields.Char(string='Contact Mobile', compute='_compute_contact_mobile', readonly=False, store=True)
|
||||
|
||||
_sql_constraints = [('unique_registration', 'unique(sale_order_line_id, event_booth_id)',
|
||||
'There can be only one registration for a booth by sale order line')]
|
||||
_unique_registration = models.Constraint(
|
||||
'unique(sale_order_line_id, event_booth_id)',
|
||||
'There can be only one registration for a booth by sale order line',
|
||||
)
|
||||
|
||||
@api.depends('partner_id')
|
||||
def _compute_contact_name(self):
|
||||
|
|
@ -41,15 +43,9 @@ class EventBoothRegistration(models.Model):
|
|||
if not registration.contact_phone:
|
||||
registration.contact_phone = registration.partner_id.phone or False
|
||||
|
||||
@api.depends('partner_id')
|
||||
def _compute_contact_mobile(self):
|
||||
for registration in self:
|
||||
if not registration.contact_mobile:
|
||||
registration.contact_mobile = registration.partner_id.mobile or False
|
||||
|
||||
@api.model
|
||||
def _get_fields_for_booth_confirmation(self):
|
||||
return ['sale_order_line_id', 'partner_id', 'contact_name', 'contact_email', 'contact_phone', 'contact_mobile']
|
||||
return ['sale_order_line_id', 'partner_id', 'contact_name', 'contact_email', 'contact_phone']
|
||||
|
||||
def action_confirm(self):
|
||||
for registration in self:
|
||||
|
|
@ -61,9 +57,9 @@ class EventBoothRegistration(models.Model):
|
|||
self._cancel_pending_registrations()
|
||||
|
||||
def _cancel_pending_registrations(self):
|
||||
body = '<p>%(message)s: <ul>%(booth_names)s</ul></p>' % {
|
||||
body = Markup('<p>%(message)s: <ul>%(booth_names)s</ul></p>') % {
|
||||
'message': _('Your order has been cancelled because the following booths have been reserved'),
|
||||
'booth_names': ''.join('<li>%s</li>' % booth.display_name for booth in self.event_booth_id)
|
||||
'booth_names': Markup().join(Markup('<li>%s</li>') % booth.display_name for booth in self.event_booth_id)
|
||||
}
|
||||
other_registrations = self.search([
|
||||
('event_booth_id', 'in', self.event_booth_id.ids),
|
||||
|
|
|
|||
|
|
@ -1,31 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class ProductTemplate(models.Model):
|
||||
_inherit = 'product.template'
|
||||
|
||||
detailed_type = fields.Selection(selection_add=[
|
||||
('event_booth', 'Event Booth'),
|
||||
], ondelete={'event_booth': 'set service'})
|
||||
|
||||
@api.onchange('detailed_type')
|
||||
def _onchange_type_event_booth(self):
|
||||
if self.detailed_type == 'event_booth':
|
||||
self.invoice_policy = 'order'
|
||||
|
||||
def _detailed_type_mapping(self):
|
||||
type_mapping = super()._detailed_type_mapping()
|
||||
type_mapping['event_booth'] = 'service'
|
||||
return type_mapping
|
||||
|
||||
|
||||
class Product(models.Model):
|
||||
_inherit = 'product.product'
|
||||
|
||||
@api.onchange('detailed_type')
|
||||
def _onchange_type_event_booth(self):
|
||||
if self.detailed_type == 'event_booth':
|
||||
self.invoice_policy = 'order'
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
from odoo import _, api, models
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
|
||||
class ProductProduct(models.Model):
|
||||
_inherit = 'product.product'
|
||||
|
||||
@api.onchange('service_tracking')
|
||||
def _onchange_type_event_booth(self):
|
||||
if self.service_tracking == 'event_booth':
|
||||
self.invoice_policy = 'order'
|
||||
|
||||
@api.constrains('service_tracking')
|
||||
def _check_service_tracking_for_event_booths(self):
|
||||
if product_not_event_booth := self.filtered(lambda p: p.service_tracking != 'event_booth'):
|
||||
booth_category = self.env['event.booth.category'].search([('product_id', 'in', product_not_event_booth.ids)], limit=1)
|
||||
if booth_category:
|
||||
raise ValidationError(
|
||||
_(
|
||||
"You cannot change the service_tracking of the product %(product_name)s because it is already assigned "
|
||||
"to %(booth_category_name)s. The service_tracking must remain 'event_booth'.",
|
||||
product_name=product_not_event_booth.name,
|
||||
booth_category_name=booth_category.name,
|
||||
)
|
||||
)
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
|
||||
class ProductTemplate(models.Model):
|
||||
_inherit = 'product.template'
|
||||
|
||||
service_tracking = fields.Selection(selection_add=[
|
||||
('event_booth', 'Event Booth'),
|
||||
], ondelete={'event_booth': 'set default'})
|
||||
|
||||
def _prepare_service_tracking_tooltip(self):
|
||||
if self.service_tracking == 'event_booth':
|
||||
return _("Mark the selected Booth as Unavailable.")
|
||||
return super()._prepare_service_tracking_tooltip()
|
||||
|
||||
@api.onchange('service_tracking')
|
||||
def _onchange_type_event_booth(self):
|
||||
if self.service_tracking == 'event_booth':
|
||||
self.invoice_policy = 'order'
|
||||
|
||||
def _service_tracking_blacklist(self):
|
||||
return super()._service_tracking_blacklist() + ['event_booth']
|
||||
|
||||
@api.constrains('service_tracking')
|
||||
def _check_service_tracking_for_event_booths(self):
|
||||
""" Prevent changing the service_tracking field if the product template or any of its variants
|
||||
is linked to an Event Booth Category.
|
||||
"""
|
||||
if product_variants_not_event_booth := self.product_variant_ids.filtered(lambda p: p.service_tracking != 'event_booth'):
|
||||
linked_booth_category = self.env['event.booth.category'].sudo().search([
|
||||
('product_id', 'in', product_variants_not_event_booth.ids)
|
||||
], limit=1)
|
||||
if linked_booth_category:
|
||||
raise ValidationError(
|
||||
_(
|
||||
'The "service_tracking" for the product template, %(product_template_name)s cannot be changed because '
|
||||
'one of its variants is assigned to the Event Booth Category, %(event_booth_category_name)s. '
|
||||
'The service_tracking must remain "Event Booth".',
|
||||
product_template_name=linked_booth_category.product_id.name,
|
||||
event_booth_category_name=linked_booth_category.name,
|
||||
)
|
||||
)
|
||||
|
|
@ -1,7 +1,8 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import api, fields, models
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.exceptions import ValidationError
|
||||
from odoo.fields import Domain
|
||||
|
||||
|
||||
class SaleOrder(models.Model):
|
||||
|
|
@ -12,20 +13,23 @@ class SaleOrder(models.Model):
|
|||
|
||||
@api.depends('event_booth_ids')
|
||||
def _compute_event_booth_count(self):
|
||||
if self.ids:
|
||||
slot_data = self.env['event.booth']._read_group(
|
||||
[('sale_order_id', 'in', self.ids)],
|
||||
['sale_order_id'], ['sale_order_id']
|
||||
)
|
||||
slot_mapped = dict((data['sale_order_id'][0], data['sale_order_id_count']) for data in slot_data)
|
||||
else:
|
||||
slot_mapped = dict()
|
||||
slot_data = self.env['event.booth']._read_group(
|
||||
[('sale_order_id', 'in', self.ids)],
|
||||
['sale_order_id'], ['__count'],
|
||||
)
|
||||
slot_mapped = {sale_order.id: count for sale_order, count in slot_data}
|
||||
for so in self:
|
||||
so.event_booth_count = slot_mapped.get(so.id, 0)
|
||||
|
||||
def action_confirm(self):
|
||||
res = super(SaleOrder, self).action_confirm()
|
||||
for so in self:
|
||||
if not any(line.service_tracking == 'event_booth' for line in so.order_line):
|
||||
continue
|
||||
so_lines_missing_booth = so.order_line.filtered(lambda line: line.service_tracking == 'event_booth' and not line.event_booth_pending_ids)
|
||||
if so_lines_missing_booth:
|
||||
so_lines_descriptions = "".join(f"\n- {so_line_description.name}" for so_line_description in so_lines_missing_booth)
|
||||
raise ValidationError(_("Please make sure all your event-booth related lines are configured before confirming this order:%s", so_lines_descriptions))
|
||||
so.order_line._update_event_booths()
|
||||
return res
|
||||
|
||||
|
|
@ -33,3 +37,6 @@ class SaleOrder(models.Model):
|
|||
action = self.env['ir.actions.act_window']._for_xml_id('event_booth.event_booth_action')
|
||||
action['domain'] = [('sale_order_id', 'in', self.ids)]
|
||||
return action
|
||||
|
||||
def _get_product_catalog_domain(self):
|
||||
return super()._get_product_catalog_domain() & Domain('service_tracking', '!=', 'event_booth')
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.exceptions import ValidationError
|
||||
from odoo.fields import Domain
|
||||
|
||||
|
||||
class SaleOrderLine(models.Model):
|
||||
|
|
@ -17,19 +18,6 @@ class SaleOrderLine(models.Model):
|
|||
event_booth_registration_ids = fields.One2many(
|
||||
'event.booth.registration', 'sale_order_line_id', string='Confirmed Registration')
|
||||
event_booth_ids = fields.One2many('event.booth', 'sale_order_line_id', string='Confirmed Booths')
|
||||
is_event_booth = fields.Boolean(compute='_compute_is_event_booth')
|
||||
|
||||
@api.depends('product_id.type')
|
||||
def _compute_is_event_booth(self):
|
||||
for record in self:
|
||||
record.is_event_booth = record.product_id.detailed_type == 'event_booth'
|
||||
|
||||
@api.depends('event_booth_ids')
|
||||
def _compute_name_short(self):
|
||||
wbooth = self.filtered(lambda line: line.event_booth_pending_ids)
|
||||
for record in wbooth:
|
||||
record.name_short = record.event_booth_pending_ids.event_id.name
|
||||
super(SaleOrderLine, self - wbooth)._compute_name_short()
|
||||
|
||||
@api.depends('event_booth_registration_ids')
|
||||
def _compute_event_booth_pending_ids(self):
|
||||
|
|
@ -55,6 +43,8 @@ class SaleOrderLine(models.Model):
|
|||
} for booth in selected_booths - existing_booths])
|
||||
|
||||
def _search_event_booth_pending_ids(self, operator, value):
|
||||
if operator in Domain.NEGATIVE_OPERATORS:
|
||||
return NotImplemented
|
||||
return [('event_booth_registration_ids.event_booth_id', operator, value)]
|
||||
|
||||
@api.constrains('event_booth_registration_ids')
|
||||
|
|
@ -74,7 +64,7 @@ class SaleOrderLine(models.Model):
|
|||
if self.event_booth_pending_ids and (not self.event_id or self.event_id != self.event_booth_pending_ids.event_id):
|
||||
self.event_booth_pending_ids = None
|
||||
|
||||
@api.depends('event_booth_registration_ids.event_booth_id')
|
||||
@api.depends('event_booth_pending_ids')
|
||||
def _compute_name(self):
|
||||
"""Override to add the compute dependency.
|
||||
|
||||
|
|
@ -83,7 +73,7 @@ class SaleOrderLine(models.Model):
|
|||
super()._compute_name()
|
||||
|
||||
def _update_event_booths(self, set_paid=False):
|
||||
for so_line in self.filtered('is_event_booth'):
|
||||
for so_line in self.filtered(lambda sol: sol.product_id.service_tracking == 'event_booth'):
|
||||
if so_line.event_booth_pending_ids and not so_line.event_booth_ids:
|
||||
unavailable = so_line.event_booth_pending_ids.filtered(lambda booth: not booth.is_available)
|
||||
if unavailable:
|
||||
|
|
@ -100,17 +90,20 @@ class SaleOrderLine(models.Model):
|
|||
return self.event_booth_pending_ids._get_booth_multiline_description()
|
||||
return super()._get_sale_order_line_multiline_description_sale()
|
||||
|
||||
def _use_template_name(self):
|
||||
""" We do not want configured description to get rewritten by template default"""
|
||||
if self.event_booth_pending_ids:
|
||||
return False
|
||||
return super()._use_template_name()
|
||||
|
||||
def _get_display_price(self):
|
||||
if self.event_booth_pending_ids and self.event_id:
|
||||
company = self.event_id.company_id or self.env.company
|
||||
currency = company.currency_id
|
||||
pricelist = self.order_id.pricelist_id
|
||||
if pricelist.discount_policy == "with_discount":
|
||||
total_price = sum([booth.booth_category_id.with_context(pricelist=pricelist.id).price_reduce for booth in self.event_booth_pending_ids])
|
||||
if not self.pricelist_item_id._show_discount():
|
||||
event_booths = self.event_booth_pending_ids.with_context(**self._get_pricelist_price_context())
|
||||
total_price = sum(booth.booth_category_id.price_reduce for booth in event_booths)
|
||||
else:
|
||||
total_price = sum([booth.price for booth in self.event_booth_pending_ids])
|
||||
return currency._convert(
|
||||
total_price, self.order_id.currency_id,
|
||||
self.order_id.company_id or self.env.company.id,
|
||||
self.order_id.date_order or fields.Date.today())
|
||||
total_price = sum(booth.price for booth in self.event_booth_pending_ids)
|
||||
|
||||
return self._convert_to_sol_currency(total_price, company.currency_id)
|
||||
return super()._get_display_price()
|
||||
|
|
|
|||
|
|
@ -1,6 +0,0 @@
|
|||
from odoo import fields, models
|
||||
|
||||
class SaleOrderTemplateLine(models.Model):
|
||||
_inherit = "sale.order.template.line"
|
||||
|
||||
product_id = fields.Many2one(domain="[('sale_ok', '=', True), ('detailed_type', 'not in', ['event', 'event_booth']), ('company_id', 'in', [company_id, False])]")
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
from odoo import fields, models
|
||||
|
||||
class SaleOrderTemplateOption(models.Model):
|
||||
_inherit = "sale.order.template.option"
|
||||
|
||||
product_id = fields.Many2one(domain="[('sale_ok', '=', True), ('detailed_type', 'not in', ['event', 'event_booth']), ('company_id', 'in', [company_id, False])]")
|
||||
Loading…
Add table
Add a link
Reference in a new issue