diff --git a/addons/event/data/event_registration_demo.xml b/addons/event/data/event_registration_demo.xml index e4197572a045306e0a510a8e39f263e731a11cd2..094517d7a6397eafd16452d83d467daff57356d8 100644 --- a/addons/event/data/event_registration_demo.xml +++ b/addons/event/data/event_registration_demo.xml @@ -2,16 +2,19 @@ <odoo><data> <!-- Design fair --> <record id="event_registration_0_0" model="event.registration"> + <field name="create_date" eval="DateTime.now() - relativedelta(days=2)"/> <field name="event_id" ref="event.event_0"/> <field name="event_ticket_id" ref="event.event_0_ticket_1"/> <field name="partner_id" ref="base.res_partner_address_1"/> </record> <record id="event_registration_0_1" model="event.registration"> + <field name="create_date" eval="DateTime.now() - relativedelta(days=2)"/> <field name="event_id" ref="event.event_0"/> <field name="event_ticket_id" ref="event.event_0_ticket_1"/> <field name="partner_id" ref="base.res_partner_address_2"/> </record> <record id="event_registration_0_2" model="event.registration"> + <field name="create_date" eval="DateTime.now() - relativedelta(days=2)"/> <field name="event_id" ref="event.event_0"/> <field name="event_ticket_id" ref="event.event_0_ticket_0"/> <field name="name">Tucker Carlson</field> @@ -51,16 +54,19 @@ <!-- Conference for architects --> <record id="event_registration_2_0" model="event.registration"> + <field name="create_date" eval="DateTime.now() - relativedelta(days=0.5)"/> <field name="event_id" ref="event.event_2"/> <field name="event_ticket_id" ref="event.event_2_ticket_1"/> <field name="partner_id" ref="base.res_partner_address_1"/> </record> <record id="event_registration_2_1" model="event.registration"> + <field name="create_date" eval="DateTime.now() - relativedelta(days=0.5)"/> <field name="event_id" ref="event.event_2"/> <field name="event_ticket_id" ref="event.event_2_ticket_1"/> <field name="partner_id" ref="base.res_partner_address_2"/> </record> <record id="event_registration_2_2" model="event.registration"> + <field name="create_date" eval="DateTime.now() - relativedelta(days=0.5)"/> <field name="event_id" ref="event.event_2"/> <field name="event_ticket_id" ref="event.event_2_ticket_2"/> <field name="name">Piers Morgan</field> @@ -68,11 +74,13 @@ <field name="partner_id" eval="False"/> </record> <record id="event_registration_2_3" model="event.registration"> + <field name="create_date" eval="DateTime.now() - relativedelta(days=1)"/> <field name="event_id" ref="event.event_2"/> <field name="event_ticket_id" ref="event.event_2_ticket_1"/> <field name="partner_id" ref="base.res_partner_address_3"/> </record> <record id="event_registration_2_4" model="event.registration"> + <field name="create_date" eval="DateTime.now() - relativedelta(days=1)"/> <field name="event_id" ref="event.event_2"/> <field name="event_ticket_id" ref="event.event_2_ticket_1"/> <field name="partner_id" ref="base.res_partner_address_4"/> @@ -110,16 +118,19 @@ <!-- Business Workshop --> <record id="event_registration_4_0" model="event.registration"> + <field name="create_date" eval="DateTime.now() - relativedelta(days=8)"/> <field name="event_id" ref="event.event_4"/> <field name="event_ticket_id" ref="event.event_4_ticket_0"/> <field name="partner_id" ref="base.res_partner_address_7"/> </record> <record id="event_registration_4_1" model="event.registration"> + <field name="create_date" eval="DateTime.now() - relativedelta(days=7)"/> <field name="event_id" ref="event.event_4"/> <field name="event_ticket_id" ref="event.event_4_ticket_0"/> <field name="partner_id" ref="base.res_partner_address_13"/> </record> <record id="event_registration_4_2" model="event.registration"> + <field name="create_date" eval="DateTime.now() - relativedelta(days=7)"/> <field name="event_id" ref="event.event_4"/> <field name="event_ticket_id" ref="event.event_4_ticket_0"/> <field name="partner_id" ref="base.res_partner_address_14"/> diff --git a/addons/event_sale/__init__.py b/addons/event_sale/__init__.py index 35e7c9600c556ac0a37452da226bd559cf1e5a03..de3c67453a778d3d4bd7a2aff54380057fd5dd14 100644 --- a/addons/event_sale/__init__.py +++ b/addons/event_sale/__init__.py @@ -1,4 +1,6 @@ # -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. from . import models +from . import report from . import wizard diff --git a/addons/event_sale/__manifest__.py b/addons/event_sale/__manifest__.py index 2b08fa36c0a23b60302b5625e853ee89c452d727..c114a046c854d26b89001b46dba3998e4fb947db 100644 --- a/addons/event_sale/__manifest__.py +++ b/addons/event_sale/__manifest__.py @@ -27,12 +27,18 @@ this event. 'data/event_sale_data.xml', 'data/mail_data.xml', 'report/event_event_templates.xml', + 'report/event_sale_report_views.xml', 'security/ir.model.access.csv', + 'security/ir_rule.xml', 'security/event_security.xml', 'wizard/event_edit_registration.xml', 'wizard/event_configurator_views.xml', ], - 'demo': ['data/event_demo.xml'], + 'demo': [ + 'data/event_sale_demo.xml', + 'data/event_demo.xml', # needs event_sale_demo + 'data/event_registration_demo.xml', # needs event_sale_demo + ], 'installable': True, 'auto_install': True, 'assets': { diff --git a/addons/event_sale/data/event_demo.xml b/addons/event_sale/data/event_demo.xml index caac9c658dd6c2022204bc25263308189267d61c..690807a7609f36e9ca5d5d8e82a8c17318a68ee9 100644 --- a/addons/event_sale/data/event_demo.xml +++ b/addons/event_sale/data/event_demo.xml @@ -6,35 +6,35 @@ <field name="price">0</field> </record> <record id="event.event_0_ticket_1" model="event.event.ticket"> - <field name="product_id" ref="product_product_event"/> + <field name="product_id" ref="product_product_event_standard"/> <field name="price">1000.0</field> </record> <record id="event.event_0_ticket_2" model="event.event.ticket"> - <field name="product_id" ref="product_product_event"/> + <field name="product_id" ref="product_product_event_vip"/> <field name="price">1500.0</field> </record> <record id="event.event_2_ticket_1" model="event.event.ticket"> - <field name="product_id" ref="product_product_event"/> + <field name="product_id" ref="product_product_event_standard"/> <field name="price">1000.0</field> </record> <record id="event.event_2_ticket_2" model="event.event.ticket"> - <field name="product_id" ref="product_product_event"/> + <field name="product_id" ref="product_product_event_vip"/> <field name="price">1500.0</field> </record> <record id="event.event_4_ticket_0" model="event.event.ticket"> - <field name="product_id" ref="product_product_event"/> + <field name="product_id" ref="product_product_event_standard"/> <field name="price">99.0</field> </record> <record id="event.event_7_ticket_1" model="event.event.ticket"> - <field name="product_id" ref="product_product_event"/> + <field name="product_id" ref="product_product_event_standard"/> <field name="price">0.0</field> </record> <record id="event.event_7_ticket_2" model="event.event.ticket"> - <field name="product_id" ref="product_product_event"/> - <field name="price">1000.0</field> + <field name="product_id" ref="product_product_event_vip"/> + <field name="price">0.0</field> </record> </odoo> diff --git a/addons/event_sale/data/event_registration_demo.xml b/addons/event_sale/data/event_registration_demo.xml new file mode 100644 index 0000000000000000000000000000000000000000..fa1f4a618b98429a4a2392c147c7df6a3a29e729 --- /dev/null +++ b/addons/event_sale/data/event_registration_demo.xml @@ -0,0 +1,70 @@ +<?xml version="1.0"?> +<odoo><data> + <!-- Design fair --> + <record id="event.event_registration_0_0" model="event.registration"> + <field name="sale_order_id" ref="event_sale.event_0_sale_order_0"/> + <field name="sale_order_line_id" ref="event_sale.event_0_sale_order_0_line_0"/> + </record> + <record id="event.event_registration_0_1" model="event.registration"> + <field name="sale_order_id" ref="event_sale.event_0_sale_order_0"/> + <field name="sale_order_line_id" ref="event_sale.event_0_sale_order_0_line_0"/> + </record> + <record id="event.event_registration_0_2" model="event.registration"> + <field name="sale_order_id" ref="event_sale.event_0_sale_order_0"/> + <field name="sale_order_line_id" ref="event_sale.event_0_sale_order_0_line_1"/> + </record> + + <!-- Conference for architects --> + <record id="event.event_registration_2_0" model="event.registration"> + <field name="sale_order_id" ref="event_sale.event_2_sale_order_0"/> + <field name="sale_order_line_id" ref="event_sale.event_2_sale_order_0_line_0"/> + </record> + <record id="event.event_registration_2_1" model="event.registration"> + <field name="sale_order_id" ref="event_sale.event_2_sale_order_0"/> + <field name="sale_order_line_id" ref="event_sale.event_2_sale_order_0_line_0"/> + </record> + <record id="event.event_registration_2_2" model="event.registration"> + <field name="sale_order_id" ref="event_sale.event_2_sale_order_0"/> + <field name="sale_order_line_id" ref="event_sale.event_2_sale_order_0_line_1"/> + </record> + <record id="event.event_registration_2_3" model="event.registration"> + <field name="sale_order_id" ref="event_sale.event_2_sale_order_1"/> + <field name="sale_order_line_id" ref="event_sale.event_2_sale_order_1_line_0"/> + </record> + <record id="event.event_registration_2_4" model="event.registration"> + <field name="sale_order_id" ref="event_sale.event_2_sale_order_1"/> + <field name="sale_order_line_id" ref="event_sale.event_2_sale_order_1_line_0"/> + </record> + + <!-- Business Workshop --> + <record id="event.event_registration_4_0" model="event.registration"> + <field name="sale_order_id" ref="event_sale.event_4_sale_order_0"/> + <field name="sale_order_line_id" ref="event_sale.event_4_sale_order_0_line_0"/> + </record> + <record id="event.event_registration_4_1" model="event.registration"> + <field name="sale_order_id" ref="event_sale.event_4_sale_order_1"/> + <field name="sale_order_line_id" ref="event_sale.event_4_sale_order_1_line_0"/> + </record> + <record id="event.event_registration_4_2" model="event.registration"> + <field name="sale_order_id" ref="event_sale.event_4_sale_order_2"/> + <field name="sale_order_line_id" ref="event_sale.event_4_sale_order_2_line_0"/> + </record> + + <!-- OpenWood Collection Online Reveal: Gemini (all) --> + <record id="event.event_registration_7_0" model="event.registration"> + <field name="sale_order_id" ref="event_sale.event_7_sale_order_0"/> + <field name="sale_order_line_id" ref="event_sale.event_7_sale_order_0_line_0"/> + </record> + <record id="event.event_registration_7_1" model="event.registration"> + <field name="sale_order_id" ref="event_sale.event_7_sale_order_1"/> + <field name="sale_order_line_id" ref="event_sale.event_7_sale_order_1_line_0"/> + </record> + <record id="event.event_registration_7_2" model="event.registration"> + <field name="sale_order_id" ref="event_sale.event_7_sale_order_0"/> + <field name="sale_order_line_id" ref="event_sale.event_7_sale_order_0_line_1"/> + </record> + <record id="event.event_registration_7_3" model="event.registration"> + <field name="sale_order_id" ref="event_sale.event_7_sale_order_1"/> + <field name="sale_order_line_id" ref="event_sale.event_7_sale_order_1_line_1"/> + </record> +</data></odoo> diff --git a/addons/event_sale/data/event_sale_demo.xml b/addons/event_sale/data/event_sale_demo.xml new file mode 100644 index 0000000000000000000000000000000000000000..683bc2835f8fa68a9c584d8012e94c6ecdf4c98c --- /dev/null +++ b/addons/event_sale/data/event_sale_demo.xml @@ -0,0 +1,202 @@ +<?xml version="1.0" encoding="utf-8"?> +<odoo><data> + <!-- ****** Products ****** --> + <record id="product_product_event_standard" model="product.product"> + <field name="list_price">30.0</field> + <field name="standard_price">10.0</field> + <field name="uom_id" ref="uom.product_uom_unit"/> + <field name="uom_po_id" ref="uom.product_uom_unit"/> + <field name="name">Event Registration - Standard</field> + <field name="description_sale" eval="False"/> + <field name="invoice_policy">order</field> + <field name="categ_id" ref="event_sale.product_category_events"/> + <field name="detailed_type">event</field> + </record> + + <record id="product_product_event_vip" model="product.product"> + <field name="list_price">100.0</field> + <field name="standard_price">50.0</field> + <field name="uom_id" ref="uom.product_uom_unit"/> + <field name="uom_po_id" ref="uom.product_uom_unit"/> + <field name="name">Event Registration - VIP</field> + <field name="description_sale" eval="False"/> + <field name="invoice_policy">order</field> + <field name="categ_id" ref="event_sale.product_category_events"/> + <field name="detailed_type">event</field> + </record> + + <!-- ****** Registrations ****** --> + <!-- Design fair --> + <record id="event_0_sale_order_0" model="sale.order"> + <field name="user_id" ref="base.user_admin"/> + <field name="partner_id" ref="base.res_partner_address_1"/> + <field name="pricelist_id" ref="product.list0"/> + <field name="date_order" eval="DateTime.now() - relativedelta(days=2)"/> + <field name="state">sale</field> + </record> + <record id="event_0_sale_order_0_line_0" model="sale.order.line"> + <field name="order_id" ref="event_0_sale_order_0"/> + <field name="name">Event Registration - Standard</field> + <field name="product_id" ref="event_sale.product_product_event_standard"/> + <field name="price_unit">1000</field> + <field name="product_uom_qty">2</field> + <field name="event_id" ref="event.event_0"/> + <field name="event_ticket_id" ref="event.event_0_ticket_1"/> + </record> + <record id="event_0_sale_order_0_line_1" model="sale.order.line"> + <field name="order_id" ref="event_0_sale_order_0"/> + <field name="name">Event Registration</field> + <field name="product_id" ref="event_sale.product_product_event"/> + <field name="price_unit">0</field> + <field name="product_uom_qty">1</field> + <field name="event_id" ref="event.event_0"/> + <field name="event_ticket_id" ref="event.event_0_ticket_0"/> + </record> + + <!-- Conference for architects --> + <record id="event_2_sale_order_0" model="sale.order"> + <field name="user_id" ref="base.user_admin"/> + <field name="partner_id" ref="base.res_partner_address_2"/> + <field name="pricelist_id" ref="product.list0"/> + <field name="date_order" eval="DateTime.now() - relativedelta(days=0.5)"/> + <field name="state">sale</field> + </record> + <record id="event_2_sale_order_0_line_0" model="sale.order.line"> + <field name="order_id" ref="event_2_sale_order_0"/> + <field name="name">Event Registration - Standard</field> + <field name="product_id" ref="event_sale.product_product_event_standard"/> + <field name="price_unit">1000</field> + <field name="product_uom_qty">2</field> + <field name="event_id" ref="event.event_2"/> + <field name="event_ticket_id" ref="event.event_2_ticket_1"/> + </record> + <record id="event_2_sale_order_0_line_1" model="sale.order.line"> + <field name="order_id" ref="event_2_sale_order_0"/> + <field name="name">Event Registration - VIP</field> + <field name="product_id" ref="event_sale.product_product_event_vip"/> + <field name="price_unit">1500</field> + <field name="product_uom_qty">1</field> + <field name="event_id" ref="event.event_2"/> + <field name="event_ticket_id" ref="event.event_2_ticket_2"/> + </record> + + <record id="event_2_sale_order_1" model="sale.order"> + <field name="user_id" ref="base.user_admin"/> + <field name="partner_id" ref="base.res_partner_address_3"/> + <field name="pricelist_id" ref="product.list0"/> + <field name="date_order" eval="DateTime.now() - relativedelta(days=1)"/> + <field name="state">sale</field> + </record> + <record id="event_2_sale_order_1_line_0" model="sale.order.line"> + <field name="order_id" ref="event_2_sale_order_1"/> + <field name="name">Event Registration - Standard</field> + <field name="product_id" ref="event_sale.product_product_event_standard"/> + <field name="price_unit">1000</field> + <field name="product_uom_qty">2</field> + <field name="event_id" ref="event.event_2"/> + <field name="event_ticket_id" ref="event.event_2_ticket_1"/> + </record> + + <!-- Business Workshop --> + <record id="event_4_sale_order_0" model="sale.order"> + <field name="user_id" ref="base.user_admin"/> + <field name="partner_id" ref="base.res_partner_address_7"/> + <field name="pricelist_id" ref="product.list0"/> + <field name="date_order" eval="DateTime.now() - relativedelta(days=8)"/> + <field name="state">sale</field> + </record> + <record id="event_4_sale_order_0_line_0" model="sale.order.line"> + <field name="order_id" ref="event_4_sale_order_0"/> + <field name="name">Event Registration - Standard</field> + <field name="product_id" ref="event_sale.product_product_event_standard"/> + <field name="price_unit">499</field> + <field name="product_uom_qty">1</field> + <field name="event_id" ref="event.event_4"/> + <field name="event_ticket_id" ref="event.event_4_ticket_0"/> + </record> + + <record id="event_4_sale_order_1" model="sale.order"> + <field name="user_id" ref="base.user_admin"/> + <field name="partner_id" ref="base.res_partner_address_13"/> + <field name="pricelist_id" ref="product.list0"/> + <field name="date_order" eval="DateTime.now() - relativedelta(days=7)"/> + <field name="state">sale</field> + </record> + <record id="event_4_sale_order_1_line_0" model="sale.order.line"> + <field name="order_id" ref="event_4_sale_order_1"/> + <field name="name">Event Registration - Standard</field> + <field name="product_id" ref="event_sale.product_product_event_standard"/> + <field name="price_unit">499</field> + <field name="product_uom_qty">1</field> + <field name="event_id" ref="event.event_4"/> + <field name="event_ticket_id" ref="event.event_4_ticket_0"/> + </record> + + <record id="event_4_sale_order_2" model="sale.order"> + <field name="user_id" ref="base.user_admin"/> + <field name="partner_id" ref="base.res_partner_address_14"/> + <field name="pricelist_id" ref="product.list0"/> + <field name="date_order" eval="DateTime.now() - relativedelta(days=7)"/> + <field name="state">sale</field> + </record> + <record id="event_4_sale_order_2_line_0" model="sale.order.line"> + <field name="order_id" ref="event_4_sale_order_2"/> + <field name="name">Event Registration - Standard</field> + <field name="product_id" ref="event_sale.product_product_event_standard"/> + <field name="price_unit">499</field> + <field name="product_uom_qty">1</field> + <field name="event_id" ref="event.event_4"/> + <field name="event_ticket_id" ref="event.event_4_ticket_0"/> + </record> + + <!-- OpenWood Collection Online Reveal: Gemini (all) --> + <record id="event_7_sale_order_0" model="sale.order"> + <field name="user_id" ref="base.user_admin"/> + <field name="partner_id" ref="base.res_partner_address_5"/> + <field name="pricelist_id" ref="product.list0"/> + <field name="state">sale</field> + </record> + <record id="event_7_sale_order_0_line_0" model="sale.order.line"> + <field name="order_id" ref="event_7_sale_order_0"/> + <field name="name">Event Registration - Standard</field> + <field name="product_id" ref="event_sale.product_product_event_standard"/> + <field name="price_unit">0</field> + <field name="product_uom_qty">1</field> + <field name="event_id" ref="event.event_7"/> + <field name="event_ticket_id" ref="event.event_7_ticket_1"/> + </record> + <record id="event_7_sale_order_0_line_1" model="sale.order.line"> + <field name="order_id" ref="event_7_sale_order_0"/> + <field name="name">Event Registration - VIP</field> + <field name="product_id" ref="event_sale.product_product_event_vip"/> + <field name="price_unit">0</field> + <field name="product_uom_qty">1</field> + <field name="event_id" ref="event.event_7"/> + <field name="event_ticket_id" ref="event.event_7_ticket_2"/> + </record> + + <record id="event_7_sale_order_1" model="sale.order"> + <field name="user_id" ref="base.user_admin"/> + <field name="partner_id" ref="base.res_partner_address_25"/> + <field name="pricelist_id" ref="product.list0"/> + <field name="state">sale</field> + </record> + <record id="event_7_sale_order_1_line_0" model="sale.order.line"> + <field name="order_id" ref="event_7_sale_order_1"/> + <field name="name">Event Registration - Standard</field> + <field name="product_id" ref="event_sale.product_product_event_standard"/> + <field name="price_unit">0</field> + <field name="product_uom_qty">1</field> + <field name="event_id" ref="event.event_7"/> + <field name="event_ticket_id" ref="event.event_7_ticket_1"/> + </record> + <record id="event_7_sale_order_1_line_1" model="sale.order.line"> + <field name="order_id" ref="event_7_sale_order_1"/> + <field name="name">Event Registration - VIP</field> + <field name="product_id" ref="event_sale.product_product_event_vip"/> + <field name="price_unit">0</field> + <field name="product_uom_qty">1</field> + <field name="event_id" ref="event.event_7"/> + <field name="event_ticket_id" ref="event.event_7_ticket_2"/> + </record> +</data></odoo> diff --git a/addons/event_sale/report/__init__.py b/addons/event_sale/report/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..9608c798dfcc018fc1d6e53023e498861282d8e0 --- /dev/null +++ b/addons/event_sale/report/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from . import event_sale_report diff --git a/addons/event_sale/report/event_sale_report.py b/addons/event_sale/report/event_sale_report.py new file mode 100644 index 0000000000000000000000000000000000000000..ebdff9d2c82fd2ffe238e0bead17acf132ac72ad --- /dev/null +++ b/addons/event_sale/report/event_sale_report.py @@ -0,0 +1,129 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import fields, models, tools + + +class EventSaleReport(models.Model): + """Event Registrations-based sales report, allowing to analyze sales and number of seats + by event (type), ticket, etc. Each opened record will also give access to all this information.""" + _name = 'event.sale.report' + _description = 'Event Sales Report' + _auto = False + _rec_name = 'sale_order_line_id' + + event_type_id = fields.Many2one('event.type', string='Event Type', readonly=True) + event_id = fields.Many2one('event.event', string='Event', readonly=True) + event_date_begin = fields.Date(string='Event Start Date', readonly=True) + event_date_end = fields.Date(string='Event End Date', readonly=True) + event_ticket_id = fields.Many2one('event.event.ticket', string='Event Ticket', readonly=True) + event_ticket_price = fields.Float(string='Ticket price', readonly=True) + event_registration_create_date = fields.Date(string='Registration Date', readonly=True) + event_registration_state = fields.Selection([ + ('draft', 'Unconfirmed'), ('cancel', 'Cancelled'), + ('open', 'Confirmed'), ('done', 'Attended')], + string='Registration Status', readonly=True) + active = fields.Boolean('Is registration active (not archived)?') + event_registration_id = fields.Many2one('event.registration', readonly=True) + event_registration_name = fields.Char('Attendee Name', readonly=True) + + product_id = fields.Many2one('product.product', string='Product', readonly=True) + sale_order_id = fields.Many2one('sale.order', readonly=True) + sale_order_date = fields.Datetime('Order Date', readonly=True) + sale_order_partner_id = fields.Many2one('res.partner', string='Customer', readonly=True) + sale_order_state = fields.Selection([ + ('draft', 'Quotation'), + ('sent', 'Quotation Sent'), + ('sale', 'Sales Order'), + ('done', 'Locked'), + ('cancel', 'Cancelled'), + ], string='Sale Order Status', readonly=True) + sale_order_user_id = fields.Many2one('res.users', string='Salesperson', readonly=True) + sale_order_line_id = fields.Many2one('sale.order.line', readonly=True) + sale_price = fields.Float('Revenues', readonly=True) + sale_price_untaxed = fields.Float('Untaxed Revenues', readonly=True) + invoice_partner_id = fields.Many2one('res.partner', string='Invoice Address', readonly=True) + is_paid = fields.Boolean('Is Paid', readonly=True) + payment_status = fields.Selection(string="Payment Status", selection=[ + ('to_pay', 'Not Paid'), + ('paid', 'Paid'), + ('free', 'Free'), + ]) + company_id = fields.Many2one('res.company', string='Company', readonly=True) + + def init(self): + tools.drop_view_if_exists(self._cr, self._table) + self._cr.execute('CREATE OR REPLACE VIEW %s AS (%s);' % (self._table, self._query())) + + def _query(self, with_=None, select=None, join=None, group_by=None): + return "\n".join([ + self._with_clause(*(with_ or [])), + self._select_clause(*(select or [])), + self._from_clause(*(join or [])), + self._group_by_clause(*(group_by or [])) + ]) + + def _with_clause(self, *with_): + # Extra clauses formatted as `cte1 AS (SELECT ...)`, `cte2 AS (SELECT ...)`... + return """ +WITH + """ + ',\n '.join(with_) if with_ else '' + + def _select_clause(self, *select): + # Extra clauses formatted as `cte1.column1 AS new_column1`, `table1.column2 AS new_column2`... + return """ +SELECT + ROW_NUMBER() OVER (ORDER BY event_registration.id) AS id, + + event_registration.id AS event_registration_id, + event_registration.company_id AS company_id, + event_registration.event_id AS event_id, + event_registration.event_ticket_id AS event_ticket_id, + event_registration.create_date AS event_registration_create_date, + event_registration.name AS event_registration_name, + event_registration.state AS event_registration_state, + event_registration.active AS active, + event_registration.sale_order_id AS sale_order_id, + event_registration.sale_order_line_id AS sale_order_line_id, + event_registration.is_paid AS is_paid, + + event_event.event_type_id AS event_type_id, + event_event.date_begin AS event_date_begin, + event_event.date_end AS event_date_end, + + event_event_ticket.price AS event_ticket_price, + + sale_order.date_order AS sale_order_date, + sale_order.partner_invoice_id AS invoice_partner_id, + sale_order.partner_id AS sale_order_partner_id, + sale_order.state AS sale_order_state, + sale_order.user_id AS sale_order_user_id, + + sale_order_line.product_id AS product_id, + sale_order_line.price_total + / CASE COALESCE(sale_order.currency_rate, 0) WHEN 0 THEN 1.0 ELSE sale_order.currency_rate END + / sale_order_line.product_uom_qty AS sale_price, + sale_order_line.price_subtotal + / CASE COALESCE(sale_order.currency_rate, 0) WHEN 0 THEN 1.0 ELSE sale_order.currency_rate END + / sale_order_line.product_uom_qty AS sale_price_untaxed, + CASE + WHEN sale_order_line.price_total = 0 THEN 'free' + WHEN event_registration.is_paid THEN 'paid' + ELSE 'to_pay' + END payment_status""" + (',\n ' + ',\n '.join(select) if select else '') + + def _from_clause(self, *join_): + # Extra clauses formatted as `column1`, `column2`... + return """ +FROM event_registration +LEFT JOIN event_event ON event_event.id = event_registration.event_id +LEFT JOIN event_event_ticket ON event_event_ticket.id = event_registration.event_ticket_id +LEFT JOIN sale_order ON sale_order.id = event_registration.sale_order_id +LEFT JOIN sale_order_line ON sale_order_line.id = event_registration.sale_order_line_id +""" + ('\n'.join(join_) + '\n' if join_ else '') + + def _group_by_clause(self, *group_by): + # Extra clauses formatted like `column1`, `column2`... + return """ +GROUP BY + """ + ',\n '.join(group_by) if group_by else '' diff --git a/addons/event_sale/report/event_sale_report_views.xml b/addons/event_sale/report/event_sale_report_views.xml new file mode 100644 index 0000000000000000000000000000000000000000..5847a5cf19b040fa468783fe1bef854785a649d2 --- /dev/null +++ b/addons/event_sale/report/event_sale_report_views.xml @@ -0,0 +1,152 @@ +<?xml version="1.0" encoding="utf-8"?> +<odoo> + <record id="event_sale_report_view_graph" model="ir.ui.view"> + <field name="name">event.sale.report.view.graph</field> + <field name="model">event.sale.report</field> + <field name="arch" type="xml"> + <graph string="Revenues" sample="1" type="line"> + <field name="sale_price" type="measure"/> + <field name="event_registration_create_date" interval="day"/> + <field name="event_ticket_price" type="measure" invisible="True"/> + </graph> + </field> + </record> + + <record id="event_sale_report_view_form" model="ir.ui.view"> + <field name="name">event.sale.report.view.form</field> + <field name="model">event.sale.report</field> + <field name="arch" type="xml"> + <form string="Registration revenues" edit="false" create="false"> + <sheet> + <group col="2"> + <group string="Event"> + <field name="event_type_id"/> + <field name="event_id"/> + <field name="event_date_begin"/> + </group> + <group string="Registration"> + <field name="event_registration_id"/> + <field name="event_registration_name"/> + <field name="event_registration_create_date"/> + <field name="event_ticket_id"/> + <field name="event_registration_state"/> + </group> + </group> + <group col="2"> + <group string="Sale Order"> + <field name="sale_order_partner_id"/> + <field name="sale_order_id"/> + <field name="product_id"/> + </group> + <group string="Revenues"> + <field name="event_ticket_price"/> + <field name="sale_price_untaxed"/> + <field name="sale_price"/> + </group> + </group> + </sheet> + </form> + </field> + </record> + + <record id="event_sale_report_view_pivot" model="ir.ui.view"> + <field name="name">event.sale.report.view.pivot</field> + <field name="model">event.sale.report</field> + <field name="arch" type="xml"> + <pivot string="Revenues" sample="1"> + <field name="sale_price_untaxed" type="measure"/> + <field name="sale_price" type="measure"/> + <field name="event_id" type="row"/> + <field name="product_id" type="row"/> + <field name="event_ticket_price" invisible="True"/> + </pivot> + </field> + </record> + + <record id="event_sale_report_view_tree" model="ir.ui.view"> + <field name="name">event.sale.report.view.tree</field> + <field name="model">event.sale.report</field> + <field name="arch" type="xml"> + <tree string="Revenues" edit="false" create="false"> + <field name="event_id"/> + <field name="event_ticket_id"/> + <field name="product_id" optional="hide"/> + <field name="event_ticket_price"/> + <field name="sale_price_untaxed" optional="hide"/> + <field name="sale_price" optional="hide"/> + <field name="event_registration_state" optional="hide"/> + <field name="sale_order_partner_id"/> + <field name="invoice_partner_id" optional="hide"/> + <field name="event_registration_name" optional="hide"/> + <field name="sale_order_state" widget="badge" + decoration-success="sale_order_state == 'sale' or sale_order_state == 'done'" + decoration-info="sale_order_state == 'draft' or sale_order_state == 'sent'"/> + </tree> + </field> + </record> + + <record id="event_sale_report_view_search" model="ir.ui.view"> + <field name="name">event.sale.report.view.search</field> + <field name="model">event.sale.report</field> + <field name="arch" type="xml"> + <search string="Event Sales Analysis"> + <field name="event_id"/> + <field name="event_registration_name" string="Participant"/> + <field name="sale_order_partner_id" string="Booked by"/> + <field name="company_id"/> + <filter string="Non-free tickets" name="priced_tickets" domain="[('event_ticket_price', '!=', 0)]"/> + <separator/> + <filter string="Free" name="free" domain="[('payment_status', '=', 'free')]"/> + <filter string="Pending payment" name="payment_pending" domain="[('payment_status', '=', 'to_pay')]"/> + <filter string="Paid" name="is_paid" domain="[('payment_status', '=', 'paid')]"/> + <separator/> + <filter string="Registration Date" name="event_registration_create_date" date="event_registration_create_date" default_period="this_year"/> + <separator/> + <filter string="Upcoming/Running" name="upcoming" help="Upcoming events from today" + domain="[('event_date_end', '>=', datetime.datetime.combine(context_today(), datetime.time(0,0,0)))]"/> + <filter string="Past Events" name="past" help="Events that have ended" + domain="[('event_date_end', '<', datetime.datetime.combine(context_today(), datetime.time(0,0,0)))]"/> + <filter string="Event Start Date" name="event_date_start" date="event_date_begin" default_period="this_year"/> + <filter string="Event End Date" name="event_date_end" date="event_date_end"/> + <group expand="0" string="Group By"> + <filter string="Event Type" name="group_by_event_type_id" context="{'group_by': 'event_type_id' }"/> + <filter string="Event" name="group_by_event_id" context="{'group_by': 'event_id' }"/> + <separator/> + <filter string="Product" name="group_by_product_id" context="{'group_by': 'product_id'}"/> + <filter string="Ticket" name="group_by_ticket_id" context="{'group_by': 'event_ticket_id'}"/> + <separator/> + <filter string="Registration Status" name="group_by_registration_state" + context="{'group_by': 'event_registration_state'}"/> + <filter string="Sale Order Status" name="group_by_sale_order_state" + context="{'group_by': 'sale_order_state'}"/> + <filter string="Customer" name="group_by_customer" context="{'group_by': 'sale_order_partner_id'}"/> + </group> + </search> + </field> + </record> + + <record id="event_sale_report_action" model="ir.actions.act_window"> + <field name="name">Revenues</field> + <field name="res_model">event.sale.report</field> + <field name="view_mode">graph,pivot</field> + <field name="context">{ + 'search_default_priced_tickets': 1, + 'search_default_event_date_start': 1, + 'pivot_measures': ['__count__', 'sale_price_untaxed', 'sale_price'], + }</field> + <field name="help" type="html"> + <p class="o_view_nocontent_smiling_face"> + No Event Revenues yet! + </p><p> + Come back once tickets have been sold to overview your sales income. + </p> + </field> + </record> + + <menuitem name="Revenues" + id="menu_action_show_revenues" + action="event_sale_report_action" + sequence="5" + parent="event.menu_reporting_events" + groups="event.group_event_user"/> +</odoo> diff --git a/addons/event_sale/security/ir.model.access.csv b/addons/event_sale/security/ir.model.access.csv index 06b66e1cf00b175224730f3bf8b549c7c2685542..7d72b912e5ba83f222bf44324100c45f091c1600 100644 --- a/addons/event_sale/security/ir.model.access.csv +++ b/addons/event_sale/security/ir.model.access.csv @@ -4,3 +4,4 @@ access_product_product_event_manager,product.product.event.manager,product.model access_registration_editor,access.registration.editor,model_registration_editor,sales_team.group_sale_salesman,1,1,1,0 access_registration_editor_line,access.registration.editor.line,model_registration_editor_line,sales_team.group_sale_salesman,1,1,1,1 access_event_event_configurator,access.event.event.configurator,model_event_event_configurator,sales_team.group_sale_salesman,1,1,1,0 +access_event_sale_report_manager,access.event.sale.report.manager,model_event_sale_report,event.group_event_manager,1,1,1,1 diff --git a/addons/event_sale/security/ir_rule.xml b/addons/event_sale/security/ir_rule.xml new file mode 100644 index 0000000000000000000000000000000000000000..87e67aed7ae309f013166ab6d89c3917013010f5 --- /dev/null +++ b/addons/event_sale/security/ir_rule.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<odoo noupdate="1"> + + <!-- Multi - Company Rules --> + <record id="event_sale_report_comp_rule" model="ir.rule"> + <field name="name">Event Sales Report multi-company</field> + <field name="model_id" ref="model_event_sale_report"/> + <field name="domain_force">['|', ('company_id', '=', False), ('company_id', 'in', company_ids)]</field> + </record> + +</odoo> diff --git a/addons/test_event_full/tests/test_performance.py b/addons/test_event_full/tests/test_performance.py index 7e476fd4f082393a63b6bb660c651ef41b79c04b..91dc5080b54affddc1cbce0d9e584d309642ce4e 100644 --- a/addons/test_event_full/tests/test_performance.py +++ b/addons/test_event_full/tests/test_performance.py @@ -52,7 +52,7 @@ class TestEventPerformance(EventPerformanceCase): batch_size = 20 # simple without type involved + website - with freeze_time(self.reference_now), self.assertQueryCount(event_user=5484): # tef only: 5069 (5065) - com runbot: 5059 - ent runbot 5484 + with freeze_time(self.reference_now), self.assertQueryCount(event_user=5483): # tef only: 5068? (5064?) - com runbot: 5058 - ent runbot 5483 self.env.cr._now = self.reference_now # force create_date to check schedulers event_values = [ dict(self.event_base_vals, @@ -125,7 +125,7 @@ class TestEventPerformance(EventPerformanceCase): has_social = 'social_menu' in self.env['event.event'] # otherwise view may crash in enterprise # no type, website - with freeze_time(self.reference_now), self.assertQueryCount(event_user=691): # tef only: 637 - com runbot: 587 - ent runbot: 691 + with freeze_time(self.reference_now), self.assertQueryCount(event_user=692): # tef only: 638? - com runbot: 588 - ent runbot: 692 self.env.cr._now = self.reference_now # force create_date to check schedulers with Form(self.env['event.event']) as event_form: event_form.name = 'Test Event' @@ -172,7 +172,7 @@ class TestEventPerformance(EventPerformanceCase): def test_event_create_single_notype_website(self): """ Test a single event creation """ # simple without type involved + website - with freeze_time(self.reference_now), self.assertQueryCount(event_user=373): # tef only: 358 (353) - com runbot: 284 - ent runbot 373 + with freeze_time(self.reference_now), self.assertQueryCount(event_user=372): # tef only: 347? (342?) - com runbot: 346 - ent runbot 372 self.env.cr._now = self.reference_now # force create_date to check schedulers event_values = dict( self.event_base_vals, @@ -225,7 +225,7 @@ class TestRegistrationPerformance(EventPerformanceCase): """ event = self.env['event.event'].browse(self.test_event.ids) - with freeze_time(self.reference_now), self.assertQueryCount(event_user=716): # tef only: 674 - com runbot 713 - ent runbot 716 + with freeze_time(self.reference_now), self.assertQueryCount(event_user=714): # tef only: 672? - com runbot 711 - ent runbot 714 self.env.cr._now = self.reference_now # force create_date to check schedulers registration_values = [ dict(reg_data, @@ -271,7 +271,7 @@ class TestRegistrationPerformance(EventPerformanceCase): form like) """ event = self.env['event.event'].browse(self.test_event.ids) - with freeze_time(self.reference_now), self.assertQueryCount(event_user=727): # tef only: 685 - com runbot 724 - ent runbot: 727 + with freeze_time(self.reference_now), self.assertQueryCount(event_user=725): # tef only: 683? - com runbot 722 - ent runbot: 725 self.env.cr._now = self.reference_now # force create_date to check schedulers registration_values = [ dict(reg_data, @@ -292,7 +292,7 @@ class TestRegistrationPerformance(EventPerformanceCase): """ Test a single registration creation using Form """ event = self.env['event.event'].browse(self.test_event.ids) - with freeze_time(self.reference_now), self.assertQueryCount(event_user=227): # tef only: 210 - com runbot: 213 - ent runbot: 227 + with freeze_time(self.reference_now), self.assertQueryCount(event_user=225): # tef only: 208? - com runbot: 211 - ent runbot: 225 self.env.cr._now = self.reference_now # force create_date to check schedulers with Form(self.env['event.registration']) as reg_form: reg_form.event_id = event @@ -308,7 +308,7 @@ class TestRegistrationPerformance(EventPerformanceCase): """ Test a single registration creation using Form """ event = self.env['event.event'].browse(self.test_event.ids) - with freeze_time(self.reference_now), self.assertQueryCount(event_user=229): # tef only: 213 - com runbot: 214 - ent runbot: 229 + with freeze_time(self.reference_now), self.assertQueryCount(event_user=227): # tef only: 211? - com runbot: 212 - ent runbot: 227 self.env.cr._now = self.reference_now # force create_date to check schedulers with Form(self.env['event.registration']) as reg_form: reg_form.event_id = event @@ -335,7 +335,7 @@ class TestRegistrationPerformance(EventPerformanceCase): event = self.env['event.event'].browse(self.test_event.ids) # simple customer data - with freeze_time(self.reference_now), self.assertQueryCount(event_user=139): # tef only: 135 - com runbot: 137 - ent runbot: 139 + with freeze_time(self.reference_now), self.assertQueryCount(event_user=138): # tef only: 133? - com runbot: 135 - ent runbot: 138 self.env.cr._now = self.reference_now # force create_date to check schedulers registration_values = dict( self.customer_data[0], @@ -349,7 +349,7 @@ class TestRegistrationPerformance(EventPerformanceCase): event = self.env['event.event'].browse(self.test_event.ids) # partner-based customer - with freeze_time(self.reference_now), self.assertQueryCount(event_user=145): # tef only: 143 - com runbot: 144 - ent runbot: 145 + with freeze_time(self.reference_now), self.assertQueryCount(event_user=143): # tef only: 141? - com runbot: 142 - ent runbot: 143 self.env.cr._now = self.reference_now # force create_date to check schedulers registration_values = { 'event_id': event.id, @@ -364,7 +364,7 @@ class TestRegistrationPerformance(EventPerformanceCase): event = self.env['event.event'].browse(self.test_event.ids) # partner-based customer - with freeze_time(self.reference_now), self.assertQueryCount(event_user=147): # tef only: 56 - com runbot: 58 - ent runbot: 147 + with freeze_time(self.reference_now), self.assertQueryCount(event_user=56): # tef only: 54? - com runbot: 54 - ent runbot: 56 self.env.cr._now = self.reference_now # force create_date to check schedulers registration_values = { 'event_id': event.id, @@ -379,7 +379,7 @@ class TestRegistrationPerformance(EventPerformanceCase): event = self.env['event.event'].browse(self.test_event.ids) # website customer data - with freeze_time(self.reference_now), self.assertQueryCount(event_user=147): # tef only: 142 - com runbot: 143 - ent runbot: 147 + with freeze_time(self.reference_now), self.assertQueryCount(event_user=145): # tef only: 140? - com runbot: 141 - ent runbot: 145 self.env.cr._now = self.reference_now # force create_date to check schedulers registration_values = dict( self.website_customer_data[0], @@ -436,7 +436,7 @@ class TestOnlineEventPerformance(EventPerformanceCase, UtilPerf): # website customer data with freeze_time(self.reference_now): self.authenticate(None, None) - with self.assertQueryCount(default=31): + with self.assertQueryCount(default=31): # tef only: 29? - com runbot: 29 - ent runbot: 31 self._test_url_open('/event/%i' % self.test_event.id) @warmup diff --git a/addons/website_event_sale/__init__.py b/addons/website_event_sale/__init__.py index 7d34c7c054abd3105d5bb41fe9674111e1c27c16..5905766d867f6dfa25c725d74bdf3f427e6c3ba6 100644 --- a/addons/website_event_sale/__init__.py +++ b/addons/website_event_sale/__init__.py @@ -3,3 +3,4 @@ from . import controllers from . import models +from . import report diff --git a/addons/website_event_sale/__manifest__.py b/addons/website_event_sale/__manifest__.py index 2e1fd181febd58fece55cc57c2e9a66f5d6b0189..d15aee651920643cb6676bc1cd651a18827f79ab 100644 --- a/addons/website_event_sale/__manifest__.py +++ b/addons/website_event_sale/__manifest__.py @@ -11,6 +11,7 @@ Sell event tickets through eCommerce app. 'depends': ['website_event', 'event_sale', 'website_sale'], 'data': [ 'data/event_data.xml', + 'report/event_sale_report_views.xml', 'views/event_event_views.xml', 'views/website_event_templates.xml', 'views/website_sale_templates.xml', diff --git a/addons/website_event_sale/report/__init__.py b/addons/website_event_sale/report/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..5e37be748c84e9b98d8a7a55fe4cf7a81bf27fae --- /dev/null +++ b/addons/website_event_sale/report/__init__.py @@ -0,0 +1 @@ +from . import event_sale_report diff --git a/addons/website_event_sale/report/event_sale_report.py b/addons/website_event_sale/report/event_sale_report.py new file mode 100644 index 0000000000000000000000000000000000000000..6efd239b8ad3695a2d7294bf3dab79d7d9abb1a9 --- /dev/null +++ b/addons/website_event_sale/report/event_sale_report.py @@ -0,0 +1,13 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import fields, models + + +class EventSaleReport(models.Model): + _inherit = 'event.sale.report' + + is_published = fields.Boolean('Published Events', readonly=True) + + def _select_clause(self, *select): + return super()._select_clause('event_event.is_published as is_published', *select) diff --git a/addons/website_event_sale/report/event_sale_report_views.xml b/addons/website_event_sale/report/event_sale_report_views.xml new file mode 100644 index 0000000000000000000000000000000000000000..bc3ffeb873668499fc81f8998b9ed080e2713543 --- /dev/null +++ b/addons/website_event_sale/report/event_sale_report_views.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="utf-8"?> +<odoo> + + <record id="event_sale_report_view_search" model="ir.ui.view"> + <field name="name">event.sale.report.view.search.inherit.website</field> + <field name="model">event.sale.report</field> + <field name="inherit_id" ref="event_sale.event_sale_report_view_search"/> + <field name="arch" type="xml"> + <xpath expr="//search" position="inside"> + <filter string="Published Events" name="is_published" domain="[('is_published', '=', True)]"/> + </xpath> + </field> + </record> +</odoo>