diff --git a/addons/pos_sale/report/sale_report.py b/addons/pos_sale/report/sale_report.py index 59632df0c993fdbb83655398ece9bac3cab17329..cf3ec8f47239809bfa97b9b1249a98226f09d404 100644 --- a/addons/pos_sale/report/sale_report.py +++ b/addons/pos_sale/report/sale_report.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -from odoo import tools from odoo import api, fields, models @@ -10,27 +9,29 @@ class SaleReport(models.Model): @api.model def _get_done_states(self): - done_states = super(SaleReport, self)._get_done_states() + done_states = super()._get_done_states() done_states.extend(['pos_done', 'invoiced']) return done_states - state = fields.Selection(selection_add=[('pos_draft', 'New'), - ('paid', 'Paid'), - ('pos_done', 'Posted'), - ('invoiced', 'Invoiced')], string='Status', readonly=True) + state = fields.Selection( + selection_add=[ + ('pos_draft', 'New'), + ('paid', 'Paid'), + ('pos_done', 'Posted'), + ('invoiced', 'Invoiced') + ], + ) - def _select_pos(self, fields=None): - if not fields: - fields = {} - select_ = ''' + def _select_pos(self): + select_ = """ MIN(l.id) AS id, l.product_id AS product_id, t.uom_id AS product_uom, - sum(l.qty) AS product_uom_qty, - sum(l.qty) AS qty_delivered, - 0 as qty_to_deliver, - CASE WHEN pos.state = 'invoiced' THEN sum(l.qty) ELSE 0 END AS qty_invoiced, - CASE WHEN pos.state != 'invoiced' THEN sum(l.qty) ELSE 0 END AS qty_to_invoice, + SUM(l.qty) AS product_uom_qty, + SUM(l.qty) AS qty_delivered, + 0 AS qty_to_deliver, + CASE WHEN pos.state = 'invoiced' THEN SUM(l.qty) ELSE 0 END AS qty_invoiced, + CASE WHEN pos.state != 'invoiced' THEN SUM(l.qty) ELSE 0 END AS qty_to_invoice, SUM(l.price_subtotal_incl) / MIN(CASE COALESCE(pos.currency_rate, 0) WHEN 0 THEN 1.0 ELSE pos.currency_rate END) AS price_total, SUM(l.price_subtotal) / MIN(CASE COALESCE(pos.currency_rate, 0) WHEN 0 THEN 1.0 ELSE pos.currency_rate END) AS price_subtotal, (CASE WHEN pos.state != 'invoiced' THEN SUM(l.price_subtotal_incl) ELSE 0 END) / MIN(CASE COALESCE(pos.currency_rate, 0) WHEN 0 THEN 1.0 ELSE pos.currency_rate END) AS amount_to_invoice, @@ -45,7 +46,6 @@ class SaleReport(models.Model): NULL AS campaign_id, NULL AS medium_id, NULL AS source_id, - extract(epoch from avg(date_trunc('day',pos.date_order)-date_trunc('day',pos.create_date)))/(24*60*60)::decimal(16,2) AS delay, t.categ_id AS categ_id, pos.pricelist_id AS pricelist_id, NULL AS analytic_account_id, @@ -54,37 +54,36 @@ class SaleReport(models.Model): partner.country_id AS country_id, partner.industry_id AS industry_id, partner.commercial_partner_id AS commercial_partner_id, - (sum(t.weight) * l.qty / u.factor) AS weight, - (sum(t.volume) * l.qty / u.factor) AS volume, - l.discount as discount, - sum((l.price_unit * l.discount * l.qty / 100.0 / CASE COALESCE(pos.currency_rate, 0) WHEN 0 THEN 1.0 ELSE pos.currency_rate END)) as discount_amount, - NULL as order_id - ''' + (SUM(t.weight) * l.qty / u.factor) AS weight, + (SUM(t.volume) * l.qty / u.factor) AS volume, + l.discount AS discount, + SUM((l.price_unit * l.discount * l.qty / 100.0 / CASE COALESCE(pos.currency_rate, 0) WHEN 0 THEN 1.0 ELSE pos.currency_rate END)) AS discount_amount, + NULL AS order_id""" - for field in fields.keys(): - select_ += ', NULL AS %s' % (field) + additional_fields_info = self._select_additional_fields() + template = """, + NULL AS %s""" + for fname in additional_fields_info.keys(): + select_ += template % fname return select_ def _from_pos(self): - from_ = ''' + return """ pos_order_line l - join pos_order pos on (l.order_id=pos.id) - left join res_partner partner ON (pos.partner_id = partner.id OR pos.partner_id = NULL) - left join product_product p on (l.product_id=p.id) - left join product_template t on (p.product_tmpl_id=t.id) - LEFT JOIN uom_uom u ON (u.id=t.uom_id) - LEFT JOIN pos_session session ON (session.id = pos.session_id) - LEFT JOIN pos_config config ON (config.id = session.config_id) - left join product_pricelist pp on (pos.pricelist_id = pp.id) - ''' - return from_ + JOIN pos_order pos ON l.order_id = pos.id + LEFT JOIN res_partner partner ON (pos.partner_id=partner.id OR pos.partner_id = NULL) + LEFT JOIN product_product p ON l.product_id=p.id + LEFT JOIN product_template t ON p.product_tmpl_id=t.id + LEFT JOIN uom_uom u ON u.id=t.uom_id + LEFT JOIN pos_session session ON session.id = pos.session_id + LEFT JOIN pos_config config ON config.id = session.config_id""" def _where_pos(self): - where_ = 'l.sale_order_line_id is NULL' - return where_ + return """ + l.sale_order_line_id IS NULL""" def _group_by_pos(self): - groupby_ = ''' + return """ l.order_id, l.product_id, l.price_unit, @@ -104,15 +103,14 @@ class SaleReport(models.Model): partner.industry_id, partner.commercial_partner_id, u.factor, - pos.crm_team_id - ''' - return groupby_ + pos.crm_team_id""" - def _query(self, with_clause='', fields=None, groupby='', from_clause=''): - if not fields: - fields = {} - res = super()._query(with_clause, fields, groupby, from_clause) - current = '(SELECT %s FROM %s WHERE %s GROUP BY %s)' % \ - (self._select_pos(fields), self._from_pos(), self._where_pos(), self._group_by_pos()) - - return '%s UNION ALL %s' % (res, current) + def _query(self): + res = super()._query() + return res + f"""UNION ALL ( + SELECT {self._select_pos()} + FROM {self._from_pos()} + WHERE {self._where_pos()} + GROUP BY {self._group_by_pos()} + ) + """ diff --git a/addons/sale/report/sale_report.py b/addons/sale/report/sale_report.py index 7bb5546e9e48e189975ab15b32d2b1f787b94056..217384f534abdcd9000ec0ac2ede6586c458a014 100644 --- a/addons/sale/report/sale_report.py +++ b/addons/sale/report/sale_report.py @@ -58,68 +58,79 @@ class SaleReport(models.Model): order_id = fields.Many2one('sale.order', 'Order #', readonly=True) - def _select_sale(self, fields=None): - if not fields: - fields = {} + def _with_sale(self): + return "" + + def _select_sale(self): select_ = """ - coalesce(min(l.id), -s.id) as id, - l.product_id as product_id, - t.uom_id as product_uom, - CASE WHEN l.product_id IS NOT NULL THEN sum(l.product_uom_qty / u.factor * u2.factor) ELSE 0 END as product_uom_qty, - CASE WHEN l.product_id IS NOT NULL THEN sum(l.qty_delivered / u.factor * u2.factor) ELSE 0 END as qty_delivered, - CASE WHEN l.product_id IS NOT NULL THEN SUM((l.product_uom_qty - l.qty_delivered) / u.factor * u2.factor) ELSE 0 END as qty_to_deliver, - CASE WHEN l.product_id IS NOT NULL THEN sum(l.qty_invoiced / u.factor * u2.factor) ELSE 0 END as qty_invoiced, - CASE WHEN l.product_id IS NOT NULL THEN sum(l.qty_to_invoice / u.factor * u2.factor) ELSE 0 END as qty_to_invoice, - CASE WHEN l.product_id IS NOT NULL THEN sum(l.price_total / CASE COALESCE(s.currency_rate, 0) WHEN 0 THEN 1.0 ELSE s.currency_rate END) ELSE 0 END as price_total, - CASE WHEN l.product_id IS NOT NULL THEN sum(l.price_subtotal / CASE COALESCE(s.currency_rate, 0) WHEN 0 THEN 1.0 ELSE s.currency_rate END) ELSE 0 END as price_subtotal, - CASE WHEN l.product_id IS NOT NULL THEN sum(l.untaxed_amount_to_invoice / CASE COALESCE(s.currency_rate, 0) WHEN 0 THEN 1.0 ELSE s.currency_rate END) ELSE 0 END as untaxed_amount_to_invoice, - CASE WHEN l.product_id IS NOT NULL THEN sum(l.untaxed_amount_invoiced / CASE COALESCE(s.currency_rate, 0) WHEN 0 THEN 1.0 ELSE s.currency_rate END) ELSE 0 END as untaxed_amount_invoiced, - count(*) as nbr, - s.name as name, - s.date_order as date, - s.state as state, - s.partner_id as partner_id, - s.user_id as user_id, - s.company_id as company_id, - s.campaign_id as campaign_id, - s.medium_id as medium_id, - s.source_id as source_id, - extract(epoch from avg(date_trunc('day',s.date_order)-date_trunc('day',s.create_date)))/(24*60*60)::decimal(16,2) as delay, - t.categ_id as categ_id, - s.pricelist_id as pricelist_id, - s.analytic_account_id as analytic_account_id, - s.team_id as team_id, + coalesce(min(l.id), -s.id) AS id, + l.product_id AS product_id, + t.uom_id AS product_uom, + CASE WHEN l.product_id IS NOT NULL THEN SUM(l.product_uom_qty / u.factor * u2.factor) ELSE 0 END AS product_uom_qty, + CASE WHEN l.product_id IS NOT NULL THEN SUM(l.qty_delivered / u.factor * u2.factor) ELSE 0 END AS qty_delivered, + CASE WHEN l.product_id IS NOT NULL THEN SUM((l.product_uom_qty - l.qty_delivered) / u.factor * u2.factor) ELSE 0 END AS qty_to_deliver, + CASE WHEN l.product_id IS NOT NULL THEN SUM(l.qty_invoiced / u.factor * u2.factor) ELSE 0 END AS qty_invoiced, + CASE WHEN l.product_id IS NOT NULL THEN SUM(l.qty_to_invoice / u.factor * u2.factor) ELSE 0 END AS qty_to_invoice, + CASE WHEN l.product_id IS NOT NULL THEN SUM(l.price_total / CASE COALESCE(s.currency_rate, 0) WHEN 0 THEN 1.0 ELSE s.currency_rate END) ELSE 0 END AS price_total, + CASE WHEN l.product_id IS NOT NULL THEN SUM(l.price_subtotal / CASE COALESCE(s.currency_rate, 0) WHEN 0 THEN 1.0 ELSE s.currency_rate END) ELSE 0 END AS price_subtotal, + CASE WHEN l.product_id IS NOT NULL THEN SUM(l.untaxed_amount_to_invoice / CASE COALESCE(s.currency_rate, 0) WHEN 0 THEN 1.0 ELSE s.currency_rate END) ELSE 0 END AS untaxed_amount_to_invoice, + CASE WHEN l.product_id IS NOT NULL THEN SUM(l.untaxed_amount_invoiced / CASE COALESCE(s.currency_rate, 0) WHEN 0 THEN 1.0 ELSE s.currency_rate END) ELSE 0 END AS untaxed_amount_invoiced, + COUNT(*) AS nbr, + s.name AS name, + s.date_order AS date, + s.state AS state, + s.partner_id AS partner_id, + s.user_id AS user_id, + s.company_id AS company_id, + s.campaign_id AS campaign_id, + s.medium_id AS medium_id, + s.source_id AS source_id, + t.categ_id AS categ_id, + s.pricelist_id AS pricelist_id, + s.analytic_account_id AS analytic_account_id, + s.team_id AS team_id, p.product_tmpl_id, - partner.country_id as country_id, - partner.industry_id as industry_id, - partner.commercial_partner_id as commercial_partner_id, - CASE WHEN l.product_id IS NOT NULL THEN sum(p.weight * l.product_uom_qty / u.factor * u2.factor) ELSE 0 END as weight, - CASE WHEN l.product_id IS NOT NULL THEN sum(p.volume * l.product_uom_qty / u.factor * u2.factor) ELSE 0 END as volume, - l.discount as discount, - CASE WHEN l.product_id IS NOT NULL THEN sum((l.price_unit * l.product_uom_qty * l.discount / 100.0 / CASE COALESCE(s.currency_rate, 0) WHEN 0 THEN 1.0 ELSE s.currency_rate END))ELSE 0 END as discount_amount, - s.id as order_id - """ + partner.country_id AS country_id, + partner.industry_id AS industry_id, + partner.commercial_partner_id AS commercial_partner_id, + CASE WHEN l.product_id IS NOT NULL THEN SUM(p.weight * l.product_uom_qty / u.factor * u2.factor) ELSE 0 END AS weight, + CASE WHEN l.product_id IS NOT NULL THEN SUM(p.volume * l.product_uom_qty / u.factor * u2.factor) ELSE 0 END AS volume, + l.discount AS discount, + CASE WHEN l.product_id IS NOT NULL THEN SUM((l.price_unit * l.product_uom_qty * l.discount / 100.0 / CASE COALESCE(s.currency_rate, 0) WHEN 0 THEN 1.0 ELSE s.currency_rate END))ELSE 0 END AS discount_amount, + s.id AS order_id""" + + additional_fields_info = self._select_additional_fields() + template = """, + %s AS %s""" + for fname, query_info in additional_fields_info.items(): + select_ += template % (query_info, fname) - for field in fields.values(): - select_ += field return select_ - def _from_sale(self, from_clause=''): - from_ = """ - sale_order_line l - right outer join sale_order s on (s.id=l.order_id) - join res_partner partner on s.partner_id = partner.id - left join product_product p on (l.product_id=p.id) - left join product_template t on (p.product_tmpl_id=t.id) - left join uom_uom u on (u.id=l.product_uom) - left join uom_uom u2 on (u2.id=t.uom_id) - left join product_pricelist pp on (s.pricelist_id = pp.id) - %s - """ % from_clause - return from_ - - def _group_by_sale(self, groupby=''): - groupby_ = """ + def _select_additional_fields(self): + """Hook to return additional fields SQL specification for select part of the table query. + + :returns: mapping field -> SQL computation of field, will be converted to '_ AS _field' in the final table definition + :rtype: dict + """ + return {} + + def _from_sale(self): + return """ + sale_order_line l + RIGHT OUTER JOIN sale_order s ON s.id=l.order_id + JOIN res_partner partner ON s.partner_id = partner.id + LEFT JOIN product_product p ON l.product_id=p.id + LEFT JOIN product_template t ON p.product_tmpl_id=t.id + LEFT JOIN uom_uom u ON u.id=l.product_uom + LEFT JOIN uom_uom u2 ON u2.id=t.uom_id""" + + def _where_sale(self): + return """ + l.display_type IS NULL""" + + def _group_by_sale(self): + return """ l.product_id, l.order_id, t.uom_id, @@ -141,18 +152,19 @@ class SaleReport(models.Model): partner.industry_id, partner.commercial_partner_id, l.discount, - s.id %s - """ % (groupby) - return groupby_ + s.id""" - def _query(self, with_clause='', fields=None, groupby='', from_clause=''): - if not fields: - fields = {} - with_ = ("WITH %s" % with_clause) if with_clause else "" - return '%s (SELECT %s FROM %s WHERE l.display_type IS NULL GROUP BY %s)' % \ - (with_, self._select_sale(fields), self._from_sale(from_clause), self._group_by_sale(groupby)) + def _query(self): + with_ = self._with_sale() + return f""" + {"WITH" + with_ + "(" if with_ else ""} + SELECT {self._select_sale()} + FROM {self._from_sale()} + WHERE {self._where_sale()} + GROUP BY {self._group_by_sale()} + {")" if with_ else ""} + """ def init(self): - # self._table = sale_report tools.drop_view_if_exists(self.env.cr, self._table) - self.env.cr.execute("""CREATE or REPLACE VIEW %s as (%s)""" % (self._table, self._query())) + self.env.cr.execute(f"CREATE OR REPLACE VIEW {self._table} AS {self._query()}") diff --git a/addons/sale_margin/report/sale_report.py b/addons/sale_margin/report/sale_report.py index 1ac87891e97cd35e94430b6f29b9ea5eaacde52c..e43a053a351700e914e0ee50331c9decbebe8bb2 100644 --- a/addons/sale_margin/report/sale_report.py +++ b/addons/sale_margin/report/sale_report.py @@ -9,6 +9,7 @@ class SaleReport(models.Model): margin = fields.Float('Margin') - def _query(self, with_clause='', fields={}, groupby='', from_clause=''): - fields['margin'] = ", SUM(l.margin / CASE COALESCE(s.currency_rate, 0) WHEN 0 THEN 1.0 ELSE s.currency_rate END) AS margin" - return super(SaleReport, self)._query(with_clause, fields, groupby, from_clause) + def _select_additional_fields(self): + res = super()._select_additional_fields() + res['margin'] = "SUM(l.margin / CASE COALESCE(s.currency_rate, 0) WHEN 0 THEN 1.0 ELSE s.currency_rate END)" + return res diff --git a/addons/sale_stock/report/sale_report.py b/addons/sale_stock/report/sale_report.py index 3c62a27930d4c81fb34ed26781a163b3f29ac15c..e4a5e74c7a4a274a4a7afd2e97a6271d7a701f30 100644 --- a/addons/sale_stock/report/sale_report.py +++ b/addons/sale_stock/report/sale_report.py @@ -9,7 +9,13 @@ class SaleReport(models.Model): warehouse_id = fields.Many2one('stock.warehouse', 'Warehouse', readonly=True) - def _query(self, with_clause='', fields={}, groupby='', from_clause=''): - fields['warehouse_id'] = ", s.warehouse_id as warehouse_id" - groupby += ', s.warehouse_id' - return super(SaleReport, self)._query(with_clause, fields, groupby, from_clause) + def _select_additional_fields(self): + res = super()._select_additional_fields() + res['warehouse_id'] = "s.warehouse_id" + return res + + def _group_by_sale(self): + res = super()._group_by_sale() + res += """, + s.warehouse_id""" + return res diff --git a/addons/website_sale/report/sale_report.py b/addons/website_sale/report/sale_report.py index 6ecf7967cadf9c835a438a2880b1fe6edf7416ae..560e54b2eaa37719d4a5c117cf69c943766813bc 100644 --- a/addons/website_sale/report/sale_report.py +++ b/addons/website_sale/report/sale_report.py @@ -8,7 +8,13 @@ class SaleReport(models.Model): website_id = fields.Many2one('website', readonly=True) - def _query(self, with_clause='', fields={}, groupby='', from_clause=''): - fields['website_id'] = ", s.website_id as website_id" - groupby += ', s.website_id' - return super(SaleReport, self)._query(with_clause, fields, groupby, from_clause) + def _select_additional_fields(self): + res = super()._select_additional_fields() + res['website_id'] = "s.website_id" + return res + + def _group_by_sale(self): + res = super()._group_by_sale() + res += """, + s.website_id""" + return res