Browse Source
Add a new module 'pos_top_sellers'.
Add a new module 'pos_top_sellers'.
Add new statistics to point of sale report section to show your best selling products for all of your shops over a given date range.pull/22/head
Peter Hahn
10 years ago
9 changed files with 702 additions and 0 deletions
-
21pos_top_sellers/__init__.py
-
73pos_top_sellers/__openerp__.py
-
72pos_top_sellers/i18n/de.po
-
3pos_top_sellers/ir.model.access.csv
-
291pos_top_sellers/pos_top_sellers.py
-
83pos_top_sellers/pos_top_sellers_view.xml
-
4pos_top_sellers/static/src/css/pos_top_sellers.css
-
143pos_top_sellers/static/src/js/pos_top_sellers.js
-
12pos_top_sellers/static/src/xml/pos_top_sellers.xml
@ -0,0 +1,21 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################### |
||||
|
# |
||||
|
# Copyright (C) 2015 initOS GmbH & Co. KG (<http://www.initos.com>). |
||||
|
# |
||||
|
# This program is free software: you can redistribute it and/or modify |
||||
|
# it under the terms of the GNU Affero General Public License as |
||||
|
# published by the Free Software Foundation, either version 3 of the |
||||
|
# License, or (at your option) any later version. |
||||
|
# |
||||
|
# This program is distributed in the hope that it will be useful, |
||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
# GNU Affero General Public License for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU Affero General Public License |
||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################### |
||||
|
|
||||
|
from . import pos_top_sellers |
@ -0,0 +1,73 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################### |
||||
|
# |
||||
|
# Copyright (C) 2015 initOS GmbH & Co. KG (<http://www.initos.com>). |
||||
|
# |
||||
|
# This program is free software: you can redistribute it and/or modify |
||||
|
# it under the terms of the GNU Affero General Public License as |
||||
|
# published by the Free Software Foundation, either version 3 of the |
||||
|
# License, or (at your option) any later version. |
||||
|
# |
||||
|
# This program is distributed in the hope that it will be useful, |
||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
# GNU Affero General Public License for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU Affero General Public License |
||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################### |
||||
|
|
||||
|
{ |
||||
|
'name': 'Point of sale topseller report.', |
||||
|
'version': '1.0', |
||||
|
'category': '', |
||||
|
'summary': 'Report topseller products in point of sale for all of your shops.', |
||||
|
'description': """ |
||||
|
Report top selling products for point of sale |
||||
|
============================================= |
||||
|
|
||||
|
This module adds 2 new reports to report sales done via point of sale for all of |
||||
|
your companies shops. |
||||
|
|
||||
|
Top 40 sales by shop |
||||
|
-------------------- |
||||
|
* List top 40 sold products in a given period for all of your shops. |
||||
|
* Click on a product to show sales for this product by shop on a daily basis over that timeframe. |
||||
|
|
||||
|
Recent sales by shop for a given product |
||||
|
---------------------------------------- |
||||
|
* Show sales for a product by shop on a daily basis over a given period. |
||||
|
* Select product by product default code. |
||||
|
* Show current stock and total quantity of sales for this product and period. |
||||
|
""", |
||||
|
'author': 'initOS GmbH & Co. KG', |
||||
|
'website': 'http://www.initos.com', |
||||
|
'depends': [ |
||||
|
'web', |
||||
|
'web_listview_date_range_bar', |
||||
|
'sale', |
||||
|
'product', |
||||
|
'point_of_sale', |
||||
|
], |
||||
|
'data': [ |
||||
|
'ir.model.access.csv', |
||||
|
'pos_top_sellers_view.xml', |
||||
|
], |
||||
|
'demo': [ |
||||
|
], |
||||
|
'installable': True, |
||||
|
'auto_install': False, |
||||
|
'application': False, |
||||
|
'images': [ |
||||
|
], |
||||
|
'css': [ |
||||
|
'static/src/css/pos_top_sellers.css', |
||||
|
], |
||||
|
'js': [ |
||||
|
'static/src/js/pos_top_sellers.js', |
||||
|
], |
||||
|
'qweb': [ |
||||
|
'static/src/xml/pos_top_sellers.xml', |
||||
|
], |
||||
|
} |
@ -0,0 +1,72 @@ |
|||||
|
# Translation of OpenERP Server. |
||||
|
# This file contains the translation of the following modules: |
||||
|
# * pos_top_sellers |
||||
|
# |
||||
|
msgid "" |
||||
|
msgstr "" |
||||
|
"Project-Id-Version: OpenERP Server 7.0\n" |
||||
|
"Report-Msgid-Bugs-To: \n" |
||||
|
"POT-Creation-Date: 2015-04-16 07:54+0000\n" |
||||
|
"PO-Revision-Date: 2015-04-16 09:54+0100\n" |
||||
|
"Last-Translator: \n" |
||||
|
"Language-Team: \n" |
||||
|
"Language: de\n" |
||||
|
"MIME-Version: 1.0\n" |
||||
|
"Content-Type: text/plain; charset=UTF-8\n" |
||||
|
"Content-Transfer-Encoding: 8bit\n" |
||||
|
"X-Generator: Poedit 1.5.4\n" |
||||
|
|
||||
|
#. module: pos_top_sellers |
||||
|
#: code:addons/pos_top_sellers/pos_top_sellers.py:117 |
||||
|
#, python-format |
||||
|
msgid "Currently in stock" |
||||
|
msgstr "Lagerbestand" |
||||
|
|
||||
|
#. module: pos_top_sellers |
||||
|
#: code:addons/pos_top_sellers/pos_top_sellers.py:214 |
||||
|
#, python-format |
||||
|
msgid "QT" |
||||
|
msgstr "Menge" |
||||
|
|
||||
|
#. module: pos_top_sellers |
||||
|
#: model:ir.model,name:pos_top_sellers.model_pos_top_sellers_shop_report |
||||
|
msgid "pos.top.sellers.shop.report" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: pos_top_sellers |
||||
|
#: code:addons/pos_top_sellers/pos_top_sellers.py:116 |
||||
|
#, python-format |
||||
|
msgid "Sold" |
||||
|
msgstr "Verkäufe" |
||||
|
|
||||
|
#. module: pos_top_sellers |
||||
|
#: model:ir.actions.act_window,name:pos_top_sellers.action_pos_top_sellers_shop_report |
||||
|
#: model:ir.ui.menu,name:pos_top_sellers.menu_pos_top_sellers_shop_report |
||||
|
#: view:pos.top.sellers.shop.report:0 |
||||
|
msgid "Best sellers by shop" |
||||
|
msgstr "Verkaufscharts Shop" |
||||
|
|
||||
|
#. module: pos_top_sellers |
||||
|
#: model:ir.actions.act_window,name:pos_top_sellers.action_pos_top_sellers_product_report |
||||
|
#: model:ir.ui.menu,name:pos_top_sellers.menu_pos_top_sellers_product_report |
||||
|
#: view:pos.top.sellers.product.report:0 |
||||
|
msgid "Recent sales by product/shop" |
||||
|
msgstr "Verkäufe Produkt/Shop" |
||||
|
|
||||
|
#. module: pos_top_sellers |
||||
|
#. openerp-web |
||||
|
#: code:addons/pos_top_sellers/static/src/xml/pos_top_sellers.xml:7 |
||||
|
#, python-format |
||||
|
msgid "Product code:" |
||||
|
msgstr "Artikelnummer:" |
||||
|
|
||||
|
#. module: pos_top_sellers |
||||
|
#: model:ir.model,name:pos_top_sellers.model_pos_top_sellers_product_report |
||||
|
msgid "pos.top.sellers.product.report" |
||||
|
msgstr "" |
||||
|
|
||||
|
#~ msgid "Date from:" |
||||
|
#~ msgstr "Anfangsdatum:" |
||||
|
|
||||
|
#~ msgid "Date end:" |
||||
|
#~ msgstr "Enddatum" |
@ -0,0 +1,3 @@ |
|||||
|
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink |
||||
|
access_pos_top_sellers_product_report,Access to pos.top.sellers.product.report,model_pos_top_sellers_product_report,point_of_sale.group_pos_manager,1,0,0,0 |
||||
|
access_pos_top_sellers_shop_report,Access to pos.top.sellers.shop.report,model_pos_top_sellers_shop_report,point_of_sale.group_pos_manager,1,0,0,0 |
@ -0,0 +1,291 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################## |
||||
|
# |
||||
|
# OpenERP, Open Source Management Solution |
||||
|
# Copyright (C) 2010-2013 OpenERP s.a. (<http://openerp.com>). |
||||
|
# Copyright (C) 2015 initOS GmbH & Co. KG (<http://www.initos.com>). |
||||
|
# |
||||
|
# This program is free software: you can redistribute it and/or modify |
||||
|
# it under the terms of the GNU Affero General Public License as |
||||
|
# published by the Free Software Foundation, either version 3 of the |
||||
|
# License, or (at your option) any later version. |
||||
|
# |
||||
|
# This program is distributed in the hope that it will be useful, |
||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
# GNU Affero General Public License for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU Affero General Public License |
||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################## |
||||
|
|
||||
|
from openerp.osv import orm, fields |
||||
|
from openerp.tools.translate import _ |
||||
|
from datetime import datetime, timedelta |
||||
|
from openerp.tools import DEFAULT_SERVER_DATE_FORMAT |
||||
|
from lxml import etree |
||||
|
|
||||
|
|
||||
|
class pos_top_sellers_product_report(orm.Model): |
||||
|
_name = 'pos.top.sellers.product.report' |
||||
|
|
||||
|
# Create no table. Everything is created dynamically in this model |
||||
|
_auto = False |
||||
|
|
||||
|
_columns = dict(date = fields.char(string='', readonly=True)) |
||||
|
|
||||
|
def get_product_code_for_id(self, cr, uid, id, context=None): |
||||
|
prod = self.pool['product.product'].browse(cr, uid, id, context) |
||||
|
return prod.default_code |
||||
|
|
||||
|
def get_product_id_for_code(self, cr, uid, default_code, context=None): |
||||
|
ids = self.pool['product.product'].search(cr, uid, [('default_code','=', default_code)], context) |
||||
|
return ids[0] if ids else 0 |
||||
|
|
||||
|
def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False): |
||||
|
res = super(pos_top_sellers_product_report, self).\ |
||||
|
fields_view_get(cr, uid, view_id=view_id, view_type=view_type, |
||||
|
context=context, toolbar=toolbar) |
||||
|
|
||||
|
if view_type == 'tree': |
||||
|
shop_model = self.pool['sale.shop'] |
||||
|
shop_ids = shop_model.search(cr, uid, []) |
||||
|
arch = etree.XML(res['arch']) |
||||
|
tree = arch.xpath("//tree") |
||||
|
for shop in shop_model.browse(cr, uid, shop_ids): |
||||
|
# create fields for shop |
||||
|
qty_key = 'qty_' + str(shop.id) |
||||
|
res['fields'].update({ |
||||
|
qty_key: dict( |
||||
|
string=shop.name, |
||||
|
type='integer', |
||||
|
readonly='True' |
||||
|
) |
||||
|
}) |
||||
|
|
||||
|
# add field to tree |
||||
|
etree.SubElement(tree[0], 'field', dict( |
||||
|
name=qty_key |
||||
|
)) |
||||
|
res['arch'] = etree.tostring(arch) |
||||
|
return res |
||||
|
|
||||
|
def _get_context_date_range(self, cr, uid, context=None): |
||||
|
""" |
||||
|
Check date range from context and create date range for the past |
||||
|
30 days if date range is missing in context. |
||||
|
""" |
||||
|
date_from = context and context.get('list_date_range_bar_start') |
||||
|
date_to = context and context.get('list_date_range_bar_end') |
||||
|
if not date_to: |
||||
|
date_to = fields.date.context_today(self, cr, uid, context=context) |
||||
|
|
||||
|
if not date_from: |
||||
|
timestamp = datetime.strptime(date_to, DEFAULT_SERVER_DATE_FORMAT) |
||||
|
timestamp -= timedelta(days=30) |
||||
|
date_from = fields.date.context_today(self, cr, uid, context=context, timestamp=timestamp) |
||||
|
|
||||
|
return (date_from, date_to) |
||||
|
|
||||
|
def search(self, cr, user, args, offset=0, limit=None, order=None, context=None, count=False): |
||||
|
product_id = context and context.get('my_res_id') |
||||
|
date_from, date_to = self._get_context_date_range(cr, user, context=context) |
||||
|
res = [] |
||||
|
|
||||
|
if product_id and date_from and date_to: |
||||
|
d0 = datetime.strptime(date_from, DEFAULT_SERVER_DATE_FORMAT) |
||||
|
d1 = datetime.strptime(date_to, DEFAULT_SERVER_DATE_FORMAT) |
||||
|
|
||||
|
# range depends on number of days in date range |
||||
|
num_days = abs((d1 - d0).days)+1 |
||||
|
res = range(1, 1+2+num_days) |
||||
|
|
||||
|
return res |
||||
|
|
||||
|
def read(self, cr, user, ids, fields=None, context=None, load='_classic_read'): |
||||
|
# create empty result lines |
||||
|
res = [dict(id=id) for id in ids] |
||||
|
product_id = context and context.get('my_res_id') |
||||
|
date_from, date_to = self._get_context_date_range(cr, user, context=context) |
||||
|
|
||||
|
if not (product_id and date_from and date_to): |
||||
|
return res |
||||
|
|
||||
|
# first two lines are summary of sales and stock |
||||
|
res[0].update(date=_('Sold')) |
||||
|
res[1].update(date=_('Currently in stock')) |
||||
|
|
||||
|
# remaining lines are sales top statistics per date |
||||
|
for shop_id in self.pool['sale.shop'].search(cr, user, [], context=context): |
||||
|
sql=''' |
||||
|
select |
||||
|
date_trunc('day', dd)::date as date |
||||
|
,COALESCE(pd.qty, 0) as qty |
||||
|
from generate_series |
||||
|
( %(date_from)s |
||||
|
, %(date_to)s |
||||
|
, '1 day'::interval) as dd |
||||
|
left join ( |
||||
|
select |
||||
|
po.date_order::date as date |
||||
|
,sum(pol.qty) as qty |
||||
|
from pos_order_line pol |
||||
|
join pos_order po |
||||
|
on po.id = pol.order_id |
||||
|
join product_product pp |
||||
|
on pp.id = product_id |
||||
|
join product_template pt |
||||
|
on pp.product_tmpl_id = pt.id |
||||
|
where pt.list_price > 0 and |
||||
|
shop_id = %(shop_id)s and product_id = %(product_id)s |
||||
|
group by |
||||
|
po.date_order::date |
||||
|
) as pd |
||||
|
on pd.date = dd.date |
||||
|
order by |
||||
|
date desc |
||||
|
''' |
||||
|
cr.execute(sql, dict(shop_id=shop_id, product_id=product_id, |
||||
|
date_from=date_from, date_to=date_to)) |
||||
|
query_result = cr.fetchall() |
||||
|
|
||||
|
qty_key = 'qty_' + str(shop_id) |
||||
|
|
||||
|
line_id=2 |
||||
|
for date, qty in query_result: |
||||
|
res[line_id].update({ |
||||
|
'date': date, |
||||
|
qty_key: qty |
||||
|
}) |
||||
|
total_qty = res[0].get(qty_key, 0) + qty |
||||
|
res[0].update({qty_key: total_qty}) |
||||
|
line_id += 1 |
||||
|
|
||||
|
ctx = dict(context or {}) |
||||
|
ctx.update({'shop': shop_id, |
||||
|
'states': ('done',), |
||||
|
'what': ('in', 'out')}) |
||||
|
product = self.pool['product.product'].browse(cr, user, product_id, context=ctx) |
||||
|
res[1].update({qty_key: product.qty_available}) |
||||
|
|
||||
|
|
||||
|
# postprocess |
||||
|
for line in res[2:]: |
||||
|
# transform date format |
||||
|
# fixme: note this uses the server locale not the user language set in odoo |
||||
|
# to really do this right we need to format this in JS on the client side |
||||
|
date = datetime.strptime(line['date'], '%Y-%m-%d') |
||||
|
line['date'] = date.strftime('%A, %x') |
||||
|
|
||||
|
return res |
||||
|
|
||||
|
class pos_top_sellers_shop_report(orm.Model): |
||||
|
_name = 'pos.top.sellers.shop.report' |
||||
|
|
||||
|
# We do not have columns. Everything is created dynamically in this model |
||||
|
_auto = False |
||||
|
|
||||
|
# by default list the top 40 products |
||||
|
_top_ten_limit = 40 |
||||
|
|
||||
|
def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False): |
||||
|
res = super(pos_top_sellers_shop_report, self).\ |
||||
|
fields_view_get(cr, uid, view_id=view_id, view_type=view_type, |
||||
|
context=context, toolbar=toolbar) |
||||
|
|
||||
|
if view_type == 'tree': |
||||
|
shop_model = self.pool['sale.shop'] |
||||
|
shop_ids = shop_model.search(cr, uid, []) |
||||
|
arch = etree.XML(res['arch']) |
||||
|
tree = arch.xpath("//tree") |
||||
|
for shop in shop_model.browse(cr, uid, shop_ids): |
||||
|
# create fields for shop |
||||
|
product_key = 'product_id_' + str(shop.id) |
||||
|
qty_key = 'qty_' + str(shop.id) |
||||
|
res['fields'].update({ |
||||
|
product_key: dict( |
||||
|
string=shop.name, |
||||
|
type='many2one', |
||||
|
relation='product.product', |
||||
|
readonly='True', |
||||
|
), |
||||
|
qty_key: dict( |
||||
|
string=_('QT'), |
||||
|
type='integer', |
||||
|
readonly='True' |
||||
|
) |
||||
|
}) |
||||
|
|
||||
|
# add field to tree |
||||
|
etree.SubElement(tree[0], 'field', dict( |
||||
|
name=product_key, |
||||
|
widget="pos_top_sellers_product_col", |
||||
|
)) |
||||
|
etree.SubElement(tree[0], 'field', dict( |
||||
|
name=qty_key |
||||
|
)) |
||||
|
|
||||
|
res['arch'] = etree.tostring(arch) |
||||
|
return res |
||||
|
|
||||
|
def search(self, cr, user, args, offset=0, limit=None, order=None, context=None, count=False): |
||||
|
# ignore sorting, limit etc |
||||
|
return range(1,1+self._top_ten_limit) |
||||
|
|
||||
|
def read(self, cr, user, ids, fields=None, context=None, load='_classic_read'): |
||||
|
|
||||
|
date_from = context and context.get('list_date_range_bar_start') |
||||
|
date_to = context and context.get('list_date_range_bar_end') |
||||
|
|
||||
|
# create empty result lines |
||||
|
res = [dict(id=id) for id in ids] |
||||
|
limit = len(res) |
||||
|
|
||||
|
for shop_id in self.pool['sale.shop'].search(cr, user, [], context=context): |
||||
|
sql=''' |
||||
|
select |
||||
|
product_id |
||||
|
,COALESCE(default_code, pt.name) |
||||
|
,sum(pol.qty) as qty |
||||
|
from pos_order_line pol |
||||
|
join pos_order po |
||||
|
on po.id = pol.order_id |
||||
|
join product_product pp |
||||
|
on pp.id = product_id |
||||
|
join product_template pt |
||||
|
on pp.product_tmpl_id = pt.id |
||||
|
where pt.list_price > 0 and |
||||
|
shop_id = %(shop_id)s |
||||
|
''' |
||||
|
|
||||
|
if date_from: |
||||
|
sql += '''and po.date_order::date >= %(date_from)s ''' |
||||
|
|
||||
|
if date_to: |
||||
|
sql += '''and po.date_order::date <= %(date_to)s ''' |
||||
|
|
||||
|
sql += \ |
||||
|
''' |
||||
|
group by |
||||
|
product_id |
||||
|
,COALESCE(default_code, pt.name) |
||||
|
order by |
||||
|
qty desc |
||||
|
fetch first %(limit)s rows only |
||||
|
''' |
||||
|
cr.execute(sql, dict(shop_id=shop_id, limit=limit, |
||||
|
date_from=date_from, date_to=date_to)) |
||||
|
|
||||
|
product_key = 'product_id_' + str(shop_id) |
||||
|
qty_key = 'qty_' + str(shop_id) |
||||
|
|
||||
|
line_id=0 |
||||
|
for product_id, default_code, qty in cr.fetchall(): |
||||
|
res[line_id].update({ |
||||
|
product_key: (product_id, default_code), |
||||
|
qty_key: qty |
||||
|
}) |
||||
|
line_id += 1 |
||||
|
|
||||
|
return res |
@ -0,0 +1,83 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<!-- |
||||
|
############################################################################## |
||||
|
# |
||||
|
# OpenERP, Open Source Management Solution |
||||
|
# Copyright (C) 2010-2013 OpenERP s.a. (<http://openerp.com>). |
||||
|
# Copyright (C) 2015 initOS GmbH & Co. KG (<http://www.initos.com>). |
||||
|
# |
||||
|
# This program is free software: you can redistribute it and/or modify |
||||
|
# it under the terms of the GNU Affero General Public License as |
||||
|
# published by the Free Software Foundation, either version 3 of the |
||||
|
# License, or (at your option) any later version. |
||||
|
# |
||||
|
# This program is distributed in the hope that it will be useful, |
||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
# GNU Affero General Public License for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU Affero General Public License |
||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################## |
||||
|
--> |
||||
|
|
||||
|
<openerp> |
||||
|
<data> |
||||
|
|
||||
|
<record id="view_pos_top_sellers_shop_report" model="ir.ui.view"> |
||||
|
<field name="name">pos.top.sellers.shop.report</field> |
||||
|
<field name="model">pos.top.sellers.shop.report</field> |
||||
|
<field name="arch" type="xml"> |
||||
|
<tree string="Best sellers by shop" create='false'/> |
||||
|
<!-- |
||||
|
Content is created dynamically in fields_view_get() |
||||
|
Do not add fields here! |
||||
|
Do not inherit this view! |
||||
|
--> |
||||
|
</field> |
||||
|
</record> |
||||
|
|
||||
|
<record id="action_pos_top_sellers_shop_report" model="ir.actions.act_window"> |
||||
|
<field name="name">Best sellers by shop</field> |
||||
|
<field name="type">ir.actions.act_window</field> |
||||
|
<field name="res_model">pos.top.sellers.shop.report</field> |
||||
|
<field name="view_type">form</field> |
||||
|
<field name="view_mode">pos_top_sellers_shop_view</field> |
||||
|
<field name="target">current</field> |
||||
|
</record> |
||||
|
|
||||
|
<record id="view_pos_top_sellers_product_report" model="ir.ui.view"> |
||||
|
<field name="name">pos.top.sellers.product.report</field> |
||||
|
<field name="model">pos.top.sellers.product.report</field> |
||||
|
<field name="arch" type="xml"> |
||||
|
<tree string="Recent sales by product/shop" create='false'> |
||||
|
<field name="date"/> |
||||
|
</tree> |
||||
|
<!-- |
||||
|
Content is created dynamically in fields_view_get() |
||||
|
Do not add fields here! |
||||
|
Do not inherit this view! |
||||
|
--> |
||||
|
</field> |
||||
|
</record> |
||||
|
|
||||
|
<record id="action_pos_top_sellers_product_report" model="ir.actions.act_window"> |
||||
|
<field name="name">Recent sales by product/shop</field> |
||||
|
<field name="type">ir.actions.act_window</field> |
||||
|
<field name="res_model">pos.top.sellers.product.report</field> |
||||
|
<field name="view_type">form</field> |
||||
|
<field name="view_mode">pos_top_sellers_product_view</field> |
||||
|
<field name="target">current</field> |
||||
|
</record> |
||||
|
|
||||
|
<menuitem name="Best sellers by shop" id="menu_pos_top_sellers_shop_report" |
||||
|
parent="point_of_sale.menu_point_rep" action="action_pos_top_sellers_shop_report" |
||||
|
sequence="40"/> |
||||
|
|
||||
|
<menuitem name="Recent sales by product/shop" id="menu_pos_top_sellers_product_report" |
||||
|
parent="point_of_sale.menu_point_rep" action="action_pos_top_sellers_product_report" |
||||
|
sequence="41"/> |
||||
|
|
||||
|
</data> |
||||
|
</openerp> |
@ -0,0 +1,4 @@ |
|||||
|
.oe_pos_top_sellers_product_id{ |
||||
|
margin-right: 1em; |
||||
|
} |
||||
|
|
@ -0,0 +1,143 @@ |
|||||
|
openerp.pos_top_sellers = function(instance) |
||||
|
{ |
||||
|
var _t = instance.web._t, |
||||
|
_lt = instance.web._lt; |
||||
|
var QWeb = instance.web.qweb; |
||||
|
|
||||
|
instance.web.list.columns.add( |
||||
|
'field.pos_top_sellers_product_col', |
||||
|
'instance.web.pos_top_sellers.PosTopSellersListClickableColumn'); |
||||
|
|
||||
|
instance.web.views.add( |
||||
|
'pos_top_sellers_shop_view', |
||||
|
'instance.web.pos_top_sellers.PosTopSellersShopListView' |
||||
|
); |
||||
|
|
||||
|
instance.web.views.add( |
||||
|
'pos_top_sellers_product_view', |
||||
|
'instance.web.pos_top_sellers.PosTopSellersProductListView' |
||||
|
); |
||||
|
|
||||
|
instance.web.pos_top_sellers = instance.web.pos_top_sellers || {}; |
||||
|
|
||||
|
instance.web.pos_top_sellers.PosTopSellersListClickableColumn = |
||||
|
instance.web.list.Column.extend({ |
||||
|
_format: function (row_data, options) |
||||
|
{ |
||||
|
var prod_id = row_data[this.id].value[0]; |
||||
|
var prod_name = _.escape(row_data[this.id].value[1] || options.value_if_empty); |
||||
|
|
||||
|
return _.str.sprintf('<a class="oe_form_uri" data-pos-t10-click-id="%s" >%s</a>', |
||||
|
prod_id, prod_name); |
||||
|
|
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
instance.web.ListView.List.include({ |
||||
|
render: function() |
||||
|
{ |
||||
|
var result = this._super(this, arguments), |
||||
|
self = this; |
||||
|
|
||||
|
this.$current.delegate('a[data-pos-t10-click-id]', |
||||
|
'click', function() |
||||
|
{ |
||||
|
// forward context from parent view
|
||||
|
var ctx = self.dataset.get_context().eval(); |
||||
|
$.extend(ctx, { |
||||
|
my_res_id: jQuery(this).data('pos-t10-click-id'), |
||||
|
}); |
||||
|
|
||||
|
self.view.do_action({ |
||||
|
type: 'ir.actions.act_window', |
||||
|
res_model: 'pos.top.sellers.product.report', |
||||
|
view_type: 'form', |
||||
|
view_mode: 'tree', |
||||
|
views: [[false, 'pos_top_sellers_product_view']], |
||||
|
}, |
||||
|
{ |
||||
|
additional_context: ctx |
||||
|
} |
||||
|
); |
||||
|
}); |
||||
|
|
||||
|
return result; |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
instance.web.pos_top_sellers.PosTopSellersShopListView = |
||||
|
instance.web.web_listview_date_range_bar.DateRangeBar.extend({ |
||||
|
|
||||
|
init: function(parent, dataset, view_id, _options) { |
||||
|
_options.selectable = false |
||||
|
_options.sortable = false |
||||
|
_options.reorderable = false |
||||
|
_options.search_view = false |
||||
|
this._super.apply(this, arguments); |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
instance.web.pos_top_sellers.PosTopSellersProductListView = |
||||
|
instance.web.pos_top_sellers.PosTopSellersShopListView.extend({ |
||||
|
|
||||
|
init: function(parent, dataset, view_id, options) { |
||||
|
this._super.apply(this, arguments); |
||||
|
}, |
||||
|
|
||||
|
start:function(){ |
||||
|
var tmp = this._super.apply(this, arguments); |
||||
|
var self = this; |
||||
|
var defs = []; |
||||
|
|
||||
|
this.$el.parent().find('.oe_list_date_range_bar_start'). |
||||
|
prepend(QWeb.render("pos_top_sellers_product_view_product_selector", {widget: this})); |
||||
|
|
||||
|
this.$el.parent().find('.oe_pos_top_sellers_product_id_input').change(function() { |
||||
|
var product_default_code = this.value === '' ? null : this.value; |
||||
|
if( product_default_code ){ |
||||
|
req = self.dataset.call('get_product_id_for_code', [product_default_code]); |
||||
|
req.then( |
||||
|
function(product_id){ |
||||
|
if(product_id) |
||||
|
{ |
||||
|
self.ViewManager.$el.find("span.oe_breadcrumb_item").text(product_default_code); |
||||
|
self.last_context["my_res_id"] = product_id; |
||||
|
self.do_search(self.last_domain, self.last_context, self.last_group_by); |
||||
|
} |
||||
|
} |
||||
|
); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
var ctx = this.dataset.get_context().eval(); |
||||
|
if ( ctx.my_res_id ) |
||||
|
{ |
||||
|
req = self.dataset.call('get_product_code_for_id', [ctx.my_res_id]) |
||||
|
req.then( |
||||
|
function(product_default_code){ |
||||
|
self.$el.parent().find('.oe_pos_top_sellers_product_id_input').val( product_default_code ); |
||||
|
self.ViewManager.$el.find("span.oe_breadcrumb_item").text(product_default_code); |
||||
|
} |
||||
|
); |
||||
|
defs.push(req); |
||||
|
} |
||||
|
|
||||
|
return $.when(tmp, defs); |
||||
|
}, |
||||
|
|
||||
|
style_for: function (record) { |
||||
|
var self = this; |
||||
|
var tmp = self._super.apply(this, arguments); |
||||
|
var id = record.attributes.id; |
||||
|
if( id == 1 ) |
||||
|
{ |
||||
|
tmp += "color: #FF0000" |
||||
|
} |
||||
|
if( id == 2 ) |
||||
|
{ |
||||
|
tmp += "color: #FFCC00" |
||||
|
} |
||||
|
return tmp; |
||||
|
}, |
||||
|
}); |
||||
|
}; |
@ -0,0 +1,12 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
|
||||
|
<templates id="template" xml:space="preserve"> |
||||
|
|
||||
|
<t t-name="pos_top_sellers_product_view_product_selector"> |
||||
|
<div class="oe_form_dropdown_section oe_pos_top_sellers_product_id"> |
||||
|
<span style="font-weight:bold;">Product code:</span> |
||||
|
<input class="oe_pos_top_sellers_product_id_input" size="30"/> |
||||
|
</div> |
||||
|
</t> |
||||
|
|
||||
|
</templates> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue