You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

291 lines
11 KiB

# -*- 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