250 lines
9.0 KiB
Python
250 lines
9.0 KiB
Python
# The COPYRIGHT file at the top level of
|
|
# this repository contains the full copyright notices and license terms.
|
|
import itertools
|
|
from functools import partial
|
|
from trytond.model import ModelView, fields, ModelSQL
|
|
from trytond.pool import PoolMeta, Pool
|
|
from trytond.pyson import Eval
|
|
from trytond.report import Report
|
|
from trytond.transaction import Transaction
|
|
from trytond.wizard import StateReport
|
|
from trytond.wizard import Wizard, StateTransition, StateView, Button
|
|
from trytond.tools.multivalue import migrate_property
|
|
from trytond.modules.company.model import CompanyValueMixin
|
|
|
|
# XXX fix: https://genshi.edgewall.org/ticket/582
|
|
from genshi.template.astutil import ASTCodeGenerator, ASTTransformer
|
|
if not hasattr(ASTCodeGenerator, 'visit_NameConstant'):
|
|
def visit_NameConstant(self, node):
|
|
if node.value is None:
|
|
self._write('None')
|
|
elif node.value is True:
|
|
self._write('True')
|
|
elif node.value is False:
|
|
self._write('False')
|
|
else:
|
|
raise Exception("Unknown NameConstant %r" % (node.value,))
|
|
ASTCodeGenerator.visit_NameConstant = visit_NameConstant
|
|
if not hasattr(ASTTransformer, 'visit_NameConstant'):
|
|
# Re-use visit_Name because _clone is deleted
|
|
ASTTransformer.visit_NameConstant = ASTTransformer.visit_Name
|
|
|
|
|
|
class SaleConfiguration(metaclass=PoolMeta):
|
|
__name__ = 'sale.configuration'
|
|
|
|
report_country_filter = fields.MultiValue(
|
|
fields.Char('Default countries for report',
|
|
help='String with IDs of countries concatenated with ";".')
|
|
)
|
|
|
|
def get_report_countries(self):
|
|
if not self.report_country_filter:
|
|
return []
|
|
return list(map(int, self.report_country_filter.split(';')))
|
|
|
|
|
|
class ConfigurationReportCountryFilter(ModelSQL, CompanyValueMixin):
|
|
"Sale Configuration Report Country Filter"
|
|
__name__ = 'sale.configuration.report_country_filter'
|
|
|
|
report_country_filter = fields.Char('Default countries for report',
|
|
help='String with IDs of countries concatenated with ";".')
|
|
|
|
@classmethod
|
|
def __register__(cls, module_name):
|
|
table_h = cls.__table_handler__(module_name)
|
|
exist = table_h.table_exist(cls._table)
|
|
|
|
super(ConfigurationReportCountryFilter, cls).__register__(module_name)
|
|
|
|
if not exist:
|
|
cls._migrate_property([], [], [])
|
|
|
|
@classmethod
|
|
def _migrate_property(cls, field_names, value_names, fields):
|
|
field_names.append('report_country_filter')
|
|
value_names.append('report_country_filter')
|
|
fields.append('company')
|
|
migrate_property(
|
|
'sale.configuration', field_names, cls, value_names,
|
|
fields=fields)
|
|
|
|
|
|
class SaleCountryNote(Report):
|
|
"""Sales per country note"""
|
|
__name__ = 'sale.sale_per_country'
|
|
|
|
@classmethod
|
|
def get_context(cls, records, header, data):
|
|
pool = Pool()
|
|
Company = pool.get('company.company')
|
|
Uom = pool.get('product.uom')
|
|
Saleline = pool.get('sale.line')
|
|
Country = pool.get('country.country')
|
|
Category = pool.get('product.category')
|
|
|
|
report_context = super(SaleCountryNote, cls).get_context(records,
|
|
header, data)
|
|
|
|
company = Company(data['company'])
|
|
_address = company.party.address_get()
|
|
country_filter = cls._get_country_criteria()
|
|
_states = ['confirmed', 'processing', 'done']
|
|
if data['quotation']:
|
|
_states.append('quotation')
|
|
|
|
address_country = 'sale.%s_address.country' % data['address_type']
|
|
|
|
report_context['company'] = company
|
|
report_context['start_date'] = data['start_date']
|
|
report_context['end_date'] = data['end_date']
|
|
_domain = [('sale.sale_date', '>=', data['start_date']),
|
|
('sale.sale_date', '<=', data['end_date']),
|
|
('sale.company', '=', report_context['company']),
|
|
('sale.state', 'in', _states),
|
|
('type', '=', 'line'),
|
|
('product', '!=', None)]
|
|
if data['type'] != 'all':
|
|
_domain.append((address_country, country_filter[data['type']],
|
|
_address.country))
|
|
if data['category']:
|
|
cats = Category.search([
|
|
('parent', 'child_of', data['category'])])
|
|
_domain.append(
|
|
('product.categories', 'in', [c.id for c in cats]))
|
|
if data['countries']:
|
|
_domain.append((address_country, 'in', data['countries']))
|
|
lines = Saleline.search(_domain)
|
|
|
|
res = {}
|
|
|
|
keyfunc = partial(cls._get_grouped_key, data['address_type'])
|
|
lines = sorted(lines, key=keyfunc)
|
|
country_ids = {}
|
|
for key, grouped_lines in itertools.groupby(lines, key=keyfunc):
|
|
grouped_lines = list(grouped_lines)
|
|
res.setdefault(key[0], [])
|
|
if key[1] and key[1] not in country_ids:
|
|
country_ids[key[1]] = Country(key[1])
|
|
res[key[0]].append({
|
|
'item': country_ids.get(key[1], None),
|
|
'quantity': sum(Uom.compute_qty(
|
|
l.unit, l.quantity, l.product.default_uom)
|
|
for l in grouped_lines),
|
|
'uom': grouped_lines[0].product.default_uom
|
|
})
|
|
|
|
if data['grouping'] == 'country':
|
|
new_res = {}
|
|
for key, values in res.items():
|
|
for value in values:
|
|
new_item = value.copy()
|
|
new_item['item'] = key
|
|
new_res.setdefault(value['item'], []).append(new_item)
|
|
res = new_res
|
|
|
|
info = []
|
|
for key, values in res.items():
|
|
new_values = sorted(values,
|
|
key=lambda x: x['item'] and x['item'].rec_name or '')
|
|
info.append((key, new_values))
|
|
info = sorted(info, key=lambda x: x[0] and x[0].rec_name or '')
|
|
|
|
report_context['products'] = info
|
|
return report_context
|
|
|
|
@staticmethod
|
|
def _get_country_criteria():
|
|
return {'export': '!=',
|
|
'national': '='}
|
|
|
|
@classmethod
|
|
def _get_grouped_key(cls, address_type, line):
|
|
address = getattr(line.sale, '%s_address' % address_type)
|
|
return (line.product, address.country and address.country.id or 0, )
|
|
|
|
|
|
class PrintSaleCountryNoteParam(ModelView):
|
|
"""Sales per country note param"""
|
|
__name__ = 'sale.sale_per_country.params'
|
|
|
|
start_date = fields.Date('Start date', required=True)
|
|
end_date = fields.Date('End date', required=True)
|
|
category = fields.Many2One('product.category', 'Product category')
|
|
quotation = fields.Boolean('Consider quotations')
|
|
type_ = fields.Selection([('export', 'Export'),
|
|
('national', 'National'),
|
|
('all', 'All')], 'Type', required=True)
|
|
restrict_countries = fields.Boolean('Restrict countries')
|
|
countries = fields.One2Many('country.country', None, 'Countries',
|
|
states={'invisible': ~Eval('restrict_countries')},
|
|
depends=['restrict_countries'])
|
|
invoice_address = fields.Boolean('Use invoice address')
|
|
grouping = fields.Selection([
|
|
('product', 'Product'),
|
|
('country', 'Country')], 'Grouping', required=True)
|
|
|
|
@staticmethod
|
|
def default_type_():
|
|
return 'export'
|
|
|
|
@classmethod
|
|
def default_quotation(cls):
|
|
return True
|
|
|
|
@classmethod
|
|
def default_restrict_countries(cls):
|
|
return cls.default_countries() != []
|
|
|
|
@classmethod
|
|
def default_countries(cls):
|
|
Conf = Pool().get('sale.configuration')
|
|
conf = Conf(1)
|
|
return conf.get_report_countries()
|
|
|
|
@classmethod
|
|
def default_invoice_address(cls):
|
|
return False
|
|
|
|
@classmethod
|
|
def default_grouping(cls):
|
|
return 'product'
|
|
|
|
|
|
class PrintSaleCountryNote(Wizard):
|
|
"""Print Sales per country note"""
|
|
__name__ = 'sale.sale_per_country.print'
|
|
|
|
start = StateTransition()
|
|
params = StateView('sale.sale_per_country.params',
|
|
'sale_per_country_report.sale_per_country_params_view_form',
|
|
[Button('Cancel', 'end', 'tryton-cancel'),
|
|
Button('Print', 'print_', 'tryton-print', default=True)])
|
|
print_ = StateReport('sale.sale_per_country')
|
|
|
|
def transition_start(self):
|
|
return 'params'
|
|
|
|
def do_print_(self, action):
|
|
data = {}
|
|
|
|
if Transaction().context.get('active_ids'):
|
|
_ = Transaction().context['active_ids'].pop()
|
|
|
|
data['company'] = Transaction().context['company']
|
|
data['start_date'] = self.params.start_date
|
|
data['end_date'] = self.params.end_date
|
|
data['category'] = \
|
|
self.params.category.id if self.params.category else None
|
|
data['type'] = self.params.type_
|
|
data['quotation'] = self.params.quotation
|
|
data['countries'] = (list(map(int, self.params.countries))
|
|
if self.params.countries and self.params.restrict_countries
|
|
else [])
|
|
data['address_type'] = \
|
|
'invoice' if self.params.invoice_address else 'shipment'
|
|
data['grouping'] = self.params.grouping
|
|
|
|
return action, data
|