diff --git a/addons/account/account.py b/addons/account/account.py index 02cb63ca818..bedff44b7e8 100644 --- a/addons/account/account.py +++ b/addons/account/account.py @@ -289,20 +289,20 @@ class account_account(osv.osv): sums = {} while brs: current = brs[0] - can_compute = True - for child in current.child_id: - if child.id not in sums: - can_compute = False - try: - brs.insert(0, brs.pop(brs.index(child))) - except ValueError: - brs.insert(0, child) - if can_compute: - brs.pop(0) - for fn in field_names: - sums.setdefault(current.id, {})[fn] = accounts.get(current.id, {}).get(fn, 0.0) - if current.child_id: - sums[current.id][fn] += sum(sums[child.id][fn] for child in current.child_id) +# can_compute = True +# for child in current.child_id: +# if child.id not in sums: +# can_compute = False +# try: +# brs.insert(0, brs.pop(brs.index(child))) +# except ValueError: +# brs.insert(0, child) +# if can_compute: + brs.pop(0) + for fn in field_names: + sums.setdefault(current.id, {})[fn] = accounts.get(current.id, {}).get(fn, 0.0) + if current.child_id: + sums[current.id][fn] += sum(sums[child.id][fn] for child in current.child_id) res = {} null_result = dict((fn, 0.0) for fn in field_names) for id in ids: @@ -2678,7 +2678,8 @@ class wizard_multi_charts_accounts(osv.osv_memory): tax_template_ref[tax.id] = new_tax #deactivate the parent_store functionnality on account_account for rapidity purpose - self.pool._init = True + ctx = context and context.copy() or {} + ctx['defer_parent_store_computation'] = True children_acc_template = obj_acc_template.search(cr, uid, [('parent_id','child_of',[obj_acc_root.id]),('nocreate','!=',True)]) children_acc_template.sort() @@ -2706,10 +2707,9 @@ class wizard_multi_charts_accounts(osv.osv_memory): 'tax_ids': [(6,0,tax_ids)], 'company_id': company_id, } - new_account = obj_acc.create(cr, uid, vals) + new_account = obj_acc.create(cr, uid, vals, context=ctx) acc_template_ref[account_template.id] = new_account #reactivate the parent_store functionnality on account_account - self.pool._init = False self.pool.get('account.account')._parent_store_compute(cr) for key,value in todo_dict.items(): @@ -2857,7 +2857,7 @@ class wizard_multi_charts_accounts(osv.osv_memory): 'name': tmp, 'currency_id': line.currency_id and line.currency_id.id or False, 'code': new_code, - 'type': 'other', + 'type': 'liquidity', 'user_type': account_template.user_type and account_template.user_type.id or False, 'reconcile': True, 'parent_id': acc_template_ref[ref_acc_bank.id] or False, @@ -2879,7 +2879,7 @@ class wizard_multi_charts_accounts(osv.osv_memory): vals_journal['name']= vals['name'] vals_journal['code']= _('BNK') + str(current_num) vals_journal['sequence_id'] = seq_id - vals_journal['type'] = 'cash' + vals_journal['type'] = line.account_type == 'cash' and 'cash' or 'bank' vals_journal['company_id'] = company_id vals_journal['analytic_journal_id'] = analitical_journal_bank diff --git a/addons/account/account_move_line.py b/addons/account/account_move_line.py index 2d9ac7d6b15..a9bb550429a 100644 --- a/addons/account/account_move_line.py +++ b/addons/account/account_move_line.py @@ -757,7 +757,7 @@ class account_move_line(osv.osv): if 'comment' in context and context['comment']: libelle = context['comment'] else: - libelle = 'Write-Off' + libelle = _('Write-Off') writeoff_lines = [ (0, 0, { 'name': libelle, diff --git a/addons/account/installer.py b/addons/account/installer.py index 0301126a373..1bda9427e75 100644 --- a/addons/account/installer.py +++ b/addons/account/installer.py @@ -219,7 +219,8 @@ class account_installer(osv.osv_memory): tax_template_ref[tax.id] = new_tax #deactivate the parent_store functionnality on account_account for rapidity purpose - self.pool._init = True + ctx = context and context.copy() or {} + ctx['defer_parent_store_computation'] = True children_acc_template = obj_acc_template.search(cr, uid, [('parent_id', 'child_of', [obj_acc_root.id]), ('nocreate', '!=', True)], context=context) children_acc_template.sort() @@ -248,7 +249,7 @@ class account_installer(osv.osv_memory): 'tax_ids': [(6, 0, tax_ids)], 'company_id': company_id.id, } - new_account = obj_acc.create(cr, uid, vals, context=context) + new_account = obj_acc.create(cr, uid, vals, context=ctx) acc_template_ref[account_template.id] = new_account if account_template.name == 'Bank Current Account': b_vals = { @@ -262,8 +263,7 @@ class account_installer(osv.osv_memory): 'tax_ids': [(6,0,tax_ids)], 'company_id': company_id.id, } - bank_account = obj_acc.create(cr, uid, b_vals, context=context) - + bank_account = obj_acc.create(cr, uid, b_vals, context=ctx) view_id_cash = obj_acc_journal_view.search(cr, uid, [('name', '=', 'Bank/Cash Journal View')], context=context)[0] #why fixed name here? view_id_cur = obj_acc_journal_view.search(cr, uid, [('name', '=', 'Bank/Cash Journal (Multi-Currency) View')], context=context)[0] #Why Fixed name here? @@ -333,7 +333,7 @@ class account_installer(osv.osv_memory): 'parent_id': bank_account, 'company_id': company_id.id } - child_bnk_acc = obj_acc.create(cr, uid, vals_bnk, context=context) + child_bnk_acc = obj_acc.create(cr, uid, vals_bnk, context=ctx) vals_seq_child = { 'name': _(vals_bnk['name'] + ' ' + 'Journal'), 'code': 'account.journal', @@ -367,7 +367,6 @@ class account_installer(osv.osv_memory): code_cnt += 1 #reactivate the parent_store functionality on account_account - self.pool._init = False obj_acc._parent_store_compute(cr) for key, value in todo_dict.items(): diff --git a/addons/account/invoice.py b/addons/account/invoice.py index 706b357465f..625a91ad289 100644 --- a/addons/account/invoice.py +++ b/addons/account/invoice.py @@ -268,21 +268,21 @@ class account_invoice(osv.osv): store={ 'account.invoice': (lambda self, cr, uid, ids, c={}: ids, ['invoice_line'], 20), 'account.invoice.tax': (_get_invoice_tax, None, 20), - 'account.invoice.line': (_get_invoice_line, ['price_unit','invoice_line_tax_id','quantity','discount'], 20), + 'account.invoice.line': (_get_invoice_line, ['price_unit','invoice_line_tax_id','quantity','discount','invoice_id'], 20), }, multi='all'), 'amount_tax': fields.function(_amount_all, method=True, digits_compute=dp.get_precision('Account'), string='Tax', store={ 'account.invoice': (lambda self, cr, uid, ids, c={}: ids, ['invoice_line'], 20), 'account.invoice.tax': (_get_invoice_tax, None, 20), - 'account.invoice.line': (_get_invoice_line, ['price_unit','invoice_line_tax_id','quantity','discount'], 20), + 'account.invoice.line': (_get_invoice_line, ['price_unit','invoice_line_tax_id','quantity','discount','invoice_id'], 20), }, multi='all'), 'amount_total': fields.function(_amount_all, method=True, digits_compute=dp.get_precision('Account'), string='Total', store={ 'account.invoice': (lambda self, cr, uid, ids, c={}: ids, ['invoice_line'], 20), 'account.invoice.tax': (_get_invoice_tax, None, 20), - 'account.invoice.line': (_get_invoice_line, ['price_unit','invoice_line_tax_id','quantity','discount'], 20), + 'account.invoice.line': (_get_invoice_line, ['price_unit','invoice_line_tax_id','quantity','discount','invoice_id'], 20), }, multi='all'), 'currency_id': fields.many2one('res.currency', 'Currency', required=True, readonly=True, states={'draft':[('readonly',False)]}), @@ -302,7 +302,7 @@ class account_invoice(osv.osv): store={ 'account.invoice': (lambda self, cr, uid, ids, c={}: ids, ['invoice_line'], 50), 'account.invoice.tax': (_get_invoice_tax, None, 50), - 'account.invoice.line': (_get_invoice_line, ['price_unit','invoice_line_tax_id','quantity','discount'], 50), + 'account.invoice.line': (_get_invoice_line, ['price_unit','invoice_line_tax_id','quantity','discount','invoice_id'], 50), 'account.move.line': (_get_invoice_from_line, None, 50), 'account.move.reconcile': (_get_invoice_from_reconcile, None, 50), }, diff --git a/addons/account/report/account_invoice_report_view.xml b/addons/account/report/account_invoice_report_view.xml index b925c115a54..949a4c934db 100644 --- a/addons/account/report/account_invoice_report_view.xml +++ b/addons/account/report/account_invoice_report_view.xml @@ -78,7 +78,7 @@ domain="['|', ('type','=','out_invoice'),('type','=','out_refund')]" help="Customer Invoices And Refunds"/> diff --git a/addons/account_voucher/account_voucher.py b/addons/account_voucher/account_voucher.py index d47d8fb1f6f..159de686764 100644 --- a/addons/account_voucher/account_voucher.py +++ b/addons/account_voucher/account_voucher.py @@ -23,10 +23,11 @@ import time from lxml import etree import netsvc -from osv import fields -from osv import osv +from osv import osv, fields +import decimal_precision as dp from tools.translate import _ + class account_move_line(osv.osv): _inherit = 'account.move.line' @@ -145,18 +146,29 @@ class account_voucher(osv.osv): res['arch'] = etree.tostring(doc) return res + def _compute_writeoff_amount(self, cr, uid, line_dr_ids, line_cr_ids, amount): + debit = credit = 0.0 + for l in line_dr_ids: + debit += l['amount'] + for l in line_cr_ids: + credit += l['amount'] + return abs(amount - abs(credit - debit)) + + def onchange_line_ids(self, cr, uid, ids, line_dr_ids, line_cr_ids, amount): + line_dr_ids = [x[2] for x in line_dr_ids] + line_cr_ids = [x[2] for x in line_cr_ids] + return {'value': {'writeoff_amount': self._compute_writeoff_amount(cr, uid, line_dr_ids, line_cr_ids, amount)}} + def _get_writeoff_amount(self, cr, uid, ids, name, args, context=None): if not ids: return {} res = {} + debit = credit = 0.0 for voucher in self.browse(cr, uid, ids, context=context): - debit= credit = 0.0 - if voucher.line_dr_ids: - for line in voucher.line_dr_ids: - debit += line.amount_unreconciled - if voucher.line_cr_ids: - for line in voucher.line_cr_ids: - credit += line.amount_unreconciled - res[voucher.id] = abs(voucher.amount - abs(credit - debit)) + for l in voucher.line_dr_ids: + debit += l.amount + for l in voucher.line_cr_ids: + credit += l.amount + res[voucher.id] = abs(voucher.amount - abs(credit - debit)) return res _name = 'account.voucher' @@ -194,8 +206,8 @@ class account_voucher(osv.osv): \n* The \'Pro-forma\' when voucher is in Pro-forma state,voucher does not have an voucher number. \ \n* The \'Posted\' state is used when user create voucher,a voucher number is generated and voucher entries are created in account \ \n* The \'Cancelled\' state is used when user cancel voucher.'), - 'amount': fields.float('Total', digits=(16, 2), required=True, readonly=True, states={'draft':[('readonly',False)]}), - 'tax_amount':fields.float('Tax Amount', digits=(14,2), readonly=True, states={'draft':[('readonly',False)]}), + 'amount': fields.float('Total', digits_compute=dp.get_precision('Account'), required=True, readonly=True, states={'draft':[('readonly',False)]}), + 'tax_amount':fields.float('Tax Amount', digits_compute=dp.get_precision('Account'), readonly=True, states={'draft':[('readonly',False)]}), 'reference': fields.char('Ref #', size=64, readonly=True, states={'draft':[('readonly',False)]}, help="Transaction reference number."), 'number': fields.char('Number', size=32, readonly=True,), 'move_id':fields.many2one('account.move', 'Account Entry'), @@ -210,14 +222,13 @@ class account_voucher(osv.osv): 'pre_line':fields.boolean('Previous Payments ?', required=False), 'date_due': fields.date('Due Date', readonly=True, states={'draft':[('readonly',False)]}), 'payment_option':fields.selection([ - ('without_writeoff', 'Without Write-off'), - ('with_writeoff', 'With Write-off'), - ], 'Payment Option', required=True, readonly=True, states={'draft': [('readonly', False)]}), + ('without_writeoff', 'Keep Open'), + ('with_writeoff', 'Reconcile with Write-Off'), + ], 'Payment Difference', required=True, readonly=True, states={'draft': [('readonly', False)]}), 'writeoff_acc_id': fields.many2one('account.account', 'Write-Off account', readonly=True, states={'draft': [('readonly', False)]}), - 'writeoff_journal_id': fields.many2one('account.journal', 'Write-Off journal', readonly=True, states={'draft': [('readonly', False)]}), - 'comment': fields.char('Comment', size=64, readonly=True, states={'draft': [('readonly', False)]}), - 'analytic_id': fields.many2one('account.analytic.account','Analytic Account', readonly=True, states={'draft': [('readonly', False)]}), - 'writeoff_amount': fields.function(_get_writeoff_amount, method=True, string='Writeoff Amount', type='float', readonly=True), + 'comment': fields.char('Write-Off Comment', size=64, required=True, readonly=True, states={'draft': [('readonly', False)]}), + 'analytic_id': fields.many2one('account.analytic.account','Write-Off Analytic Account', readonly=True, states={'draft': [('readonly', False)]}), + 'writeoff_amount': fields.function(_get_writeoff_amount, method=True, string='Write-Off Amount', type='float', readonly=True), } _defaults = { 'period_id': _get_period, @@ -235,7 +246,7 @@ class account_voucher(osv.osv): 'company_id': lambda self,cr,uid,c: self.pool.get('res.company')._company_default_get(cr, uid, 'account.voucher',context=c), 'tax_id': _get_tax, 'payment_option': 'without_writeoff', - 'comment': 'Write-Off', + 'comment': _('Write-Off'), } def compute_tax(self, cr, uid, ids, context={}): @@ -390,13 +401,17 @@ class account_voucher(osv.osv): @return: Returns a dict which contains new values, and context """ + if context is None: + context = {} if not journal_id: return {} - if context is None: - context = {} - currency_pool = self.pool.get('res.currency') line_pool = self.pool.get('account.voucher.line') + line_ids = ids and line_pool.search(cr, uid, [('voucher_id', '=', ids[0])]) or False + if line_ids: + line_pool.unlink(cr, uid, line_ids) + + currency_pool = self.pool.get('res.currency') move_line_pool = self.pool.get('account.move.line') partner_pool = self.pool.get('res.partner') journal_pool = self.pool.get('account.journal') @@ -410,13 +425,6 @@ class account_voucher(osv.osv): if not partner_id: return default - - if not partner_id and ids: - line_ids = line_pool.search(cr, uid, [('voucher_id', '=', ids[0])]) - if line_ids: - line_pool.unlink(cr, uid, line_ids) - return default - journal = journal_pool.browse(cr, uid, journal_id) partner = partner_pool.browse(cr, uid, partner_id) account_id = False @@ -449,16 +457,6 @@ class account_voucher(osv.osv): ids.reverse() moves = move_line_pool.browse(cr, uid, ids) - priority_moves = [] - unpriority_moves = [] - for line in moves: - original_amount = line.credit or line.debit or 0.0 - if original_amount == price: - priority_moves.append(line) - else: - unpriority_moves.append(line) - moves = priority_moves + unpriority_moves - company_currency = journal.company_id.currency_id.id if company_currency != currency_id and ttype == 'payment': total_debit = currency_pool.compute(cr, uid, currency_id, company_currency, total_debit) @@ -509,6 +507,8 @@ class account_voucher(osv.osv): default['value']['pre_line'] = 1 elif ttype == 'receipt' and len(default['value']['line_dr_ids']) > 0: default['value']['pre_line'] = 1 + default['value']['writeoff_amount'] = self._compute_writeoff_amount(cr, uid, default['value']['line_dr_ids'], default['value']['line_cr_ids'], price) + return default def onchange_date(self, cr, user, ids, date, context={}): @@ -734,16 +734,20 @@ class account_voucher(osv.osv): if line.move_line_id.id: rec_ids = [master_line, line.move_line_id.id] rec_list_ids.append(rec_ids) - writeoff_account_id = False - writeoff_journal_id = False - writeoff_period_id = inv.period_id.id, - comment = False if not currency_pool.is_zero(cr, uid, inv.currency_id, line_total): diff = line_total + account_id = False + if inv.payment_option == 'with_writeoff': + account_id = inv.writeoff_acc_id.id + elif inv.type in ('sale', 'receipt'): +# if inv.journal_id.type in ('sale','sale_refund', 'cash', 'bank'): + account_id = inv.partner_id.property_account_receivable.id + else: + account_id = inv.partner_id.property_account_payable.id move_line = { 'name': name, - 'account_id': False, + 'account_id': account_id, 'move_id': move_id, 'partner_id': inv.partner_id.id, 'date': inv.date, @@ -752,24 +756,8 @@ class account_voucher(osv.osv): 'amount_currency': company_currency <> current_currency and currency_pool.compute(cr, uid, company_currency, current_currency, diff * -1) or 0.0, 'currency_id': company_currency <> current_currency and current_currency or False, } - account_id = False - if inv.type in ('sale', 'receipt'): -# if inv.journal_id.type in ('sale','sale_refund', 'cash', 'bank'): - account_id = inv.partner_id.property_account_receivable.id - else: - account_id = inv.partner_id.property_account_payable.id - move_line['account_id'] = account_id move_line_pool.create(cr, uid, move_line) - for rec_ids in rec_list_ids: - if len(rec_ids) >= 2: - if inv.payment_option == 'with_writeoff': - writeoff_account_id = inv.writeoff_acc_id.id - writeoff_journal_id = inv.writeoff_journal_id.id - comment = inv.comment - move_line_pool.reconcile(cr, uid, rec_ids, 'manual', writeoff_account_id, writeoff_period_id, writeoff_journal_id, context) - else: - move_line_pool.reconcile_partial(cr, uid, rec_ids) self.write(cr, uid, [inv.id], { 'move_id': move_id, @@ -777,6 +765,9 @@ class account_voucher(osv.osv): 'number': name, }) move_pool.post(cr, uid, [move_id], context={}) + for rec_ids in rec_list_ids: + if len(rec_ids) >= 2: + move_line_pool.reconcile_partial(cr, uid, rec_ids) return True def copy(self, cr, uid, id, default={}, context=None): @@ -828,7 +819,7 @@ class account_voucher_line(osv.osv): 'account_id':fields.many2one('account.account','Account', required=True), 'partner_id':fields.related('voucher_id', 'partner_id', type='many2one', relation='res.partner', string='Partner'), 'untax_amount':fields.float('Untax Amount'), - 'amount':fields.float('Amount', digits=(14,2)), + 'amount':fields.float('Amount', digits_compute=dp.get_precision('Account')), 'type':fields.selection([('dr','Debit'),('cr','Credit')], 'Cr/Dr'), 'account_analytic_id': fields.many2one('account.analytic.account', 'Analytic Account'), 'move_line_id': fields.many2one('account.move.line', 'Journal Item'), @@ -993,4 +984,4 @@ class account_bank_statement_line(osv.osv): account_bank_statement_line() -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: \ No newline at end of file +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/account_voucher/account_voucher_view.xml b/addons/account_voucher/account_voucher_view.xml index 0511a8325d5..c41356f3973 100644 --- a/addons/account_voucher/account_voucher_view.xml +++ b/addons/account_voucher/account_voucher_view.xml @@ -70,8 +70,21 @@ - - + + + + + + + + + + + + + + + diff --git a/addons/account_voucher/voucher_payment_receipt_view.xml b/addons/account_voucher/voucher_payment_receipt_view.xml index 3a0dbf40f4d..b47fa46bb7e 100644 --- a/addons/account_voucher/voucher_payment_receipt_view.xml +++ b/addons/account_voucher/voucher_payment_receipt_view.xml @@ -105,7 +105,7 @@ - + - + @@ -198,22 +198,18 @@ - + - - + domain="[('type','=','other')]"/> - @@ -227,8 +223,21 @@ - - + + + + + + + + + + + + + + + @@ -295,7 +304,7 @@ - + - + @@ -330,10 +339,7 @@ attrs="{'invisible':[('payment_option','!=','with_writeoff')]}"/> - - + domain="[('type','=','other')]"/> - - + + + + + + + + + + + + + + + diff --git a/addons/account_voucher/voucher_sales_purchase_view.xml b/addons/account_voucher/voucher_sales_purchase_view.xml index 5bef0c26718..2800511a8e0 100644 --- a/addons/account_voucher/voucher_sales_purchase_view.xml +++ b/addons/account_voucher/voucher_sales_purchase_view.xml @@ -144,8 +144,21 @@ - - + + + + + + + + + + + + + + + @@ -251,8 +264,21 @@ - - + + + + + + + + + + + + + + + diff --git a/addons/base_report_designer/plugin/openerp_report_designer.zip b/addons/base_report_designer/plugin/openerp_report_designer.zip index 22c0b7768f2..3ee0260060f 100644 Binary files a/addons/base_report_designer/plugin/openerp_report_designer.zip and b/addons/base_report_designer/plugin/openerp_report_designer.zip differ diff --git a/addons/base_report_designer/plugin/openerp_report_designer/bin/script/Change.py b/addons/base_report_designer/plugin/openerp_report_designer/bin/script/Change.py index 730ce8efefb..d4359a466ca 100644 --- a/addons/base_report_designer/plugin/openerp_report_designer/bin/script/Change.py +++ b/addons/base_report_designer/plugin/openerp_report_designer/bin/script/Change.py @@ -54,11 +54,11 @@ class Change: def __init__(self, aVal= None, sURL=""): self.win=DBModalDialog(60, 50, 120, 90, "Connect to Open ERP Server") - self.win.addFixedText("lblVariable", 38, 12, 60, 15, "Server") + self.win.addFixedText("lblVariable", 38, 12, 25, 15, "Server ") self.win.addEdit("txtHost",-2,9,60,15,sURL[sURL.find("/")+2:sURL.rfind(":")]) - self.win.addFixedText("lblReportName",45 , 31, 60, 15, "Port") + self.win.addFixedText("lblReportName",45 , 31, 15, 15, "Port ") self.win.addEdit("txtPort",-2,28,60,15,sURL[sURL.rfind(":")+1:]) self.win.addFixedText("lblLoginName", 2, 51, 60, 15, "Protocol Connection") @@ -92,6 +92,8 @@ class Change: global url url = self.protocol[self.win.getListBoxSelectedItem("lstProtocol")]+self.win.getEditText("txtHost")+":"+self.win.getEditText("txtPort") self.sock=RPCSession(url) + desktop=getDesktop() + doc = desktop.getCurrentComponent() docinfo=doc.getDocumentInfo() docinfo.setUserFieldValue(0,url) res=self.sock.listdb() diff --git a/addons/base_report_designer/plugin/openerp_report_designer/bin/script/ServerParameter.py b/addons/base_report_designer/plugin/openerp_report_designer/bin/script/ServerParameter.py index de67ea0abda..123be9b9bc2 100644 --- a/addons/base_report_designer/plugin/openerp_report_designer/bin/script/ServerParameter.py +++ b/addons/base_report_designer/plugin/openerp_report_designer/bin/script/ServerParameter.py @@ -192,6 +192,10 @@ class ServerParameter( unohelper.Base, XJobExecutor ): Change(aVal,url) if aVal[1]== -1: self.win.getEditText("lstDatabase") + self.win.removeListBoxItems("lstDatabase", 0, self.win.getListBoxItemCount("lstDatabase")) + self.win.setEditText("txtHost",aVal[0]) + for i in range(len(aVal[1])): + self.lstDatabase.addItem(aVal[1][i],i) elif aVal[1]==0: ErrorDialog(aVal[0],"") else: diff --git a/addons/crm/crm.py b/addons/crm/crm.py index 9798fd09a4e..b40f7131a47 100644 --- a/addons/crm/crm.py +++ b/addons/crm/crm.py @@ -22,6 +22,7 @@ import time import base64 import tools + from osv import fields from osv import osv from tools.translate import _ @@ -135,6 +136,52 @@ class crm_case(object): user = self.pool.get('res.users').browse(cr, uid, uid, context=context) return user.context_section_id.id or False + def _find_next_stage(self, cr, uid, stage_list, index, current_seq, stage_pool, context=None): + if index + 1 == len(stage_list): + return False + next_stage_id = stage_list[index + 1] + next_stage = stage_pool.browse(cr, uid, next_stage_id, context=context) + if not next_stage: + return False + + next_seq = next_stage.sequence + if (abs(next_seq - current_seq)) >= 1: + return next_stage + else : + return self._find_next_stage(cr, uid, stage_list, index + 1, current_seq, stage_pool) + + def stage_change(self, cr, uid, ids, context=None, order='sequence'): + if not context: + context = {} + stage_pool = self.pool.get('crm.case.stage') + stage_type = context and context.get('stage_type','') + current_seq = False + next_stage_id = False + for case in self.browse(cr, uid, ids, context): + next_stage = False + data = {} + + domain = [('type', '=', stage_type),('section_ids', '=', case.section_id.id)] + if case.section_id and case.section_id.stage_ids: + domain.append(('id', 'in', map(lambda x: x.id, case.section_id.stage_ids))) + + stages = stage_pool.search(cr, uid, domain, order=order) + current_seq = case.stage_id.sequence + index = -1 + if case.stage_id and case.stage_id.id in stages: + index = stages.index(case.stage_id.id) + + next_stage = self._find_next_stage(cr, uid, stages, index, current_seq, stage_pool, context=context) + if next_stage: + next_stage_id = next_stage.id + data = {'stage_id': next_stage.id} + if next_stage.on_change: + data.update({'probability': next_stage.probability}) + self.write(cr, uid, [case.id], data, context=context) + + return next_stage_id + + def stage_next(self, cr, uid, ids, context=None): """This function computes next stage for case from its current stage using available stage for that case type @@ -143,32 +190,10 @@ class crm_case(object): @param uid: the current user’s ID for security checks, @param ids: List of case IDs @param context: A standard dictionary for contextual values""" - if not context: - context = {} - stage_pool = self.pool.get('crm.case.stage') - model = self._name - for case in self.browse(cr, uid, ids, context): - next_stage = False - data = {} - domain = [('object_id.model', '=', model),('section_ids', '=', case.section_id.id)] - if case.section_id and case.section_id.stage_ids: - domain.append(('id', 'in', map(lambda x: x.id, case.section_id.stage_ids))) - stages = stage_pool.search(cr, uid, domain, order='sequence') - index = -1 - if case.stage_id and case.stage_id.id in stages: - index = stages.index(case.stage_id.id) - if index + 1 == len(stages): - return False - else: - next_stage = stages[index + 1] - if next_stage: - data = {'stage_id': next_stage} - stage = stage_pool.browse(cr, uid, next_stage, context=context) - if stage.on_change: - data.update({'probability': stage.probability}) - self.write(cr, uid, [case.id], data, context=context) - return next_stage - + + + return self.stage_change(cr, uid, ids, context=context, order='sequence') + def stage_previous(self, cr, uid, ids, context=None): """This function computes previous stage for case from its current stage using available stage for that case type @@ -177,31 +202,7 @@ class crm_case(object): @param uid: the current user’s ID for security checks, @param ids: List of case IDs @param context: A standard dictionary for contextual values""" - if not context: - context = {} - stage_pool = self.pool.get('crm.case.stage') - model = self._name - for case in self.browse(cr, uid, ids, context): - prev_stage = False - data = {} - domain = [('object_id.model', '=', model),('section_ids', '=', case.section_id.id)] - if case.section_id and case.section_id.stage_ids: - domain.append(('id', 'in', map(lambda x: x.id, case.section_id.stage_ids))) - stages = stage_pool.search(cr, uid, domain, order='sequence') - index = 0 - if case.stage_id and case.stage_id.id in stages: - index = stages.index(case.stage_id.id) - if index == 0: - return False - else: - prev_stage = stages[index - 1] - if prev_stage: - data = {'stage_id': prev_stage} - stage = stage_pool.browse(cr, uid, prev_stage, context=context) - if stage.on_change: - data.update({'probability': stage.probability}) - self.write(cr, uid, [case.id], data, context=context) - return prev_stage + return self.stage_change(cr, uid, ids, context=context, order='sequence desc') def onchange_partner_id(self, cr, uid, ids, part, email=False): """This function returns value of partner address based on partner @@ -482,27 +483,27 @@ class crm_case_stage(osv.osv): _columns = { 'name': fields.char('Stage Name', size=64, required=True, translate=True), 'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of case stages."), - 'object_id': fields.many2one('ir.model', 'Object Name'), 'probability': fields.float('Probability (%)', required=True, help="This percentage depicts the default/average probability of the Case for this stage to be a success"), 'on_change': fields.boolean('Change Probability Automatically', \ help="Change Probability on next and previous stages."), - 'requirements': fields.text('Requirements') + 'requirements': fields.text('Requirements'), + 'type': fields.selection([('lead','Lead'),('opportunity','Opportunity'),('claim','Claim'), ('fundraising','Fundraising')], 'Type'), } - def _find_object_id(self, cr, uid, context=None): - """Finds id for case object + + def _find_stage_type(self, cr, uid, context=None): + """Finds type of stage according to object. @param self: The object pointer @param cr: the current row, from the database cursor, @param uid: the current user’s ID for security checks, @param context: A standard dictionary for contextual values """ - object_id = context and context.get('object_id', False) or False - ids = self.pool.get('ir.model').search(cr, uid, [('model', '=', object_id)]) - return ids and ids[0] + type = context and context.get('type', '') or '' + return type _defaults = { 'sequence': lambda *args: 1, 'probability': lambda *args: 0.0, - 'object_id' : _find_object_id + 'type': _find_stage_type, } crm_case_stage() @@ -531,7 +532,7 @@ class crm_case_section(osv.osv): 'reply_to': fields.char('Reply-To', size=64, help="The email address put in the 'Reply-To' of all emails sent by OpenERP about cases in this sales team"), 'parent_id': fields.many2one('crm.case.section', 'Parent Team'), 'child_ids': fields.one2many('crm.case.section', 'parent_id', 'Child Teams'), - 'resource_calendar_id': fields.many2one('resource.calendar', "Resource's Calendar"), + 'resource_calendar_id': fields.many2one('resource.calendar', "Working Time"), 'note': fields.text('Description'), 'working_hours': fields.float('Working Hours', digits=(16,2 )), 'stage_ids': fields.many2many('crm.case.stage', 'section_stage_rel', 'section_id', 'stage_id', 'Stages'), @@ -629,6 +630,7 @@ class crm_case_stage(osv.osv): _columns = { 'section_ids':fields.many2many('crm.case.section', 'section_stage_rel', 'stage_id', 'section_id', 'Sections'), } + crm_case_stage() diff --git a/addons/crm/crm_lead.py b/addons/crm/crm_lead.py index e0bed8934f2..8cc45c75e57 100644 --- a/addons/crm/crm_lead.py +++ b/addons/crm/crm_lead.py @@ -135,7 +135,7 @@ class crm_lead(crm_case, osv.osv): ],'Type', help="Type is used to separate Leads and Opportunities"), 'priority': fields.selection(crm.AVAILABLE_PRIORITIES, 'Priority'), 'date_closed': fields.datetime('Closed', readonly=True), - 'stage_id': fields.many2one('crm.case.stage', 'Stage'), + 'stage_id': fields.many2one('crm.case.stage', 'Stage', domain="[('type','=','lead')]"), 'user_id': fields.many2one('res.users', 'Salesman',help='By Default Salesman is Administrator when create New User'), 'referred': fields.char('Referred By', size=64), 'date_open': fields.datetime('Opened', readonly=True), @@ -150,6 +150,19 @@ class crm_lead(crm_case, osv.osv): \nIf the case needs to be reviewed then the state is set to \'Pending\'.'), 'message_ids': fields.one2many('mailgate.message', 'res_id', 'Messages', domain=[('model','=',_name)]), } + + def _get_stage_id(self, cr, uid, context=None): + """Finds type of stage according to object. + @param self: The object pointer + @param cr: the current row, from the database cursor, + @param uid: the current user’s ID for security checks, + @param context: A standard dictionary for contextual values + """ + if context is None: + context = {} + type = context and context.get('stage_type', '') + stage_ids = self.pool.get('crm.case.stage').search(cr, uid, [('type','=',type),('sequence','>=',1)]) + return stage_ids and stage_ids[0] or False _defaults = { 'active': lambda *a: 1, @@ -160,6 +173,7 @@ class crm_lead(crm_case, osv.osv): 'section_id': crm_case._get_section, 'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(cr, uid, 'crm.lead', context=c), 'priority': lambda *a: crm.AVAILABLE_PRIORITIES[2][0], + 'stage_id': _get_stage_id, } def onchange_partner_address_id(self, cr, uid, ids, add, email=False): @@ -297,7 +311,7 @@ class crm_lead(crm_case, osv.osv): return super(crm_lead,self).write(cr, uid, ids, vals, context) def stage_next(self, cr, uid, ids, context=None): - stage = super(crm_lead, self).stage_next(cr, uid, ids, context) + stage = super(crm_lead, self).stage_next(cr, uid, ids, context=context) if stage: stage_obj = self.pool.get('crm.case.stage').browse(cr, uid, stage, context=context) if stage_obj.on_change: diff --git a/addons/crm/crm_lead_data.xml b/addons/crm/crm_lead_data.xml index 2a49e43e683..3cb34c49299 100644 --- a/addons/crm/crm_lead_data.xml +++ b/addons/crm/crm_lead_data.xml @@ -2,34 +2,39 @@ - + + Lost + + + lead + New - + lead Qualification - + lead Proposition - + lead Negotiation - + lead @@ -37,15 +42,10 @@ - + lead - - Lost - - - - + Sales Department diff --git a/addons/crm/crm_lead_menu.xml b/addons/crm/crm_lead_menu.xml index 6c74dccac15..2fff1b57d3c 100644 --- a/addons/crm/crm_lead_menu.xml +++ b/addons/crm/crm_lead_menu.xml @@ -9,7 +9,7 @@ ['|', ('type','=','lead'), ('type','=',False)] - {'search_default_current':1, 'default_type': 'lead', 'search_default_section_id': section_id} + {'search_default_current':1, 'default_type': 'lead', 'search_default_section_id': section_id, 'stage_type': 'lead'} 'Leads' allows you to manage and keep track of all first potential interests of a partner in one of your products or services. A lead is a first, unqualified, contact with a prospect or customer. After being qualified, a lead can be converted into a business opportunity with the creation of the related partner for further detailed tracking of any linked activities. You can use leads when you import a database of prospects or to integrate your website's contact form with OpenERP. diff --git a/addons/crm/crm_lead_view.xml b/addons/crm/crm_lead_view.xml index cda3c4fcc1f..979ab26cfd1 100644 --- a/addons/crm/crm_lead_view.xml +++ b/addons/crm/crm_lead_view.xml @@ -9,8 +9,8 @@ crm.case.stage form - [('object_id.model', '=', 'crm.lead')] - {'object_id':'crm.lead'} + [('type', '=', 'lead')] + {'type':'lead'} Create specific stages that will help your sales better organise their sales pipeline by maintaining them to their leads and sales opportunities. It will allow them to easily track how is positioned a specific lead or opportunity in the sales cycle. @@ -57,14 +57,14 @@ - +