diff --git a/INSTALL b/INSTALL index 8912f214be..7156417a8c 100644 --- a/INSTALL +++ b/INSTALL @@ -4,7 +4,7 @@ Installing trytond_sale_opportunity Prerequisites ------------- - * Python 2.4 or later (http://www.python.org/) + * Python 2.5 or later (http://www.python.org/) * trytond (http://www.tryton.org/) * trytond_party (http://www.tryton.org/) * trytond_company (http://www.tryton.org/) diff --git a/opportunity.py b/opportunity.py index 17bc7017e6..959c8b5479 100644 --- a/opportunity.py +++ b/opportunity.py @@ -1,11 +1,13 @@ #This file is part of Tryton. The COPYRIGHT file at the top level of #this repository contains the full copyright notices and license terms. "Sales extension for managing leads and opportunities" +from __future__ import with_statement import datetime from trytond.model import ModelView, ModelSQL, ModelWorkflow, fields from trytond.wizard import Wizard from trytond.backend import FIELDS from trytond.pyson import Equal, Eval, Not, In, If, Get, PYSONEncoder +from trytond.transaction import Transaction STATES = [ ('lead', 'Lead'), @@ -21,6 +23,7 @@ _STATES_STOP = { 'readonly': In(Eval('state'), ['converted', 'lost', 'cancelled']), } + class SaleOpportunity(ModelWorkflow, ModelSQL, ModelView): 'Sale Opportunity' _name = "sale.opportunity" @@ -98,79 +101,70 @@ class SaleOpportunity(ModelWorkflow, ModelSQL, ModelView): 'button_lead': True, }) - def default_state(self, cursor, user, context=None): + def default_state(self): return 'lead' - def default_start_date(self, cursor, user, context=None): + def default_start_date(self): date_obj = self.pool.get('ir.date') - return date_obj.today(cursor, user, context=context) + return date_obj.today() - def default_probability(self, cursor, user, context=None): + def default_probability(self): return 50 - def default_company(self, cursor, user, context=None): - if context is None: - context = {} - return context.get('company', False) + def default_company(self): + return Transaction().context.get('company') or False - def default_employee(self, cursor, user_id, context=None): + def default_employee(self): user_obj = self.pool.get('res.user') employee_obj = self.pool.get('company.employee') - if context is None: - context = {} - employee_id = None - if context.get('employee'): - employee_id = context['employee'] + employee_id = False + if Transaction().context.get('employee'): + employee_id = Transaction().context['employee'] else: - user = user_obj.browse(cursor, user_id, user_id, context=context) + user = user_obj.browse(Transaction().user) if user.employee: employee_id = user.employee.id - if employee_id: - return employee_id - return False + return employee_id - def default_payment_term(self, cursor, user, context=None): + def default_payment_term(self): payment_term_obj = self.pool.get('account.invoice.payment_term') - payment_term_ids = payment_term_obj.search(cursor, user, - self.payment_term.domain, context=context) + payment_term_ids = payment_term_obj.search(self.payment_term.domain) if len(payment_term_ids) == 1: return payment_term_ids[0] return False - def default_warehouse(self, cursor, user, context=None): + def default_warehouse(self): location_obj = self.pool.get('stock.location') - location_ids = location_obj.search(cursor, user, - self.warehouse.domain, context=context) + location_ids = location_obj.search(self.warehouse.domain) if len(location_ids) == 1: return location_ids[0] return False - def get_currency(self, cursor, user, ids, name, context=None): + def get_currency(self, ids, name): res = {} - for opportunity in self.browse(cursor, user, ids, context=context): + for opportunity in self.browse(ids): res[opportunity.id] = opportunity.company.currency.id return res - def get_currency_digits(self, cursor, user, ids, name, context=None): + def get_currency_digits(self, ids, name): res = {} - for opportunity in self.browse(cursor, user, ids, context=context): + for opportunity in self.browse(ids): res[opportunity.id] = opportunity.company.currency.digits return res - def on_change_company(self, cursor, user, values, context=None): + def on_change_company(self, values): company_obj = self.pool.get('company.company') res = {} if values.get('company'): - company = company_obj.browse(cursor, user, values['company'], - context=context) + company = company_obj.browse(values['company']) res['currency'] = company.currency.id res['currency.rec_name'] = company.currency.rec_name res['currency_digits'] = company.currency.digits return res - def on_change_party(self, cursor, user, values, context=None): + def on_change_party(self, values): party_obj = self.pool.get('party.party') payment_term_obj = self.pool.get('account.invoice.payment_term') @@ -178,47 +172,38 @@ class SaleOpportunity(ModelWorkflow, ModelSQL, ModelView): 'payment_term': False, } if vals.get('party'): - party = party_obj.browse(cursor, user, vals['party'], - context=context) + party = party_obj.browse(vals['party']) if party.payment_term: res['payment_term'] = party.payment_term.id if not res['payment_term']: - res['payment_term'] = self.default_payment_term(cursor, user, - context=context) + res['payment_term'] = self.default_payment_term() if res['payment_term']: - res['payment_term.rec_name'] = payment_term_obj.browse(cursor, user, - res['payment_term'], context=context).rec_name + res['payment_term.rec_name'] = payment_term_obj.browse( + res['payment_term']).rec_name return res - def set_end_date(self, cursor, user, opportunity_id, state, context=None): + def set_end_date(self, opportunity_id, state): """ This will fill the end_date for a lead/opportunity and change the state - :param cursor: the database cursor - :param user: the user id :param opportunity_id: the id of the opportunity :param state: the target state - :param context: the context """ date_obj = self.pool.get('ir.date') - self.write(cursor, user, opportunity_id, { - 'end_date': date_obj.today(cursor, user, context=context), + self.write(opportunity_id, { + 'end_date': date_obj.today(), 'state': state, - }, context=context) + }) - def button_lead(self, cursor, user, ids, context=None): - self.workflow_trigger_create(cursor, user, ids, context=context) + def button_lead(self, ids): + self.workflow_trigger_create(ids) return True - def _get_sale_line_opportunity_line(self, cursor, user, opportunity, - context=None): + def _get_sale_line_opportunity_line(self, opportunity): ''' Return sale line values for each opportunity line - :param cursor: the database cursor - :param user: the user id :param opportunity: the BrowseRecord of opportunity - :param context: the context :return: a dictionary with opportunity line id as key and a dictionary of sale line values as value @@ -226,19 +211,16 @@ class SaleOpportunity(ModelWorkflow, ModelSQL, ModelView): line_obj = self.pool.get('sale.opportunity.line') res = {} for line in opportunity.lines: - val = line_obj.get_sale_line(cursor, user, line, context=context) + val = line_obj.get_sale_line(line) if val: res[line.id] = val return res - def _get_sale_opportunity(self, cursor, user, opportunity, context=None): + def _get_sale_opportunity(self, opportunity): ''' Return sale values for an opportunity - :param cursor: the database cursor - :param user: the user id :param opportunity: the BrowseRecord of the opportunity - :param context: the context :return: a dictionary with sale fields as key and sale values as value ''' @@ -255,45 +237,35 @@ class SaleOpportunity(ModelWorkflow, ModelSQL, ModelView): } return res - def create_sale(self, cursor, user, opportunity_id, context=None): + def create_sale(self, opportunity_id): ''' Create a sale for the opportunity - :param cursor: the database cursor - :param user: the user id :param opportunity_id: the id of the opportunity - :param context: the context ''' sale_obj = self.pool.get('sale.sale') sale_line_obj = self.pool.get('sale.line') line_obj = self.pool.get('sale.opportunity.line') - if context is None: - context = {} + opportunity = self.browse(opportunity_id) - opportunity = self.browse(cursor, user, opportunity_id, - context=context) + values = self._get_sale_opportunity(opportunity) + sale_line_values = self._get_sale_line_opportunity_line(opportunity) - values = self._get_sale_opportunity(cursor, user, opportunity, - context=context) - sale_line_values = self._get_sale_line_opportunity_line(cursor, user, - opportunity, context=context) - - ctx = context.copy() - ctx['user'] = user - sale_id = sale_obj.create(cursor, 0, values, context=ctx) + with Transaction().set_user(0, set_context=True): + sale_id = sale_obj.create(values) for line_id, values in sale_line_values.iteritems(): values['sale'] = sale_id - sale_line_id = sale_line_obj.create(cursor, 0, values, - context=ctx) - line_obj.write(cursor, user, line_id, { + with Transaction().set_user(0, set_context=True): + sale_line_id = sale_line_obj.create(values) + line_obj.write(line_id, { 'sale_line': sale_line_id, - }, context=context) + }) - self.write(cursor, user, opportunity_id, { + self.write(opportunity_id, { 'sale': sale_id, - }, context=context) + }) return sale_id SaleOpportunity() @@ -325,34 +297,30 @@ class SaleOpportunityLine(ModelSQL, ModelView): super(SaleOpportunityLine, self).__init__() self._order.insert(0, ('sequence', 'ASC')) - def on_change_with_unit_digits(self, cursor, user, vals, context=None): + def on_change_with_unit_digits(self, vals): uom_obj = self.pool.get('product.uom') if vals.get('unit'): - uom = uom_obj.browse(cursor, user, vals['unit'], - context=context) + uom = uom_obj.browse(vals['unit']) return uom.digits return 2 - def get_unit_digits(self, cursor, user, ids, name, context=None): + def get_unit_digits(self, ids, name): res = {} - for line in self.browse(cursor, user, ids, context=context): + for line in self.browse(ids): if line.unit: res[line.id] = line.unit.digits else: res[line.id] = 2 return res - def on_change_product(self, cursor, user, vals, context=None): + def on_change_product(self, vals): product_obj = self.pool.get('product.product') - if context is None: - context = {} if not vals.get('product'): return {} res = {} - product = product_obj.browse(cursor, user, vals['product'], - context=context) + product = product_obj.browse(vals['product']) category = product.sale_uom.category if not vals.get('unit') \ or vals.get('unit') not in [x.id for x in category.uoms]: @@ -361,14 +329,11 @@ class SaleOpportunityLine(ModelSQL, ModelView): res['unit_digits'] = product.sale_uom.digits return res - def get_sale_line(self, cursor, user, line, context=None): + def get_sale_line(self, line): ''' Return sale line values for opportunity line - :param cursor: the database cursor - :param user: the user id :param line: the BrowseRecord of the line - :param context: the context :return: a dictionary with sale line fields as key and sale line values as value @@ -380,13 +345,13 @@ class SaleOpportunityLine(ModelSQL, ModelView): 'unit': line.unit.id, 'product': line.product.id, } - res.update(sale_line_obj.on_change_product(cursor, user, { + res.update(sale_line_obj.on_change_product({ 'product': line.product.id, 'unit': line.unit.id, 'quantity': line.quantity, '_parent_sale.party': line.opportunity.party.id, '_parent_sale.currency': line.opportunity.company.currency.id, - }, context=context)) + })) return res SaleOpportunityLine() @@ -394,15 +359,13 @@ SaleOpportunityLine() class One2ManyHistory(fields.One2Many): - def get(self, cursor, user, ids, model, name, values=None, - context=None): + def get(self, ids, model, name, values=None): opportunity2id = {} - histories = model.browse(cursor, user, ids, context=context) + histories = model.browse(ids) opportunity2id = dict((history.opportunity.id, history.id) for history in histories) - res = super(One2ManyHistory, self).get(cursor, user, - opportunity2id.keys(), model, name, values=values, - context=context) + res = super(One2ManyHistory, self).get(opportunity2id.keys(), model, + name, values=values) return dict((opportunity2id[i], j) for i, j in res.iteritems()) @@ -441,7 +404,7 @@ class SaleOpportunityHistory(ModelSQL, ModelView): super(SaleOpportunityHistory, self).__init__() self._order.insert(0, ('date', 'DESC')) - def _table_query_fields(self, context=None): + def _table_query_fields(self): opportunity_obj = self.pool.get('sale.opportunity') table = '%s__history' % opportunity_obj._table return [ @@ -453,7 +416,7 @@ class SaleOpportunityHistory(ModelSQL, ModelView): if name not in ('id', 'opportunity', 'date', 'user') and not hasattr(field, 'set')] - def _table_query_group(self, context=None): + def _table_query_group(self): opportunity_obj = self.pool.get('sale.opportunity') table = '%s__history' % opportunity_obj._table return [ @@ -464,18 +427,18 @@ class SaleOpportunityHistory(ModelSQL, ModelView): if name not in ('id', 'opportunity', 'date', 'user') and not hasattr(field, 'set')] - def table_query(self, context=None): + def table_query(self): opportunity_obj = self.pool.get('sale.opportunity') return (('SELECT ' + \ - (', '.join(self._table_query_fields(context=context))) + \ + (', '.join(self._table_query_fields())) + \ ' FROM "%s__history" ' 'GROUP BY ' + \ - (', '.join(self._table_query_group(context=context)))) % \ + (', '.join(self._table_query_group()))) % \ opportunity_obj._table, []) - def read(self, cursor, user, ids, fields_names=None, context=None): - res = super(SaleOpportunityHistory, self).read(cursor, user, ids, - fields_names=fields_names, context=context) + def read(self, ids, fields_names=None): + res = super(SaleOpportunityHistory, self).read(ids, + fields_names=fields_names) # Remove microsecond from timestamp for values in res: @@ -510,24 +473,22 @@ class SaleOpportunityEmployee(ModelSQL, ModelView): conversion_amount_rate = fields.Function(fields.Float( 'Conversion Amount Rate', help='In %'), 'get_conversion_amount_rate') - def _converted_state(self, context=None): + def _converted_state(self): return ['converted'] - def _lost_state(self, context=None): + def _lost_state(self): return ['lost'] - def table_query(self, context=None): + def table_query(self): opportunity_obj = self.pool.get('sale.opportunity') - if context is None: - context = {} clause = ' ' args = [True] - if context.get('start_date'): + if Transaction().context.get('start_date'): clause += 'AND start_date >= %s ' - args.append(context['start_date']) - if context.get('end_date'): + args.append(Transaction().context['start_date']) + if Transaction().context.get('end_date'): clause += 'AND start_date <= %s ' - args.append(context['end_date']) + args.append(Transaction().context['end_date']) return ('SELECT DISTINCT(employee) AS id, ' 'MAX(create_uid) AS create_uid, ' 'MAX(create_date) AS create_date, ' @@ -537,44 +498,44 @@ class SaleOpportunityEmployee(ModelSQL, ModelView): 'company, ' 'COUNT(1) AS number, ' 'SUM(CASE WHEN state IN (' + ','.join("'%s'" % x - for x in self._converted_state(context=context)) + ') ' + for x in self._converted_state()) + ') ' 'THEN 1 ELSE 0 END) AS converted, ' 'SUM(CASE WHEN state IN (' + ','.join("'%s'" % x - for x in self._lost_state(context=context)) + ') ' + for x in self._lost_state()) + ') ' 'THEN 1 ELSE 0 END) AS lost, ' 'SUM(amount) AS amount, ' 'SUM(CASE WHEN state IN (' + ','.join("'%s'" % x - for x in self._converted_state(context=context)) + ') ' + for x in self._converted_state()) + ') ' 'THEN amount ELSE 0 END) AS converted_amount ' 'FROM "' + opportunity_obj._table + '" ' 'WHERE %s ' \ + clause + \ 'GROUP BY employee, company', args) - def get_conversion_rate(self, cursor, user, ids, name, context=None): + def get_conversion_rate(self, ids, name): res = {} - for record in self.browse(cursor, user, ids, context=context): + for record in self.browse(ids): if record.number: res[record.id] = float(record.converted) / record.number * 100.0 else: res[record.id] = 0.0 return res - def get_currency(self, cursor, user, ids, name, context=None): + def get_currency(self, ids, name): res = {} - for record in self.browse(cursor, user, ids, context=context): + for record in self.browse(ids): res[record.id] = record.company.currency.id return res - def get_currency_digits(self, cursor, user, ids, name, context=None): + def get_currency_digits(self, ids, name): res = {} - for record in self.browse(cursor, user, ids, context=context): + for record in self.browse(ids): res[record.id] = record.company.currency.digits return res - def get_conversion_amount_rate(self, cursor, user, ids, name, context=None): + def get_conversion_amount_rate(self, ids, name): res = {} - for record in self.browse(cursor, user, ids, context=context): + for record in self.browse(ids): if record.amount: res[record.id] = (float(record.converted_amount) / float(record.amount) * 100.0) @@ -618,12 +579,12 @@ class OpenSaleOpportunityEmployee(Wizard): }, } - def _action_open(self, cursor, user, data, context=None): + def _action_open(self, data): model_data_obj = self.pool.get('ir.model.data') act_window_obj = self.pool.get('ir.action.act_window') - act_window_id = model_data_obj.get_id(cursor, user, 'sale_opportunity', - 'act_opportunity_employee_form', context=context) - res = act_window_obj.read(cursor, user, act_window_id, context=context) + act_window_id = model_data_obj.get_id('sale_opportunity', + 'act_opportunity_employee_form') + res = act_window_obj.read(act_window_id) res['pyson_context'] = PYSONEncoder().encode({ 'start_date': data['form']['start_date'], 'end_date': data['form']['end_date'], @@ -665,13 +626,13 @@ class SaleOpportunityMonthly(ModelSQL, ModelView): self._order.insert(0, ('year', 'DESC')) self._order.insert(1, ('month', 'DESC')) - def _converted_state(self, context=None): + def _converted_state(self): return ['converted'] - def _lost_state(self, context=None): + def _lost_state(self): return ['lost'] - def table_query(self, context=None): + def table_query(self): opportunity_obj = self.pool.get('sale.opportunity') type_id = FIELDS[self.id._type].sql_type(self.id)[0] type_year = FIELDS[self.year._type].sql_type(self.year)[0] @@ -691,49 +652,49 @@ class SaleOpportunityMonthly(ModelSQL, ModelView): 'company, ' 'COUNT(1) AS number, ' 'SUM(CASE WHEN state IN (' + ','.join("'%s'" % x - for x in self._converted_state(context=context)) + ') ' + for x in self._converted_state()) + ') ' 'THEN 1 ELSE 0 END) AS converted, ' 'SUM(CASE WHEN state IN (' + ','.join("'%s'" % x - for x in self._lost_state(context=context)) + ') ' + for x in self._lost_state()) + ') ' 'THEN 1 ELSE 0 END) AS lost, ' 'SUM(amount) AS amount, ' 'SUM(CASE WHEN state IN (' + ','.join("'%s'" % x - for x in self._converted_state(context=context)) + ') ' + for x in self._converted_state()) + ') ' 'THEN amount ELSE 0 END) AS converted_amount ' 'FROM "' + opportunity_obj._table + '" ' 'GROUP BY year, month, company) AS "' + self._table + '"', []) - def get_conversion_rate(self, cursor, user, ids, name, context=None): + def get_conversion_rate(self, ids, name): res = {} - for record in self.browse(cursor, user, ids, context=context): + for record in self.browse(ids): if record.number: res[record.id] = float(record.converted) / record.number * 100.0 else: res[record.id] = 0.0 return res - def get_year_month(self, cursor, user, ids, name, context=None): + def get_year_month(self, ids, name): res = {} - for record in self.browse(cursor, user, ids, context=context): + for record in self.browse(ids): res[record.id] = '%s-%s' % (record.year, int(record.month)) return res - def get_currency(self, cursor, user, ids, name, context=None): + def get_currency(self, ids, name): res = {} - for record in self.browse(cursor, user, ids, context=context): + for record in self.browse(ids): res[record.id] = record.company.currency.id return res - def get_currency_digits(self, cursor, user, ids, name, context=None): + def get_currency_digits(self, ids, name): res = {} - for record in self.browse(cursor, user, ids, context=context): + for record in self.browse(ids): res[record.id] = record.company.currency.digits return res - def get_conversion_amount_rate(self, cursor, user, ids, name, context=None): + def get_conversion_amount_rate(self, ids, name): res = {} - for record in self.browse(cursor, user, ids, context=context): + for record in self.browse(ids): if record.amount: res[record.id] = (float(record.converted_amount) / float(record.amount) * 100.0) @@ -776,13 +737,13 @@ class SaleOpportunityEmployeeMonthly(ModelSQL, ModelView): self._order.insert(1, ('month', 'DESC')) self._order.insert(2, ('employee', 'ASC')) - def _converted_state(self, context=None): + def _converted_state(self): return ['converted'] - def _lost_state(self, context=None): + def _lost_state(self): return ['lost'] - def table_query(self, context=None): + def table_query(self): opportunity_obj = self.pool.get('sale.opportunity') type_id = FIELDS[self.id._type].sql_type(self.id)[0] type_year = FIELDS[self.year._type].sql_type(self.year)[0] @@ -805,43 +766,43 @@ class SaleOpportunityEmployeeMonthly(ModelSQL, ModelView): 'company, ' 'COUNT(1) AS number, ' 'SUM(CASE WHEN state IN (' + ','.join("'%s'" % x - for x in self._converted_state(context=context)) + ') ' + for x in self._converted_state()) + ') ' 'THEN 1 ELSE 0 END) AS converted, ' 'SUM(CASE WHEN state IN (' + ','.join("'%s'" % x - for x in self._lost_state(context=context)) + ') ' + for x in self._lost_state()) + ') ' 'THEN 1 ELSE 0 END) AS lost, ' 'SUM(amount) AS amount, ' 'SUM(CASE WHEN state IN (' + ','.join("'%s'" % x - for x in self._converted_state(context=context)) + ') ' + for x in self._converted_state()) + ') ' 'THEN amount ELSE 0 END) AS converted_amount ' 'FROM "' + opportunity_obj._table + '" ' 'GROUP BY year, month, employee, company) ' 'AS "' + self._table + '"', []) - def get_conversion_rate(self, cursor, user, ids, name, context=None): + def get_conversion_rate(self, ids, name): res = {} - for record in self.browse(cursor, user, ids, context=context): + for record in self.browse(ids): if record.number: res[record.id] = float(record.converted) / record.number * 100.0 else: res[record.id] = 0.0 return res - def get_currency(self, cursor, user, ids, name, context=None): + def get_currency(self, ids, name): res = {} - for record in self.browse(cursor, user, ids, context=context): + for record in self.browse(ids): res[record.id] = record.company.currency.id return res - def get_currency_digits(self, cursor, user, ids, name, context=None): + def get_currency_digits(self, ids, name): res = {} - for record in self.browse(cursor, user, ids, context=context): + for record in self.browse(ids): res[record.id] = record.company.currency.digits return res - def get_conversion_amount_rate(self, cursor, user, ids, name, context=None): + def get_conversion_amount_rate(self, ids, name): res = {} - for record in self.browse(cursor, user, ids, context=context): + for record in self.browse(ids): if record.amount: res[record.id] = (float(record.converted_amount) / float(record.amount) * 100.0)